From 8a4614bb66eeb193fcd4ca0cb36a46e58d0a64e7 Mon Sep 17 00:00:00 2001 From: Yurii Cherkasov Date: Mon, 19 Oct 2026 00:53:59 +0700 Subject: [PATCH 1/4] docs: expand namespaces chapter to C++26 and normalize sections --- 01_low_level/11_namespaces/README.md | 113 +++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 14 deletions(-) diff --git a/01_low_level/11_namespaces/README.md b/01_low_level/11_namespaces/README.md index fad8ff1..4d4fc87 100644 --- a/01_low_level/11_namespaces/README.md +++ b/01_low_level/11_namespaces/README.md @@ -8,7 +8,7 @@ They exist to structure programs, prevent naming conflicts, and enable large-sca --- -## Purpose of namespaces +## 1. Purpose of namespaces * Use namespaces to express logical structure (C++ Standard 14.3.1) * Prevent name collisions across large codebases and libraries @@ -27,7 +27,7 @@ Namespaces **do not**: --- -## Linkage and namespaces +## 2. Linkage and namespaces * A name that can be used in translation units different from the one in which it was defined is said to have **external linkage** * A name that can be referred to only in the translation unit in which it is defined is said to have **internal linkage** @@ -50,7 +50,7 @@ extern const int y = 20; // explicitly external --- -## Unnamed (anonymous) namespaces +## 3. Unnamed (anonymous) namespaces * An unnamed namespace can be used to make names local to a compilation unit * The effect of an unnamed namespace is very similar to that of internal linkage @@ -64,14 +64,14 @@ namespace { ### Important details -* Entities technically have external linkage -* But they are **unreachable** from other translation units +* Unnamed-namespace members have internal linkage in the usual C++ model. +* Their names are local to the translation unit * Preferred modern replacement for `static` globals * Applies to variables, functions, classes, templates --- -## Named namespaces and namespace merging +## 4. Named namespaces and namespace merging Namespaces can be **defined in multiple places** and are merged by name: @@ -93,7 +93,7 @@ namespace my { --- -## Namespace aliases +## 5. Namespace aliases Namespace aliases provide shorter or version-neutral access: @@ -114,7 +114,7 @@ namespace lib = very_long_vendor_library; --- -## `using` declarations and `using` directives +## 6. `using` declarations and `using` directives ### `using` directive @@ -141,7 +141,7 @@ using std::vector; --- -## Argument-Dependent Lookup (ADL) +## 7. Argument-Dependent Lookup (ADL) Namespaces participate in **argument-dependent lookup** (Koenig lookup): @@ -169,7 +169,7 @@ normalize(v); // found via ADL --- -## Namespaces and operator overloading +## 8. Namespaces and operator overloading Operators should usually be **free functions in the same namespace as their operands**: @@ -187,7 +187,7 @@ namespace math { --- -## Inline namespaces (C++11+) +## 9. Inline namespaces (C++11+) * The `inline` specifier makes a nested namespace the **default meaning** of the enclosing namespace * This is primarily used for **API and ABI versioning** @@ -230,7 +230,7 @@ using lib; --- -## Headers, includes, and namespaces +## 10. Headers, includes, and namespaces * Spaces are significant within the `< >` or `" "` of an include directive * The absence of a `.h` suffix does **not** imply anything about how the header is stored @@ -245,7 +245,7 @@ using lib; --- -## C linkage and namespaces +## 11. C linkage and namespaces * We can specify a linkage convention to be used in an `extern` declaration * `extern "C"` specifies C linkage (no name mangling) @@ -274,6 +274,91 @@ extern "C" { --- +## 12. Modern name lookup and namespace evolution: advanced boundaries + +### ADL uses associated classes and namespaces, not a textual global search + +Argument-dependent lookup adds candidate functions from namespaces and classes associated with the argument types. It enables `swap(a,b)` customization and the hidden-friend idiom but can introduce overload candidates that ordinary unqualified lookup would not find. + +```cpp +namespace network { + struct Packet { + friend bool operator==(const Packet&, const Packet&) = default; + }; +} +bool same(network::Packet a, network::Packet b) { return a == b; } +``` + +The friend function can be found through argument-dependent lookup even though it is not ordinarily visible through namespace-qualified lookup. This is why adding operators or converting constructors can change overload resolution across otherwise unrelated call sites. + +### Hidden friends constrain lookup surface but are not private implementation secrets + +A friend function first declared within a class is a namespace-scope function associated with that class, and often participates in ADL without being found by ordinary unqualified lookup. This pattern improves generic operator design and prevents inappropriate implicit conversions from making overload candidates available too broadly. + +Do not confuse it with member-function access control or module export restrictions. A hidden friend can still be part of a public library API, and changing its signature can create source or binary compatibility issues. + +### C++17 nested-namespace definitions reduce boilerplate only + +`namespace library::detail { ... }` is shorthand for nested namespace blocks. It does not create a visibility boundary, a module partition, or an implicitly private implementation area. + +```cpp +namespace library::detail { + inline constexpr int format_version = 2; +} +``` + +If an implementation name is in a public header, client code may depend on it despite the convention `detail`. A versioned public API requires documentation and change discipline rather than relying on namespace spelling to hide entities. + +### C++20 modules decouple namespaces from compilation units + +Named module boundaries govern reachability and module linkage; namespaces remain a name-organization and lookup facility. Two modules can define names in the same namespace without becoming the same module, and an exported namespace-qualified declaration must still comply with the relevant module interface rules. + +```cpp +// Interface of a C++20 named module (illustrative) +export module example.math; +export namespace example { + int calculate(int); +} +``` + +A module name is not a namespace, and `import example.math;` is not a `using namespace example;` directive. This distinction is essential in migrations from textual headers, particularly where dependent lookup and template instantiation are involved. + +### C++20 `using enum` may reintroduce exactly the collisions scoped enums prevented + +Scoped enum members are normally qualified names. `using enum Color;` introduces them into the local scope, which is convenient for switches but can conflict with other local declarations or imported enumerators. + +Prefer it within a small function scope with a clear enum domain. Avoid putting a blanket `using enum` into a public header, just as you would avoid a broad namespace using-directive there. + +### Inline namespaces help version APIs but complicate overload sets + +An inline namespace makes its members available for lookup as though injected into the enclosing namespace while retaining an explicit qualified name. This supports coexistence of ABI versions but does not by itself prevent conflicting overloads, duplicate definitions, or symbol-export errors. + +```cpp +namespace api { +inline namespace v2 { int parse(const char*); } +namespace v1 { int parse(const char*); } +} +``` + +Be cautious when changing which namespace is inline: ADL, mangled names, template specializations, and client rebuild requirements may change. ABI versioning needs toolchain-specific symbol verification. + +### Namespace aliases are not ABI aliases + +A namespace alias redirects source-level name lookup; it does not rename definitions or exported symbols, and cannot be reopened as a namespace definition. + +```cpp +namespace vendor::version_2 { void dispatch(); } +namespace client_api = vendor::version_2; +``` + +Use aliases to reduce qualification noise, not as proof that a linked library can replace one namespace's ABI with another. + +### C++23/26 reflection does not change foundational namespace lookup + +Recent language advances such as explicit object parameters and C++26 static reflection introduce new forms of compile-time inspection and declarations, but do not make namespaces runtime objects or make ADL optional. Generic code still needs explicit reasoning about ordinary lookup, dependent lookup, argument-associated candidates, and two-phase template lookup. + +--- + ## Rules worth keeping in working memory The controlling distinction for namespace lookup 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. @@ -411,7 +496,7 @@ Modules affect declaration visibility but do not make namespaces modules. No material change to namespace lookup fundamentals. -### C++26: Forthcoming support +### C++26: Evolving tools without fundamental linkage or lookup changes New reflection/library features still obey namespace and lookup rules. From 33f10d710203855497dd4734f36c475c1d8320ec Mon Sep 17 00:00:00 2001 From: Yurii Cherkasov Date: Mon, 19 Oct 2026 00:58:16 +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 06964aa9b55fe720c5eb3eda2819e764ae1ebe31 Mon Sep 17 00:00:00 2001 From: Yurii Cherkasov Date: Mon, 19 Oct 2026 00:58:18 +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 8fdb2590931e63553eb413a7ee6101b4e046f638 Mon Sep 17 00:00:00 2001 From: Yurii Cherkasov Date: Mon, 19 Oct 2026 01:03:51 +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