| name | lang-cpp-library-dev |
| description | C++-specific library development patterns. Use when creating C++ libraries, designing header-only vs compiled libraries, configuring CMake for library targets, managing ABI stability and versioning, integrating with Conan/vcpkg, documenting with Doxygen, or testing with GoogleTest/Catch2. Extends meta-library-dev with C++ tooling and idioms. |
C++ Library Development
C++-specific patterns for library development. This skill extends meta-library-dev with C++ tooling, modern C++ idioms (C++17/20/23), and ecosystem practices.
This Skill Extends
meta-library-dev- Foundational library patterns (API design, versioning, testing strategies)lang-cpp-dev- Core C++ language features and patterns
For general concepts like semantic versioning, module organization principles, and testing pyramids, see the meta-skill first.
This Skill Adds
- Library architecture: Header-only vs compiled libraries, template libraries
- CMake tooling: Library targets, installation, package configuration
- ABI stability: Version management, symbol visibility, compatibility
- Package managers: Conan, vcpkg integration
- Documentation: Doxygen patterns for libraries
- Testing: GoogleTest, Catch2 patterns for library testing
This Skill Does NOT Cover
- General library patterns - see
meta-library-dev - Core C++ syntax and features - see
lang-cpp-dev - CMake basics - see
lang-cpp-cmake-dev - Memory optimization - see
lang-cpp-memory-eng - Performance profiling - see
lang-cpp-profiling-eng
Quick Reference
| Task | Command/Pattern |
|---|---|
| CMake library target | add_library(mylib src/mylib.cpp) |
| Header-only library | add_library(mylib INTERFACE) |
| Build library | cmake --build build |
| Install library | cmake --install build |
| Run tests | ctest --test-dir build |
| Generate docs | doxygen Doxyfile |
| Conan install | conan install . --build=missing |
| vcpkg install | vcpkg install mylib |
Library Architecture
Header-Only vs Compiled Libraries
Header-Only Libraries:
// my_library.hpp
#ifndef MY_LIBRARY_HPP
#define MY_LIBRARY_HPP
#include <string>
#include <vector>
namespace mylib {
// All code in headers - no .cpp files
template<typename T>
class Container {
public:
void add(const T& item) {
items_.push_back(item);
}
size_t size() const {
return items_.size();
}
private:
std::vector<T> items_;
};
// Non-template functions must be inline
inline std::string version() {
return "1.0.0";
}
} // namespace mylib
#endif
Compiled Libraries:
// include/mylib/mylib.hpp
#ifndef MYLIB_HPP
#define MYLIB_HPP
#include <string>
#include <memory>
namespace mylib {
class Parser {
public:
Parser();
~Parser();
// Public interface only
bool parse(const std::string& input);
std::string result() const;
private:
struct Impl; // PIMPL pattern
std::unique_ptr<Impl> impl_;
};
} // namespace mylib
#endif
// src/mylib.cpp
#include "mylib/mylib.hpp"
namespace mylib {
struct Parser::Impl {
std::string result;
// Implementation details hidden
};
Parser::Parser() : impl_(std::make_unique<Impl>()) {}
Parser::~Parser() = default;
bool Parser::parse(const std::string& input) {
// Implementation
impl_->result = "parsed: " + input;
return true;
}
std::string Parser::result() const {
return impl_->result;
}
} // namespace mylib
When to Use Each
| Pattern | Use When | Pros | Cons |
|---|---|---|---|
| Header-only | Templates, small utilities | No linking, easy distribution | Longer compile times, code bloat |
| Compiled | Large implementations, ABI stability | Fast compilation, smaller binaries | Requires linking, distribution complexity |
| Hybrid | Mix of both | Best of both worlds | More complex build setup |
CMake Library Configuration
Basic Library Target
cmake_minimum_required(VERSION 3.15)
project(MyLibrary VERSION 1.0.0 LANGUAGES CXX)
# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# Compiled library
add_library(mylib
src/parser.cpp
src/serializer.cpp
)
# Include directories
target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Link dependencies
target_link_libraries(mylib
PUBLIC
fmt::fmt
PRIVATE
nlohmann_json::nlohmann_json
)
# Set library properties
set_target_properties(mylib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
PUBLIC_HEADER include/mylib/mylib.hpp
)
Header-Only Library
# Interface library (header-only)
add_library(mylib INTERFACE)
target_include_directories(mylib
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_features(mylib INTERFACE cxx_std_17)
# Header-only dependencies
target_link_libraries(mylib
INTERFACE
range-v3::range-v3
)
Static vs Shared Library
# Option for library type
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
add_library(mylib
src/parser.cpp
src/serializer.cpp
)
# Or explicitly
add_library(mylib_static STATIC src/parser.cpp)
add_library(mylib_shared SHARED src/parser.cpp)
# Symbol visibility for shared libraries
include(GenerateExportHeader)
generate_export_header(mylib
EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/include/mylib/export.hpp
)
target_compile_definitions(mylib
PRIVATE
MYLIB_EXPORTS # When building the library
)
Installation Configuration
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
# Install targets
install(TARGETS mylib
EXPORT mylibTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
)
# Install headers
install(DIRECTORY include/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
# Export targets
install(EXPORT mylibTargets
FILE mylibTargets.cmake
NAMESPACE mylib::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mylib
)
# Generate package config
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/mylibConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/mylibConfig.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mylib
)
# Generate version file
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/mylibConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)
# Install package config files
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/mylibConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/mylibConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mylib
)
Package Config Template
# cmake/mylibConfig.cmake.in
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
# Find dependencies
find_dependency(fmt)
# Include targets
include("${CMAKE_CURRENT_LIST_DIR}/mylibTargets.cmake")
check_required_components(mylib)
Symbol Visibility and ABI Stability
Export Macros
// include/mylib/export.hpp
#ifndef MYLIB_EXPORT_HPP
#define MYLIB_EXPORT_HPP
#ifdef _WIN32
#ifdef MYLIB_EXPORTS
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
#else
#define MYLIB_API __attribute__((visibility("default")))
#endif
#endif
// Usage in headers
#include "mylib/export.hpp"
namespace mylib {
class MYLIB_API Parser {
public:
Parser();
~Parser();
bool parse(const std::string& input);
};
} // namespace mylib
CMake Visibility Configuration
# Set default symbol visibility to hidden
set_target_properties(mylib PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
# Generate export header
include(GenerateExportHeader)
generate_export_header(mylib
BASE_NAME MYLIB
EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/include/mylib/export.hpp
)
ABI Versioning
// Use inline namespaces for ABI versioning
namespace mylib {
inline namespace v1 {
class Parser {
// v1 implementation
};
} // namespace v1
// When breaking ABI in next major version:
// inline namespace v2 {
// class Parser { /* new implementation */ };
// }
} // namespace mylib
// Users automatically get latest inline namespace
mylib::Parser p; // Uses mylib::v1::Parser
PIMPL Pattern for ABI Stability
// Public header - ABI stable
// include/mylib/database.hpp
#ifndef MYLIB_DATABASE_HPP
#define MYLIB_DATABASE_HPP
#include <memory>
#include <string>
namespace mylib {
class Database {
public:
Database(const std::string& path);
~Database();
// Copyable/movable
Database(const Database&);
Database& operator=(const Database&);
Database(Database&&) noexcept;
Database& operator=(Database&&) noexcept;
void execute(const std::string& query);
private:
struct Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace mylib
#endif
// Implementation - can change without breaking ABI
// src/database.cpp
#include "mylib/database.hpp"
#include <vector>
#include <map>
namespace mylib {
struct Database::Impl {
std::string path;
std::vector<std::string> cache;
std::map<std::string, int> indices;
// Private implementation details
};
Database::Database(const std::string& path)
: impl_(std::make_unique<Impl>()) {
impl_->path = path;
}
Database::~Database() = default;
Database::Database(const Database& other)
: impl_(std::make_unique<Impl>(*other.impl_)) {}
Database& Database::operator=(const Database& other) {
if (this != &other) {
impl_ = std::make_unique<Impl>(*other.impl_);
}
return *this;
}
Database::Database(Database&&) noexcept = default;
Database& Database::operator=(Database&&) noexcept = default;
void Database::execute(const std::string& query) {
// Implementation
}
} // namespace mylib
Template Library Patterns
Header Organization
include/
└── mylib/
├── mylib.hpp # Main public header
├── container.hpp # Template class
└── detail/
└── impl.hpp # Implementation details
// include/mylib/container.hpp
#ifndef MYLIB_CONTAINER_HPP
#define MYLIB_CONTAINER_HPP
#include <vector>
#include <algorithm>
namespace mylib {
template<typename T>
class Container {
public:
void add(const T& item);
void add(T&& item);
template<typename Predicate>
void remove_if(Predicate pred);
size_t size() const { return items_.size(); }
private:
std::vector<T> items_;
};
// Template implementations in same header
template<typename T>
void Container<T>::add(const T& item) {
items_.push_back(item);
}
template<typename T>
void Container<T>::add(T&& item) {
items_.push_back(std::move(item));
}
template<typename T>
template<typename Predicate>
void Container<T>::remove_if(Predicate pred) {
items_.erase(
std::remove_if(items_.begin(), items_.end(), pred),
items_.end()
);
}
} // namespace mylib
#endif
Extern Templates (Reduce Compile Time)
// mylib.hpp - Declaration
template<typename T>
class Container {
void add(const T& item);
};
// mylib_extern.hpp - Extern template declarations
extern template class Container<int>;
extern template class Container<std::string>;
// mylib.cpp - Explicit instantiation
#include "mylib.hpp"
template class Container<int>;
template class Container<std::string>;
Concepts for Template Constraints (C++20)
#include <concepts>
namespace mylib {
// Define concepts
template<typename T>
concept Serializable = requires(T t) {
{ t.serialize() } -> std::convertible_to<std::string>;
};
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;
// Use in templates
template<Serializable T>
class DataStore {
public:
void save(const T& data) {
std::string serialized = data.serialize();
// Save serialized data
}
};
// Multiple constraints
template<typename T>
concept Container = requires(T t) {
typename T::value_type;
{ t.size() } -> std::convertible_to<size_t>;
{ t.begin() } -> std::input_iterator;
{ t.end() } -> std::input_iterator;
};
template<Container C>
void process(const C& container) {
for (const auto& item : container) {
// Process item
}
}
} // namespace mylib
Package Manager Integration
Conan Configuration
# conanfile.py
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
class MyLibConan(ConanFile):
name = "mylib"
version = "1.0.0"
license = "MIT"
author = "Your Name <your.email@example.com>"
url = "https://github.com/username/mylib"
description = "A C++ library for parsing"
topics = ("parsing", "cpp", "library")
settings = "os", "compiler", "build_type", "arch"
options = {
"shared": [True, False],
"fPIC": [True, False]
}
default_options = {
"shared": False,
"fPIC": True
}
exports_sources = "CMakeLists.txt", "src/*", "include/*"
def requirements(self):
self.requires("fmt/9.1.0")
self.requires("nlohmann_json/3.11.2")
def config_options(self):
if self.settings.os == "Windows":
del self.options.fPIC
def layout(self):
cmake_layout(self)
def generate(self):
tc = CMakeToolchain(self)
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def package_info(self):
self.cpp_info.libs = ["mylib"]
self.cpp_info.includedirs = ["include"]
vcpkg Configuration
{
"name": "mylib",
"version": "1.0.0",
"description": "A C++ library for parsing",
"homepage": "https://github.com/username/mylib",
"license": "MIT",
"dependencies": [
"fmt",
"nlohmann-json"
],
"features": {
"tests": {
"description": "Build tests",
"dependencies": [
"gtest"
]
}
}
}
# ports/mylib/portfile.cmake
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO username/mylib
REF v1.0.0
SHA512 <hash>
HEAD_REF main
)
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS
-DBUILD_TESTING=OFF
)
vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/mylib)
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
Documentation with Doxygen
Doxyfile Configuration
# Doxyfile
PROJECT_NAME = "MyLib"
PROJECT_BRIEF = "A modern C++ parsing library"
PROJECT_VERSION = 1.0.0
INPUT = include/ src/ README.md
FILE_PATTERNS = *.hpp *.cpp *.md
RECURSIVE = YES
EXCLUDE_PATTERNS = */detail/* */test/*
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_STATIC = YES
GENERATE_HTML = YES
HTML_OUTPUT = docs/html
GENERATE_LATEX = NO
USE_MDFILE_AS_MAINPAGE = README.md
MARKDOWN_SUPPORT = YES
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = NO
HAVE_DOT = YES
CALL_GRAPH = YES
CALLER_GRAPH = YES
CLASS_DIAGRAMS = YES
Documentation Comments
/**
* @file parser.hpp
* @brief Main parser interface
* @author Your Name
* @version 1.0.0
*/
#ifndef MYLIB_PARSER_HPP
#define MYLIB_PARSER_HPP
namespace mylib {
/**
* @brief A parser for processing input data
*
* This class provides methods for parsing various input formats
* and converting them to structured data.
*
* @code
* Parser p;
* p.parse("input data");
* auto result = p.result();
* @endcode
*
* @note The parser is not thread-safe
* @warning Reusing the same parser instance may lead to data races
*/
class Parser {
public:
/**
* @brief Construct a new Parser object
*
* @param strict Enable strict parsing mode
* @throw std::invalid_argument if configuration is invalid
*/
explicit Parser(bool strict = false);
/**
* @brief Parse input data
*
* @param input The input string to parse
* @return true if parsing succeeded
* @return false if parsing failed
*
* @pre input must not be empty
* @post result() contains parsed data if successful
*
* @see result()
*/
bool parse(const std::string& input);
/**
* @brief Get the parsing result
*
* @return const std::string& Reference to the result
*
* @pre parse() must have been called successfully
*/
const std::string& result() const;
private:
bool strict_; ///< Strict parsing mode flag
std::string result_; ///< Parsed result storage
};
/**
* @brief Parse input with default settings
*
* @tparam T The output type
* @param input Input string
* @return T Parsed result
*
* @example
* @code
* auto data = parse<Data>("input");
* @endcode
*/
template<typename T>
T parse(const std::string& input);
} // namespace mylib
#endif
CMake Integration
# Find Doxygen
find_package(Doxygen)
if(DOXYGEN_FOUND)
# Configure Doxyfile
set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in)
set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
# Add documentation target
add_custom_target(docs
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM
)
endif()
Testing with GoogleTest
CMake Test Configuration
# Enable testing
enable_testing()
# Find GoogleTest
find_package(GTest REQUIRED)
# Test executable
add_executable(mylib_test
tests/parser_test.cpp
tests/serializer_test.cpp
)
target_link_libraries(mylib_test
PRIVATE
mylib
GTest::gtest
GTest::gtest_main
)
# Discover tests
include(GoogleTest)
gtest_discover_tests(mylib_test)
Test Structure
// tests/parser_test.cpp
#include <gtest/gtest.h>
#include "mylib/parser.hpp"
namespace mylib {
namespace test {
class ParserTest : public ::testing::Test {
protected:
void SetUp() override {
parser = std::make_unique<Parser>();
}
void TearDown() override {
parser.reset();
}
std::unique_ptr<Parser> parser;
};
TEST_F(ParserTest, ParseValidInput) {
ASSERT_TRUE(parser->parse("valid input"));
EXPECT_EQ(parser->result(), "parsed: valid input");
}
TEST_F(ParserTest, ParseEmptyInput) {
EXPECT_FALSE(parser->parse(""));
}
TEST_F(ParserTest, ParseWithStrictMode) {
Parser strict_parser(true);
EXPECT_THROW(strict_parser.parse("invalid"), std::runtime_error);
}
// Parameterized tests
class ParserParamTest : public ::testing::TestWithParam<std::string> {};
TEST_P(ParserParamTest, ParseMultipleInputs) {
Parser p;
EXPECT_TRUE(p.parse(GetParam()));
}
INSTANTIATE_TEST_SUITE_P(
ValidInputs,
ParserParamTest,
::testing::Values("input1", "input2", "input3")
);
} // namespace test
} // namespace mylib
Testing with Catch2
CMake Configuration
find_package(Catch2 3 REQUIRED)
add_executable(mylib_test
tests/parser_test.cpp
)
target_link_libraries(mylib_test
PRIVATE
mylib
Catch2::Catch2WithMain
)
include(Catch)
catch_discover_tests(mylib_test)
Test Structure
// tests/parser_test.cpp
#include <catch2/catch_test_macros.hpp>
#include "mylib/parser.hpp"
TEST_CASE("Parser handles valid input", "[parser]") {
mylib::Parser p;
SECTION("Basic parsing") {
REQUIRE(p.parse("test input"));
REQUIRE(p.result() == "parsed: test input");
}
SECTION("Empty input fails") {
REQUIRE_FALSE(p.parse(""));
}
}
TEST_CASE("Parser with strict mode", "[parser][strict]") {
mylib::Parser p(true);
REQUIRE_THROWS_AS(p.parse("invalid"), std::runtime_error);
}
// Template tests
TEMPLATE_TEST_CASE("Container works with different types",
"[container][template]",
int, double, std::string) {
mylib::Container<TestType> container;
TestType value{};
container.add(value);
REQUIRE(container.size() == 1);
}
Versioning Best Practices
Semantic Versioning
MAJOR.MINOR.PATCH (e.g., 1.2.3)
MAJOR: Incompatible API changes (breaking changes)
MINOR: Backward-compatible functionality additions
PATCH: Backward-compatible bug fixes
Version Header
// include/mylib/version.hpp
#ifndef MYLIB_VERSION_HPP
#define MYLIB_VERSION_HPP
#define MYLIB_VERSION_MAJOR 1
#define MYLIB_VERSION_MINOR 0
#define MYLIB_VERSION_PATCH 0
#define MYLIB_VERSION \
(MYLIB_VERSION_MAJOR * 10000 + \
MYLIB_VERSION_MINOR * 100 + \
MYLIB_VERSION_PATCH)
#define MYLIB_VERSION_STRING "1.0.0"
namespace mylib {
struct Version {
static constexpr int major = MYLIB_VERSION_MAJOR;
static constexpr int minor = MYLIB_VERSION_MINOR;
static constexpr int patch = MYLIB_VERSION_PATCH;
static constexpr const char* string = MYLIB_VERSION_STRING;
};
} // namespace mylib
#endif
CMake Version Configuration
# CMakeLists.txt
project(MyLib VERSION 1.0.0)
# Generate version header
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/mylib/version.hpp.in
${CMAKE_CURRENT_BINARY_DIR}/include/mylib/version.hpp
)
// include/mylib/version.hpp.in
#ifndef MYLIB_VERSION_HPP
#define MYLIB_VERSION_HPP
#define MYLIB_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define MYLIB_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define MYLIB_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define MYLIB_VERSION_STRING "@PROJECT_VERSION@"
#endif
Example: Complete Library Structure
mylib/
├── CMakeLists.txt
├── conanfile.py
├── vcpkg.json
├── README.md
├── LICENSE
├── CHANGELOG.md
├── Doxyfile
├── .clang-format
├── include/
│ └── mylib/
│ ├── mylib.hpp # Main public header
│ ├── parser.hpp
│ ├── serializer.hpp
│ ├── version.hpp
│ └── export.hpp
├── src/
│ ├── parser.cpp
│ └── serializer.cpp
├── tests/
│ ├── CMakeLists.txt
│ ├── parser_test.cpp
│ └── serializer_test.cpp
├── examples/
│ ├── CMakeLists.txt
│ ├── basic_usage.cpp
│ └── advanced_usage.cpp
├── docs/
│ └── Doxyfile.in
└── cmake/
├── mylibConfig.cmake.in
└── FindMyLibDeps.cmake
Anti-Patterns
1. Exposing Implementation Details
// Bad: Exposing internal types
class Parser {
public:
std::shared_ptr<InternalNode> parse(const std::string& input);
};
// Good: Return opaque type or public interface
class Parser {
public:
Document parse(const std::string& input);
};
2. Using using namespace in Headers
// Bad: Pollutes namespace for users
// mylib.hpp
using namespace std;
class Parser {
string parse(const string& input);
};
// Good: Fully qualified names
// mylib.hpp
class Parser {
std::string parse(const std::string& input);
};
3. Inline Everything
// Bad: Large inline functions
class Parser {
void parse(const std::string& input) {
// 100 lines of code...
}
};
// Good: Separate declaration and definition
class Parser {
void parse(const std::string& input);
};
4. Breaking ABI Without Major Version Bump
// v1.0.0
class Data {
int value;
};
// v1.1.0 - WRONG! This breaks ABI
class Data {
int value;
int newValue; // Changes size/layout
};
// Correct: Create new type or bump major version
References
meta-library-dev- Foundational library patternslang-cpp-dev- Core C++ featureslang-cpp-cmake-dev- CMake configuration- CMake Documentation
- Conan Documentation
- vcpkg Documentation
- GoogleTest Documentation
- Catch2 Documentation
- Doxygen Documentation
- C++ Core Guidelines