From 742635eff3baf8942504a16789f053497509c4d8 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Wed, 4 Nov 2026 16:15:50 -0500 Subject: [PATCH 1/4] CLAUDE.md: doc pages link to Compiler Explorer with the normal includes The flattened-headers section was the only CE guidance, and it describes including the headers by URL. Say that the links on the doc pages include as usual, with Boost from CE's Libraries list, and that dev/ce_link.py generates them. Note what that implies: the link runs the Boost release CE offers, and nothing regenerates it. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index bc029ee5..17f41a3b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -725,6 +725,27 @@ compiles each generated header after the root, checks that each one *fails* on i and runs the `ce/*.cpp` examples against the generated tree. Those examples are also ordinary tests in the CMake build (`ce/CMakeLists.txt`), so they cannot rot silently. +### Compiler Explorer links in the documentation + +**A link from a doc page uses the normal includes, not the flattened headers.** The example +includes `` as it would locally, and the link selects Boost from CE's +Libraries list. The flattened headers are for the `ce/` examples only. + +`dev/ce_link.py` generates the link from the tagged region the page includes, as a +`godbolt.org/clientstate/` URL that carries the source itself: + +```bash +dev/ce_link.py doc/modules/ROOT/examples/ast.cpp +``` + +Its constants fix the compiler (x86-64 clang 23.1.0), the options (`-std=c++17 -O2`) and the Boost +release (1.92). The NOTE at the top of `performance.adoc` states all three, so change them together. + +- **A link runs the Boost release CE offers, not this tree.** An example that uses something newer + than that release breaks its link. Compile it against that release's headers before linking it. +- **Nothing regenerates a link.** When an example changes, its link keeps the old code until + `dev/ce_link.py` is re-run and the new URL pasted into the page. + ### Custom RTTI When `` is unavailable or insufficient, use static_rtti or implement custom RTTI. See `doc/modules/ROOT/examples/custom_rtti/` and policies in `include/boost/openmethod/policies/`. From d7c057e7dbf94f505a0100af743f03758b774ccf Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Wed, 4 Nov 2026 16:21:50 -0500 Subject: [PATCH 2/4] doc: compare the three postfix calls in three Compiler Explorer columns The comparison opened two columns: the virtual function call beside a translation unit with both method calls, whose assembly pane showed the two together. Give each call its own column instead, its source above its assembly. dev/ce_link.py replaces --beside and --beside-tag with --column EXAMPLE:TAG, once per column. A column holds the content region of its example minus every nested region except TAG, so that it compiles one call only. Co-Authored-By: Claude Opus 5.5 --- dev/ce_link.py | 78 +++++++++++++++++++------ doc/modules/ROOT/pages/performance.adoc | 2 +- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/dev/ce_link.py b/dev/ce_link.py index 3a5c224b..42b99d4a 100755 --- a/dev/ce_link.py +++ b/dev/ce_link.py @@ -15,11 +15,16 @@ The source is the tagged region the page includes - `tag=content` by default - so the reader gets exactly the code shown above the link. -With `--beside`, the two examples are compiled side by side, each in its own -compiler pane, to compare the assembly: +With `--column EXAMPLE:TAG`, given once per column, the link opens the +examples side by side, each source above its assembly, to compare the code +generated for several functions. A column holds the tagged region of its +example, minus every region nested in it except `TAG`, so that its assembly +shows that one function: - dev/ce_link.py doc/modules/ROOT/examples/dispatch_open_method.cpp \\ - --beside doc/modules/ROOT/examples/dispatch_virtual_function.cpp + dev/ce_link.py \\ + --column doc/modules/ROOT/examples/dispatch_virtual_function.cpp:call_virtual_function \\ + --column doc/modules/ROOT/examples/dispatch_open_method.cpp:call_via_ref \\ + --column doc/modules/ROOT/examples/dispatch_open_method.cpp:call_via_virtual_ptr An example that includes a Boost header gets the Boost release that Compiler Explorer offers in its Libraries dropdown, not the headers of this tree. @@ -47,10 +52,10 @@ "directives": True, "commentOnly": True, } -TAG_DIRECTIVE = re.compile(r"\s*// (tag|end)::\w+\[\]\s*$") +TAG_DIRECTIVE = re.compile(r"\s*// (tag|end)::(\w+)\[\]\s*$") -def tagged_region(path, tag): +def tagged_region(path, tag, only=None): begin, end = f"// tag::{tag}[]", f"// end::{tag}[]" lines = path.read_text().splitlines(keepends=True) starts = [i for i, line in enumerate(lines) if line.strip() == begin] @@ -60,9 +65,31 @@ def tagged_region(path, tag): sys.exit(f"{path}: expected exactly one `{begin}` ... `{end}`") # Like Asciidoctor, drop the directives of the tags nested in the region. - region = lines[starts[0] + 1 : ends[0]] + # With `only`, also drop the regions of the nested tags other than `only`. + kept, nested, skipping = [], set(), None - return "".join(line for line in region if not TAG_DIRECTIVE.match(line)) + for line in lines[starts[0] + 1 : ends[0]]: + directive = TAG_DIRECTIVE.match(line) + + if directive: + kind, name = directive.groups() + nested.add(name) + + if only and name != only: + skipping = name if kind == "tag" else None + elif not skipping: + kept.append(line) + + if only and only not in nested: + sys.exit(f"{path}: no `// tag::{only}[]` inside `{begin}`") + + source = "".join(kept) + + if only: + # A dropped region leaves the blank lines that surrounded it. + source = re.sub(r"\n{3,}", "\n\n", source).rstrip() + "\n" + + return source def uses_boost(source): @@ -98,7 +125,7 @@ def editor(id, source): return component("codeEditor", id=id, lang="c++", source=source) -def beside_state(base, source, compiler): +def columns_state(sources, compiler): def column(id, source): return { "type": "column", @@ -117,7 +144,9 @@ def column(id, source): ], } - return {"content": [{"type": "row", "content": [column(1, base), column(2, source)]}]} + columns = [column(id, source) for id, source in enumerate(sources, 1)] + + return {"content": [{"type": "row", "content": columns}]} def clientstate_url(state): @@ -133,21 +162,34 @@ def clientstate_url(state): def main(): parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) - parser.add_argument("example", type=Path) + parser.add_argument("example", type=Path, nargs="?") parser.add_argument("--tag", default="content") parser.add_argument( - "--beside", type=Path, help="compile this example beside, for the assembly" + "--column", + action="append", + metavar="EXAMPLE:TAG", + help="a column showing the function in the nested region TAG", ) - parser.add_argument("--beside-tag", help="the tag of the other example (default: --tag)") parser.add_argument("--compiler", default=COMPILER, help="CE compiler id") args = parser.parse_args() - source = tagged_region(args.example, args.tag) - if args.beside: - other = tagged_region(args.beside, args.beside_tag or args.tag) - state = beside_state(other, source, args.compiler) + if bool(args.example) == bool(args.column): + parser.error("give either an example or --column, not both") + + if args.column: + sources = [] + + for spec in args.column: + path, sep, only = spec.rpartition(":") + + if not sep or not path or not only: + parser.error(f"--column {spec}: expected EXAMPLE:TAG") + + sources.append(tagged_region(Path(path), args.tag, only)) + + state = columns_state(sources, args.compiler) else: - state = session_state(source, args.compiler) + state = session_state(tagged_region(args.example, args.tag), args.compiler) print(clientstate_url(state)) diff --git a/doc/modules/ROOT/pages/performance.adoc b/doc/modules/ROOT/pages/performance.adoc index 8d5dbe20..59523928 100644 --- a/doc/modules/ROOT/pages/performance.adoc +++ b/doc/modules/ROOT/pages/performance.adoc @@ -96,7 +96,7 @@ mov rax, qword ptr [rip + postfix::fn+88] jmp qword ptr [rdi + 8*rax] # TAILCALL ---- -link:https://godbolt.org/clientstate/eNrNU1tr2zAU%2FisHDYpL3Uu8hzL38tAusJfFg%2FZtKUGx5VYgHxlJ9haM99t3ZDup03ZrO1ZoAol0vu9c9Z2GpRqdQMdi%2BN4wtyoFnZjRP1gIT2OpVlWBf4GLUqM3h6PLjBcDmolpJp02W%2FCV487jDZMZ%2FU0IUxxvO4%2B9PU%2B1ujJpF%2BKDxFRVmYBTqa0zghfnc5wjHavUwYwSQDNHoE8tjau4gl%2FeGOxC027bJTqouao8Rr1YB2dwdLLNqbXMoKREufwZWJfF8ZB0B7TddmtPfB2dQ8qVWgwhFnmFqZMag57si9kBpN8QHgccSvfwwTot2X34ObK2DeHloy5KqcSzg95M9ompb2J4k8eij5MjDyi5tP7db%2BisS9%2Bdv7J9auiMfCfHsJ9EnphL5YSxfUqSi6ITvRT1zjJRUEQl7i2KL4Wyo7tcGm5WlzQMMuZc2c5PGkEDrcWISYUW1F6CajUY27a9eTCtN9dt9DLdLjU97KEuBRbC3ens4K4sScNvL%2BxeoZWVeAtdESS%2BTRVxvBZs6UxHvEiSq%2BtF8m06%2Bzq9%2FpJ8DgZBhhCMqKf3sj7%2Fk6zDbo92Hy0IXxiRv2Yv1ivR0zaL8TDqqLzXlvpMpv%2B%2BgtFYJNE%2FrGDDcEjXPamH6p4%2B%2BRSx9p1vqP%2F%2BBlxDKSs%3D[Compare the three calls on Compiler Explorer^] +link:https://godbolt.org/clientstate/eNrtU1tr2zAU%2FisHDYpL3UuchzH38tA2sJfFg%2FZtKUGx5VYgS0KSvQXj%2FfYd2U7qrJc1g%2BxlTSCRznduOuf7apIq6Zh0JIZvNXFLzfBEjPpOQngeS5UoC%2FkKXGglvTkcXKa06NGMTTLulNmAbxx1Hq8Jz%2FBvhJig8r6NODjwrlaVJm1TfOAyFWXG4Iwr6wyjxcVMziQey9TBFAtAPZOAn4obV1IBP70x2Ie62bRz6aCiovQYvsU6OIeT002fSvEMNBbK%2BY%2FAuiyO%2B6J7oOxmWHPq%2B2gDUirEvE8xz0uZOq5k0Dn7ZvZA4m8ITxP2rXv4aFUW7T79TJKmCeHtoy40F%2ByPg15P9pmpr3N4k8ei8ejEA4IvrN%2F7HZ6V9q%2FzV3KIDzrH2NFHOEwi75hz4ZixXUmki8ATbgrfTjJWYEbBHi2CLpiwgztfGGqWVzgMNOZU2DaOG4YDrdjAExst8HmJFMve2DTN3W%2FT2jlvo7fxdqFwscdKM1kw96CyowetkcO7J3bH0NJyeQ9tE0i%2BdRdxvCKsdqZ1vEySm9t58nUy%2FTK5%2FZxcBz0hQwgGrmePtL54idZhq6P9JwKhc8PybXSxkkTntjthRMPVRX8hjJrIvlw7aA9VnfvoU0Sad90Mpz1%2B1822uhkk2jbpv9bSeLjO8X%2BnJf%2F9Be%2B0G0Y%3D[Compare the three calls on Compiler Explorer^] `virtual_ptr` arguments are passed through the method call, to the overrider, which can use them to make further method calls. From 07f1370d0b5b6980006c320c34491458398adbb3 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Wed, 4 Nov 2026 16:24:21 -0500 Subject: [PATCH 3/4] doc: rename call_via_ref and call_via_virtual_ptr to call_method_via_* The names now say what is called, beside call_virtual_function, in the listings of performance.adoc and in the Compiler Explorer comparison. ast.cpp and the two custom RTTI examples have the same helpers, outside any tagged region, and are renamed with them. Co-Authored-By: Claude Opus 5.5 --- dev/ce_link.py | 7 ++++--- doc/modules/ROOT/examples/ast.cpp | 5 +++-- .../ROOT/examples/custom_rtti/1/custom_rtti.cpp | 2 +- .../ROOT/examples/custom_rtti/2/custom_rtti.cpp | 2 +- doc/modules/ROOT/examples/dispatch_open_method.cpp | 13 +++++++------ doc/modules/ROOT/pages/performance.adoc | 10 +++++----- 6 files changed, 21 insertions(+), 18 deletions(-) diff --git a/dev/ce_link.py b/dev/ce_link.py index 42b99d4a..6201a19d 100755 --- a/dev/ce_link.py +++ b/dev/ce_link.py @@ -23,8 +23,8 @@ dev/ce_link.py \\ --column doc/modules/ROOT/examples/dispatch_virtual_function.cpp:call_virtual_function \\ - --column doc/modules/ROOT/examples/dispatch_open_method.cpp:call_via_ref \\ - --column doc/modules/ROOT/examples/dispatch_open_method.cpp:call_via_virtual_ptr + --column doc/modules/ROOT/examples/dispatch_open_method.cpp:call_method_via_ref \\ + --column doc/modules/ROOT/examples/dispatch_open_method.cpp:call_method_via_virtual_ptr An example that includes a Boost header gets the Boost release that Compiler Explorer offers in its Libraries dropdown, not the headers of this tree. @@ -47,7 +47,8 @@ "demangle": True, "labels": True, # CE takes a function whose every instruction comes from a header for - # library code: `call_via_virtual_ptr` is the inlined dispatch, all of it. + # library code: `call_method_via_virtual_ptr` is the inlined dispatch, all + # of it. "libraryCode": False, "directives": True, "commentOnly": True, diff --git a/doc/modules/ROOT/examples/ast.cpp b/doc/modules/ROOT/examples/ast.cpp index 6912cbd7..9ecf11b1 100644 --- a/doc/modules/ROOT/examples/ast.cpp +++ b/doc/modules/ROOT/examples/ast.cpp @@ -85,10 +85,11 @@ int main() { } // end::content[] -void call_via_ref(const Node& node, std::ostream& os) { +void call_method_via_ref(const Node& node, std::ostream& os) { postfix(node, os); } -void call_via_virtual_ptr(virtual_ptr node, std::ostream& os) { +void call_method_via_virtual_ptr( + virtual_ptr node, std::ostream& os) { postfix(node, os); } diff --git a/doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp b/doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp index 6e3ada52..d08e5b6c 100644 --- a/doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp +++ b/doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp @@ -120,6 +120,6 @@ int main() { std::cout << " = " << e.value() << "\n"; // 2 3 + 4 * = 20 } -void call_via_ref(const Node& node, std::ostream& os) { +void call_method_via_ref(const Node& node, std::ostream& os) { postfix(node, os); } diff --git a/doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp b/doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp index 87fc9657..dfa7e0d8 100644 --- a/doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp +++ b/doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp @@ -114,6 +114,6 @@ int main() { std::cout << value(e) << "\n"; // 2 3 + 4 * = 20 } -auto call_via_ref(const Node& node) { +auto call_method_via_ref(const Node& node) { return value(node); } diff --git a/doc/modules/ROOT/examples/dispatch_open_method.cpp b/doc/modules/ROOT/examples/dispatch_open_method.cpp index 7db59b11..1a7c4c52 100644 --- a/doc/modules/ROOT/examples/dispatch_open_method.cpp +++ b/doc/modules/ROOT/examples/dispatch_open_method.cpp @@ -18,17 +18,18 @@ using boost::openmethod::virtual_ptr; BOOST_OPENMETHOD(postfix, (virtual_ptr node, std::ostream& os), void); -// tag::call_via_ref[] -void call_via_ref(const Node& node, std::ostream& os) { +// tag::call_method_via_ref[] +void call_method_via_ref(const Node& node, std::ostream& os) { postfix(node, os); } -// end::call_via_ref[] +// end::call_method_via_ref[] -// tag::call_via_virtual_ptr[] -void call_via_virtual_ptr(virtual_ptr node, std::ostream& os) { +// tag::call_method_via_virtual_ptr[] +void call_method_via_virtual_ptr( + virtual_ptr node, std::ostream& os) { postfix(node, os); } -// end::call_via_virtual_ptr[] +// end::call_method_via_virtual_ptr[] // end::content[] int main() { diff --git a/doc/modules/ROOT/pages/performance.adoc b/doc/modules/ROOT/pages/performance.adoc index 59523928..31314678 100644 --- a/doc/modules/ROOT/pages/performance.adoc +++ b/doc/modules/ROOT/pages/performance.adoc @@ -31,7 +31,7 @@ Let's look at a method call now: [source,c++] ---- -include::{examplesdir}/dispatch_open_method.cpp[tag=call_via_ref] +include::{examplesdir}/dispatch_open_method.cpp[tag=call_method_via_ref] ---- This compiles to (variable names are shortened for readability): @@ -65,8 +65,8 @@ a single virtual argument _via_ _a_ _reference_ is between 30% and 50% slower than calling the equivalent virtual function, with an empty body and no other arguments. In most real programs, the overhead would be unnoticeable. -*However*, `call_via_ref` does two things: it constructs a `virtual_ptr` -from a `const Node&`, then it calls the method. +*However*, `call_method_via_ref` does two things: it constructs a +`virtual_ptr` from a `const Node&`, then it calls the method. The construction of the `virtual_ptr` is the costly part. It performs a lookup in a perfect hash table, indexed by pointers to `std::type_info`, to find the @@ -85,7 +85,7 @@ If we already have a `virtual_ptr`: [source,c++] ---- -include::{examplesdir}/dispatch_open_method.cpp[tag=call_via_virtual_ptr] +include::{examplesdir}/dispatch_open_method.cpp[tag=call_method_via_virtual_ptr] ---- A method call compiles to: @@ -96,7 +96,7 @@ mov rax, qword ptr [rip + postfix::fn+88] jmp qword ptr [rdi + 8*rax] # TAILCALL ---- -link:https://godbolt.org/clientstate/eNrtU1tr2zAU%2FisHDYpL3UuchzH38tA2sJfFg%2FZtKUGx5VYgS0KSvQXj%2FfYd2U7qrJc1g%2BxlTSCRznduOuf7apIq6Zh0JIZvNXFLzfBEjPpOQngeS5UoC%2FkKXGglvTkcXKa06NGMTTLulNmAbxx1Hq8Jz%2FBvhJig8r6NODjwrlaVJm1TfOAyFWXG4Iwr6wyjxcVMziQey9TBFAtAPZOAn4obV1IBP70x2Ie62bRz6aCiovQYvsU6OIeT002fSvEMNBbK%2BY%2FAuiyO%2B6J7oOxmWHPq%2B2gDUirEvE8xz0uZOq5k0Dn7ZvZA4m8ITxP2rXv4aFUW7T79TJKmCeHtoy40F%2ByPg15P9pmpr3N4k8ei8ejEA4IvrN%2F7HZ6V9q%2FzV3KIDzrH2NFHOEwi75hz4ZixXUmki8ATbgrfTjJWYEbBHi2CLpiwgztfGGqWVzgMNOZU2DaOG4YDrdjAExst8HmJFMve2DTN3W%2FT2jlvo7fxdqFwscdKM1kw96CyowetkcO7J3bH0NJyeQ9tE0i%2BdRdxvCKsdqZ1vEySm9t58nUy%2FTK5%2FZxcBz0hQwgGrmePtL54idZhq6P9JwKhc8PybXSxkkTntjthRMPVRX8hjJrIvlw7aA9VnfvoU0Sad90Mpz1%2B1822uhkk2jbpv9bSeLjO8X%2BnJf%2F9Be%2B0G0Y%3D[Compare the three calls on Compiler Explorer^] +link:https://godbolt.org/clientstate/eNrtU11r2zAU%2FSsXDYpL3a5xHsbcj4dtgb0sHrRvSwmKLbcCWRKS7C0Y77fvynZSu2u7rJC9rAkk0j1X9%2FOcmqRKOiYdieFbTdxaMzwRo76TEB7HUiXKQj4DF1pJbw4HlzktejRjs4w7ZUbwlaPO4zXhGf5NEBNU3rYvjo68q1WlSdsQb7hMRZkxOOfKOsNocbmQC4nHMnUwxwRQLyTgp%2BLGlVTAT28MDqFuxnYuHVRUlB7DXqyDCzg9G%2FtUimegMVHOfwTWZXHcJz0AZcfPmjNfR%2FsgpUIs%2BxDLvJSp40oGnbMv5gAk%2Fobwe8C%2BdA%2BfbNKi3YdfSNI0Iew%2B6kJzwf446O1kH5n6NoY3eSyaTk49IPjK%2Br3f4Flp352%2FkmNs6ALfTt7BcRJ5x5wLx4ztUiJdBJ5wU9g7yViBEQW7twi6YsIO7nxlqFl%2FxGGgMafCtu%2B4YTjQig08sdAC20ukWPfGpmluHkxr77yNduPtSuFi3yrNZMHcncpO7rRGDu%2Bf2B1DS8vlLbRFIPm2VcTxhrDamdbxQ5JcXS%2BTr7P5l9n15%2BRT0BMyhGDgen5P68unaB22Ojp8IJAuL%2BqELg3L%2F0YeG2V0bvvTRzTcYPQCfdRE9unaeXuo6twn7yPSvMpnOO3pq3xeKJ9BvGDUy64Z%2FrWwpsPdTv87YfnvLzUYIlA%3D[Compare the three calls on Compiler Explorer^] `virtual_ptr` arguments are passed through the method call, to the overrider, which can use them to make further method calls. From eea7e9cba9aa5b23e7486751c5122765e7ad0418 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Wed, 4 Nov 2026 16:31:21 -0500 Subject: [PATCH 4/4] doc: drop the call_ prefix from the dispatch examples' functions call_virtual_function, call_method_via_ref and call_method_via_virtual_ptr become virtual_function, method_via_ref and method_via_virtual_ptr, with their tags, in the listings of performance.adoc, in its Compiler Explorer comparison, and in the examples that carry the same helpers. Co-Authored-By: Claude Opus 5.5 --- dev/ce_link.py | 8 ++++---- doc/modules/ROOT/examples/ast.cpp | 5 ++--- .../ROOT/examples/ast_virtual_function_2.cpp | 2 +- .../ROOT/examples/custom_rtti/1/custom_rtti.cpp | 2 +- .../ROOT/examples/custom_rtti/2/custom_rtti.cpp | 2 +- doc/modules/ROOT/examples/dispatch_open_method.cpp | 13 ++++++------- .../ROOT/examples/dispatch_virtual_function.cpp | 6 +++--- doc/modules/ROOT/pages/performance.adoc | 10 +++++----- 8 files changed, 23 insertions(+), 25 deletions(-) diff --git a/dev/ce_link.py b/dev/ce_link.py index 6201a19d..e80642f3 100755 --- a/dev/ce_link.py +++ b/dev/ce_link.py @@ -22,9 +22,9 @@ shows that one function: dev/ce_link.py \\ - --column doc/modules/ROOT/examples/dispatch_virtual_function.cpp:call_virtual_function \\ - --column doc/modules/ROOT/examples/dispatch_open_method.cpp:call_method_via_ref \\ - --column doc/modules/ROOT/examples/dispatch_open_method.cpp:call_method_via_virtual_ptr + --column doc/modules/ROOT/examples/dispatch_virtual_function.cpp:virtual_function \\ + --column doc/modules/ROOT/examples/dispatch_open_method.cpp:method_via_ref \\ + --column doc/modules/ROOT/examples/dispatch_open_method.cpp:method_via_virtual_ptr An example that includes a Boost header gets the Boost release that Compiler Explorer offers in its Libraries dropdown, not the headers of this tree. @@ -47,7 +47,7 @@ "demangle": True, "labels": True, # CE takes a function whose every instruction comes from a header for - # library code: `call_method_via_virtual_ptr` is the inlined dispatch, all + # library code: `method_via_virtual_ptr` is the inlined dispatch, all # of it. "libraryCode": False, "directives": True, diff --git a/doc/modules/ROOT/examples/ast.cpp b/doc/modules/ROOT/examples/ast.cpp index 9ecf11b1..e3ca92bc 100644 --- a/doc/modules/ROOT/examples/ast.cpp +++ b/doc/modules/ROOT/examples/ast.cpp @@ -85,11 +85,10 @@ int main() { } // end::content[] -void call_method_via_ref(const Node& node, std::ostream& os) { +void method_via_ref(const Node& node, std::ostream& os) { postfix(node, os); } -void call_method_via_virtual_ptr( - virtual_ptr node, std::ostream& os) { +void method_via_virtual_ptr(virtual_ptr node, std::ostream& os) { postfix(node, os); } diff --git a/doc/modules/ROOT/examples/ast_virtual_function_2.cpp b/doc/modules/ROOT/examples/ast_virtual_function_2.cpp index c0872c46..4c0da188 100644 --- a/doc/modules/ROOT/examples/ast_virtual_function_2.cpp +++ b/doc/modules/ROOT/examples/ast_virtual_function_2.cpp @@ -51,6 +51,6 @@ int main() { } // end::all[] -void call_virtual_function(const Node& node, std::ostream& os) { +void virtual_function(const Node& node, std::ostream& os) { node.postfix(os); } diff --git a/doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp b/doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp index d08e5b6c..a5f60deb 100644 --- a/doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp +++ b/doc/modules/ROOT/examples/custom_rtti/1/custom_rtti.cpp @@ -120,6 +120,6 @@ int main() { std::cout << " = " << e.value() << "\n"; // 2 3 + 4 * = 20 } -void call_method_via_ref(const Node& node, std::ostream& os) { +void method_via_ref(const Node& node, std::ostream& os) { postfix(node, os); } diff --git a/doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp b/doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp index dfa7e0d8..349a5d0c 100644 --- a/doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp +++ b/doc/modules/ROOT/examples/custom_rtti/2/custom_rtti.cpp @@ -114,6 +114,6 @@ int main() { std::cout << value(e) << "\n"; // 2 3 + 4 * = 20 } -auto call_method_via_ref(const Node& node) { +auto method_via_ref(const Node& node) { return value(node); } diff --git a/doc/modules/ROOT/examples/dispatch_open_method.cpp b/doc/modules/ROOT/examples/dispatch_open_method.cpp index 1a7c4c52..31353ea8 100644 --- a/doc/modules/ROOT/examples/dispatch_open_method.cpp +++ b/doc/modules/ROOT/examples/dispatch_open_method.cpp @@ -18,18 +18,17 @@ using boost::openmethod::virtual_ptr; BOOST_OPENMETHOD(postfix, (virtual_ptr node, std::ostream& os), void); -// tag::call_method_via_ref[] -void call_method_via_ref(const Node& node, std::ostream& os) { +// tag::method_via_ref[] +void method_via_ref(const Node& node, std::ostream& os) { postfix(node, os); } -// end::call_method_via_ref[] +// end::method_via_ref[] -// tag::call_method_via_virtual_ptr[] -void call_method_via_virtual_ptr( - virtual_ptr node, std::ostream& os) { +// tag::method_via_virtual_ptr[] +void method_via_virtual_ptr(virtual_ptr node, std::ostream& os) { postfix(node, os); } -// end::call_method_via_virtual_ptr[] +// end::method_via_virtual_ptr[] // end::content[] int main() { diff --git a/doc/modules/ROOT/examples/dispatch_virtual_function.cpp b/doc/modules/ROOT/examples/dispatch_virtual_function.cpp index 9ba1f6b1..0dcd9c01 100644 --- a/doc/modules/ROOT/examples/dispatch_virtual_function.cpp +++ b/doc/modules/ROOT/examples/dispatch_virtual_function.cpp @@ -14,11 +14,11 @@ struct Node { virtual void postfix(std::ostream& os) const = 0; }; -// tag::call_virtual_function[] -void call_virtual_function(const Node& node, std::ostream& os) { +// tag::virtual_function[] +void virtual_function(const Node& node, std::ostream& os) { node.postfix(os); } -// end::call_virtual_function[] +// end::virtual_function[] // end::content[] int main() { diff --git a/doc/modules/ROOT/pages/performance.adoc b/doc/modules/ROOT/pages/performance.adoc index 31314678..e8e73f78 100644 --- a/doc/modules/ROOT/pages/performance.adoc +++ b/doc/modules/ROOT/pages/performance.adoc @@ -14,7 +14,7 @@ function call: [source,c++] ---- -include::{examplesdir}/dispatch_virtual_function.cpp[tag=call_virtual_function] +include::{examplesdir}/dispatch_virtual_function.cpp[tag=virtual_function] ---- Clang compiles this function to the following assembly on the x64 architecture: @@ -31,7 +31,7 @@ Let's look at a method call now: [source,c++] ---- -include::{examplesdir}/dispatch_open_method.cpp[tag=call_method_via_ref] +include::{examplesdir}/dispatch_open_method.cpp[tag=method_via_ref] ---- This compiles to (variable names are shortened for readability): @@ -65,7 +65,7 @@ a single virtual argument _via_ _a_ _reference_ is between 30% and 50% slower than calling the equivalent virtual function, with an empty body and no other arguments. In most real programs, the overhead would be unnoticeable. -*However*, `call_method_via_ref` does two things: it constructs a +*However*, `method_via_ref` does two things: it constructs a `virtual_ptr` from a `const Node&`, then it calls the method. The construction of the `virtual_ptr` is the costly part. It performs a lookup @@ -85,7 +85,7 @@ If we already have a `virtual_ptr`: [source,c++] ---- -include::{examplesdir}/dispatch_open_method.cpp[tag=call_method_via_virtual_ptr] +include::{examplesdir}/dispatch_open_method.cpp[tag=method_via_virtual_ptr] ---- A method call compiles to: @@ -96,7 +96,7 @@ mov rax, qword ptr [rip + postfix::fn+88] jmp qword ptr [rdi + 8*rax] # TAILCALL ---- -link:https://godbolt.org/clientstate/eNrtU11r2zAU%2FSsXDYpL3a5xHsbcj4dtgb0sHrRvSwmKLbcCWRKS7C0Y77fvynZSu2u7rJC9rAkk0j1X9%2FOcmqRKOiYdieFbTdxaMzwRo76TEB7HUiXKQj4DF1pJbw4HlzktejRjs4w7ZUbwlaPO4zXhGf5NEBNU3rYvjo68q1WlSdsQb7hMRZkxOOfKOsNocbmQC4nHMnUwxwRQLyTgp%2BLGlVTAT28MDqFuxnYuHVRUlB7DXqyDCzg9G%2FtUimegMVHOfwTWZXHcJz0AZcfPmjNfR%2FsgpUIs%2BxDLvJSp40oGnbMv5gAk%2Fobwe8C%2BdA%2BfbNKi3YdfSNI0Iew%2B6kJzwf446O1kH5n6NoY3eSyaTk49IPjK%2Br3f4Flp352%2FkmNs6ALfTt7BcRJ5x5wLx4ztUiJdBJ5wU9g7yViBEQW7twi6YsIO7nxlqFl%2FxGGgMafCtu%2B4YTjQig08sdAC20ukWPfGpmluHkxr77yNduPtSuFi3yrNZMHcncpO7rRGDu%2Bf2B1DS8vlLbRFIPm2VcTxhrDamdbxQ5JcXS%2BTr7P5l9n15%2BRT0BMyhGDgen5P68unaB22Ojp8IJAuL%2BqELg3L%2F0YeG2V0bvvTRzTcYPQCfdRE9unaeXuo6twn7yPSvMpnOO3pq3xeKJ9BvGDUy64Z%2FrWwpsPdTv87YfnvLzUYIlA%3D[Compare the three calls on Compiler Explorer^] +link:https://godbolt.org/clientstate/eNrtU1tr2zAU%2FisHDYpL3UuchzL38tA2sJfFg%2FZtKUGx5VYgS0KSvQXj%2FfYd2U7ibN3WDLKXNYFEOt%2B56Zzvq0mqpGPSkRg%2B18QtNcMTMeoLCeFlLFWiLORv4EIr6c3h4DKlRY9mbJJxp8wWfO%2Bo83hNeIZ%2FI8QElU9txNGRd7WqNGmb4h2XqSgzBpdcWWcYLa5ncibxWKYOplgA6pkE%2FFTcuJIK%2BOaNwSHUzbadSwcVFaXH8C3WwRWcXWz7VIpnoLFQzr8G1mVx3Bc9AGW3w5oL30cb0EfP81KmjisZdH6%2BjwOQ%2BBvCz7n6rj18sqqIdp95JknThPD6KReaC%2FbHGa%2BH%2BsLA1zm8yWPReHTmAcEX1q%2F8Ec9K%2B9f5KznGB11h7OgcjpPIO%2BZcOGZsVxKZIvCES8K3k4wVmFGwjUXQBRN2cOcLQ83yFoeBxpwK28Zxw3CgFRt4YqMFPi%2BRYtkbm6Z5%2FGFae6ds9DrKLhQu9lRpJgvmnlV28qw10nf%2FnO7IWVoun6BtAsm37iKOV4TVzrSON0ly%2FzBPPk2mHycPH5K7oCdkCMHA9XJD6%2Btf0TpsJXS40UZXcl5xOjcs30UZK1F0bvuTRjRcXvQX0qiJ7Mu1o%2FZQ1bmP3kekeVPOcNrjN%2BXsrpxBql3T%2Fms1jYcLHf93avLf78akGxU%3D[Compare the three calls on Compiler Explorer^] `virtual_ptr` arguments are passed through the method call, to the overrider, which can use them to make further method calls.