C++ Library Coding Guidelines¶
Applies on top of the general C++ coding guidelines and the library design guidelines.
Header Layout¶
Public Headers — include/<library-name>/¶
Public headers form the installed API surface of the library. Only declarations that consumers need belong here. Everything else is an implementation detail.
- Use
#pragma once(not include guards). - Minimise includes: prefer forward declarations over full includes where possible.
- Never include implementation-only headers (e.g.
<format>, internal helpers) in public headers. - Keep the directory name identical to the CMake target name and the Conan package name.
Consumers include public headers as:
Private Headers — src/¶
Implementation details, helpers, and internal types belong in src/. They are never
installed and never appear in the public API.
CMake Target Design¶
Expose the public header directory through the BUILD_INTERFACE / INSTALL_INTERFACE generator
expressions so that the same target works both during the build and after installation:
target_include_directories(<target>
PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
Export the target so consumers can use find_package:
install(TARGETS <target>
EXPORT <target>Targets
ARCHIVE DESTINATION lib
)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/
DESTINATION include
)
install(EXPORT <target>Targets
FILE <target>Targets.cmake
NAMESPACE <target>::
DESTINATION lib/cmake/<target>
)
Conan Recipe¶
Use a full recipe (not a consumer recipe) at the project root. A full recipe declares
name, version, exports_sources, build(), package(), and package_info().
Verify the package is consumable via a test package in packaging/test_package/:
conan create . --test-folder=packaging/test_package
The test package must build and run a minimal consumer — it is the integration test for the Conan recipe.
ABI and API Stability¶
- Mark symbols with visibility attributes (
[[gnu::visibility("default")]]) when building a shared library. - Avoid exposing STL containers in the public API across shared-library boundaries.
- Increment
SOVERSIONin CMake when breaking ABI; increment the Conanversionwhen breaking API.
Acceptance Tests¶
A thin, test-only CLI wrapper ("CLI-shim") around the public API lives under
test/bdd/cli_shim/; it is built only when BUILD_TESTING is on and never installed or packaged.
pytest-bdd drives it through a subprocess, exactly like a CLI tool's acceptance tests. This gives
a library without a running process of its own an executable acceptance-test tier.
What to Avoid¶
- Putting implementation
.cppfiles underinclude/. - Putting private headers under
include/. - Linking private dependencies as
PUBLICintarget_link_libraries— this leaks them to consumers. - Using
target_include_directorieswithSYSTEMon your own headers — it suppresses warnings that consumers need to see.
Directives¶
- Public headers in
include/<library-name>/; private headers and implementations insrc/ - Use
BUILD_INTERFACE/INSTALL_INTERFACEgenerator expressions intarget_include_directories - Full Conan recipe at project root; include a test package in
packaging/test_package/ - Link private dependencies as
PRIVATEintarget_link_libraries— neverPUBLICfor private deps - Increment
SOVERSIONon ABI break; increment Conan version on API break - A test-only CLI-shim under
test/bdd/cli_shim/exposes the public API to pytest-bdd; never install or package it