From acdd422beab4010202eb081b46b49794f8a0cf3e Mon Sep 17 00:00:00 2001 From: Yurii Cherkasov Date: Mon, 19 Oct 2026 00:53:51 +0700 Subject: [PATCH 1/4] docs: expand linking chapter to C++26 and normalize sections --- 01_low_level/10_linking/README.md | 103 +++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 17 deletions(-) diff --git a/01_low_level/10_linking/README.md b/01_low_level/10_linking/README.md index 2e4806f..ab13582 100644 --- a/01_low_level/10_linking/README.md +++ b/01_low_level/10_linking/README.md @@ -9,7 +9,7 @@ Most subtle C++ bugs at scale are **linking bugs**, not syntax or type-system bu --- -## Translation Units +## 1. Translation Units A **translation unit (TU)** is the result of preprocessing a source file: @@ -23,7 +23,7 @@ Each TU is compiled **independently**, then linked together. --- -## Linkage Types +## 2. Linkage Types ### 1. Internal linkage @@ -86,7 +86,7 @@ They are **purely compile-time or local constructs**. --- -## Scope vs Linkage (do not confuse) +## 3. Scope vs Linkage (do not confuse) | Concept | Meaning | | ----------- | --------------------------------------------------- | @@ -109,7 +109,7 @@ C++ adds: --- -## The One Definition Rule (ODR) +## 4. The One Definition Rule (ODR) The **ODR** governs what may be defined where: @@ -129,7 +129,7 @@ Violations lead to: --- -## `static` keyword: historical overload +## 5. `static` keyword: historical overload `static` has **two unrelated meanings**: @@ -147,7 +147,7 @@ static int x; // internal linkage (global) --- -## `const` and linkage +## 6. `const` and linkage In **C++ (unlike C)**: @@ -173,7 +173,7 @@ inline constexpr int ci = 42; --- -## Headers and Inclusion Rules +## 7. Headers and Inclusion Rules Best practices: @@ -185,7 +185,7 @@ Best practices: --- -## Global Initialization Order +## 8. Global Initialization Order There is **no guaranteed order** of initialization of global objects across TUs. @@ -216,7 +216,7 @@ constinit int g = 42; --- -## `extern "C"` and ABI Boundaries +## 9. `extern "C"` and ABI Boundaries ```cpp extern "C" void c_function(); @@ -254,7 +254,7 @@ extern "C" { --- -## Name Mangling +## 10. Name Mangling C++ encodes: @@ -278,7 +278,7 @@ Use tools to inspect: --- -## Inline Functions and Variables +## 11. Inline Functions and Variables ### Inline functions @@ -297,7 +297,7 @@ inline int counter = 0; --- -## Templates and Linking +## 12. Templates and Linking Templates are **instantiated at use sites**. @@ -315,7 +315,7 @@ template int foo(int); --- -## Symbol Visibility (Shared Libraries) +## 13. Symbol Visibility (Shared Libraries) On ELF / Mach-O systems (GCC/Clang): @@ -336,7 +336,7 @@ Benefits: --- -## Windows DLL Import / Export +## 14. Windows DLL Import / Export ```cpp __declspec(dllexport) @@ -360,7 +360,7 @@ Different from ELF: --- -## Dynamic Linking at Runtime +## 15. Dynamic Linking at Runtime POSIX example: @@ -376,7 +376,7 @@ Requires: --- -## Facts about Linking +## 16. Facts about Linking ``` extern "C" { @@ -401,6 +401,75 @@ extern "C" {...} --- +## 17. Modern linking and ABI engineering: details beyond the linker command + +### C++17 inline variables preserve one entity across translation units + +C++98's common header pattern for global mutable variables required an `extern` declaration in a header and exactly one definition elsewhere. C++17 introduces inline variables, allowing an identical definition to appear in multiple translation units while designating one program-level entity, subject to the one-definition rule. An inline variable should be declared inline on every relevant declaration that defines it; it is not a substitute for managing a shared library's symbol visibility and ABI. + +```cpp +// configuration.hpp (C++17) +inline std::atomic active_sessions{0}; +// Unlike a namespace-scope const in a header, this is one shared entity +// within a correctly linked program. +``` + +Keep dynamic initialization costs and initialization dependencies visible even for inline variables. An ODR violation may be ill-formed with no diagnostic required, rather than an ordinary linker error. + +### C++20 modules introduce a third linkage category: module linkage + +In named-module contexts, some namespace-scope entities have *module linkage*: they can be referenced from translation units attached to the same named module, but are not exposed as external-linkage declarations to unrelated translation units. This is not equivalent to an anonymous namespace's internal linkage or to a namespace nested under a module name. + +```cpp +// module-unit.cpp (C++20 modules) +module; +#include +export module telemetry; +int detail_counter = 0; // module linkage in the appropriate context +export int read_counter() { return detail_counter; } +``` + +This requires an actual module build graph and compiled module interface artifacts; simply renaming a header to a module does not solve dependency or ODR problems. The global module fragment and textual headers preserve relevant traditional linkage rules. + +### Dynamic initialization ordering has stronger and weaker guarantees + +Within a translation unit, ordered dynamic initialization follows definition order for the applicable variables. Across translation units, initialization order is much more constrained by language rules and may remain indeterminate for many dependencies. The *construct-on-first-use* idiom relies on function-local statics and C++11's thread-safe initialization guarantee. + +```cpp +Registry& registry() { + static Registry instance; + return instance; +} +``` + +Initialization is safe under the specified concurrency rules; destruction-order dependencies can still be problematic at process termination. C++20 `constinit` forces static initialization or rejects the declaration, but does not make an object immutable and does not impose arbitrary cross-module teardown ordering. + +### LTO can expose ODR and ABI mistakes that separate compilation concealed + +Link-time optimization allows the optimizer to see definitions across translation units and reason more aggressively about inline functions, class layouts, and aliasing. Code that accidentally violates the one-definition rule might appear stable when compiled independently and break under LTO. + +```text +TU A: header sees PACKED=1, defines inline access() +TU B: header sees PACKED=0, defines inline access() +Result: potentially different definitions of the same inline entity +``` + +The problem is not LTO itself: compiling the same entity inconsistently already breaks the C++ program contract. Enable reproducible macro settings and ABI-oriented integration tests, and consider compiler ODR diagnostics where available. + +### Weak symbols, COMDAT, and visibility are ABI mechanisms, not ISO C++ contracts + +ELF symbol binding, PE/COFF COMDAT folding, Mach-O symbol visibility, and linker-specific dead-stripping are implementation technologies. They help implement inline definitions, templates, and dynamic libraries, but their exact behavior is not a source-language guarantee. + +For ELF use `readelf -Ws`, `nm -C`, `objdump -T` and linker map files; for Windows use `dumpbin /symbols` and `dumpbin /exports`. Record symbol exports as part of ABI tests rather than expecting visibility to follow from namespaces or C++ linkage alone. + +### C++23 and C++26 preserve explicit module and ABI design obligations + +C++23's language and library additions do not replace the one-definition rule, and C++26 reflection or package-oriented tooling does not promise ABI compatibility between independently built C++ libraries. Template instantiations, exception models, RTTI, standard library configurations, and calling conventions still require deliberate compatibility decisions. + +Prefer narrow C interfaces for long-lived cross-toolchain plugin boundaries, and document compiler, target, standard library, and exported symbol versions for C++-to-C++ binary interfaces. + +--- + ## Rules worth keeping in working memory The controlling distinction for linkage and translation units is between the portable language contract and the behavior of a particular compilation environment. C++ gives programmers the ability to build close to system boundaries, but also requires those boundaries to be specified rather than guessed. @@ -538,7 +607,7 @@ Modules provide explicit compiled interfaces but retain ABI/linkage concerns. Module integration improves without eliminating platform linkers. -### C++26: Forthcoming support +### C++26: Evolving tools without fundamental linkage or lookup changes Expect evolving module-toolchain interoperability; check build-system support. From a91cdba2cd027a20c98daff89d28a778148f7304 Mon Sep 17 00:00:00 2001 From: Yurii Cherkasov Date: Mon, 19 Oct 2026 00:58:11 +0700 Subject: [PATCH 2/4] test: add reusable Python chapter structure checker --- scripts/check_chapter_structure.py | 75 ++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 scripts/check_chapter_structure.py diff --git a/scripts/check_chapter_structure.py b/scripts/check_chapter_structure.py new file mode 100644 index 0000000..bb89f76 --- /dev/null +++ b/scripts/check_chapter_structure.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Validate Book 01 technical-literature chapter contracts (stdlib only).""" +from __future__ import annotations + +import argparse +from pathlib import Path +import re +import sys + +ENDING = ( + "Rules worth keeping in working memory", + "Diagnostics, useful compiler settings and extensions", + "Standards timeline", + "Migration note for Java / Python / C# developers", + "Further reading", +) +YEARS = ("C++98/03", "C++11", "C++14", "C++17", "C++20", "C++23", "C++26") +STUBS = re.compile(r"\b(?:TODO|TBD|FIXME|PLACEHOLDER|fill this in)\b|", re.I) +HEADING = re.compile(r"^(#{1,6})\s+(.+?)\s*$", re.M) +FENCE = re.compile(r"^\s*(`{3,}|~{3,})", re.M) +CHAPTER = re.compile(r"^(\d{2})_") + +def validate(path: Path) -> list[str]: + body = path.read_text(encoding="utf-8") + errors: list[str] = [] + chapter = CHAPTER.match(path.parent.name) + if not chapter: + return [f"{path}: not a numbered chapter directory"] + headings = [(len(m.group(1)), m.group(2)) for m in HEADING.finditer(body)] + if not headings or headings[0][0] != 1 or not headings[0][1].startswith(chapter.group(1)): + errors.append("H1 must start with chapter number") + h2 = [h for level, h in headings if level == 2] + if h2[-len(ENDING):] != list(ENDING): + errors.append("final five H2 sections must match the chapter template in order") + for h in ENDING: + if h2.count(h) != 1: + errors.append(f"expected exactly one H2: {h}") + numeric = [int(m.group(1)) for h in h2 if (m := re.match(r"^(\d+)\.\s", h))] + if not numeric or numeric != list(range(1, len(numeric) + 1)): + errors.append(f"numbered H2 sections not consecutive: {numeric}") + found_stubs = STUBS.findall(body) + if found_stubs: + errors.append(f"contains stub markers: {found_stubs[:3]}") + fences = FENCE.findall(body) + if len(fences) % 2: + errors.append("unbalanced fenced code blocks") + timeline_start = body.find("\n## Standards timeline") + migration_start = body.find("\n## Migration note") + if timeline_start >= 0 and migration_start > timeline_start: + timeline = body[timeline_start:migration_start] + missing = [std for std in YEARS if not re.search(r"^### " + re.escape(std) + r"\b", timeline, re.M)] + if missing: + errors.append(f"standards timeline missing: {', '.join(missing)}") + if len(body) < 7000: + errors.append("chapter is unexpectedly short for advanced technical literature") + for item in errors: + print(f"{path}: {item}", file=sys.stderr) + if not errors: + print(f"PASS {path} ({len(numeric)} numbered sections)") + return errors + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("paths", nargs="*", type=Path, + help="README.md paths; defaults to chapters 01–13") + args = ap.parse_args() + paths = args.paths or sorted(Path("01_low_level").glob("[0-9][0-9]_*/README.md")) + if not paths: + print("No chapter READMEs found", file=sys.stderr) + return 2 + failures = sum(bool(validate(p)) for p in paths) + return 1 if failures else 0 + +if __name__ == "__main__": + raise SystemExit(main()) From 131f91d01eb0e8ac419aee4f703857bee81c0394 Mon Sep 17 00:00:00 2001 From: Yurii Cherkasov Date: Mon, 19 Oct 2026 00:58:13 +0700 Subject: [PATCH 3/4] ci: check modified chapter markdown --- .github/workflows/chapter-structure.yml | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/chapter-structure.yml diff --git a/.github/workflows/chapter-structure.yml b/.github/workflows/chapter-structure.yml new file mode 100644 index 0000000..f0b0042 --- /dev/null +++ b/.github/workflows/chapter-structure.yml @@ -0,0 +1,37 @@ +name: Chapter Markdown Structure +on: + pull_request: + paths: + - '01_low_level/**/README.md' + - 'scripts/check_chapter_structure.py' + - '.github/workflows/chapter-structure.yml' + push: + branches: [master] + paths: + - '01_low_level/**/README.md' + - 'scripts/check_chapter_structure.py' + - '.github/workflows/chapter-structure.yml' + workflow_dispatch: +jobs: + chapter-contract: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Validate Book 01 chapter structure + shell: bash + run: | + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + mapfile -t files < <(git diff --name-only --diff-filter=ACMR "${{ github.event.pull_request.base.sha }}" HEAD -- '01_low_level/*/README.md') + if (( ${#files[@]} )); then + python scripts/check_chapter_structure.py "${files[@]}" + else + echo "No changed chapter READMEs to validate" + fi + else + python scripts/check_chapter_structure.py + fi From c274d09179bc21e98073827a57cca908fe281304 Mon Sep 17 00:00:00 2001 From: Yurii Cherkasov Date: Mon, 19 Oct 2026 01:03:48 +0700 Subject: [PATCH 4/4] ci: validate only changed chapters on push and PR; keep manual full-book audit --- .github/workflows/chapter-structure.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/chapter-structure.yml b/.github/workflows/chapter-structure.yml index f0b0042..f2afa8e 100644 --- a/.github/workflows/chapter-structure.yml +++ b/.github/workflows/chapter-structure.yml @@ -26,12 +26,16 @@ jobs: shell: bash run: | if [[ "${{ github.event_name }}" == "pull_request" ]]; then - mapfile -t files < <(git diff --name-only --diff-filter=ACMR "${{ github.event.pull_request.base.sha }}" HEAD -- '01_low_level/*/README.md') - if (( ${#files[@]} )); then - python scripts/check_chapter_structure.py "${files[@]}" - else - echo "No changed chapter READMEs to validate" - fi + base="${{ github.event.pull_request.base.sha }}" + elif [[ "${{ github.event_name }}" == "push" ]]; then + base="${{ github.event.before }}" else python scripts/check_chapter_structure.py + exit + fi + mapfile -t files < <(git diff --name-only --diff-filter=ACMR "$base" HEAD -- '01_low_level/*/README.md') + if (( ${#files[@]} )); then + python scripts/check_chapter_structure.py "${files[@]}" + else + echo "No changed chapter READMEs to validate" fi