-
Notifications
You must be signed in to change notification settings - Fork 3
Add presentation for lightning talk at C++ London #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,217 @@ | ||
| --- | ||
| marp: true | ||
| theme: default | ||
| paginate: true | ||
| size: 16:9 | ||
| #footer: https://github.com/jbcoe/cc-protocol | ||
| --- | ||
|
|
||
| # Structural Subtyping for C++ | ||
|
|
||
| ## [P4148R0](https://wg21.link/P4148R0) | ||
|
|
||
| ### 11 May 2026 | ||
|
|
||
| --- | ||
|
|
||
| # Introduction | ||
|
|
||
| We propose adding two new class templates to the C++ Standard Library to support structural sub-typing at runtime: | ||
|
|
||
| ```c++ | ||
| template <typename T, typename Allocator = std::allocator<T>> | ||
| class protocol; | ||
| ``` | ||
|
|
||
| ```c++ | ||
| template <typename T> | ||
| class protocol_view; | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| # Polymorphism | ||
|
|
||
| polymorphism (from the Greek for "many forms") is the ability of different objects to respond to the same function call in their own specific ways. | ||
|
|
||
| C++ achieves polymorphism through mechanisms: Compile-time and Runtime. | ||
|
|
||
| --- | ||
|
|
||
| # Compile-time Polymorphism | ||
|
|
||
| Overloaded functions or function templates allow the compiler to detemrine which function to call during the build process: | ||
|
|
||
| ```c++ | ||
| double func(const A& a); | ||
| double func(const B& b); | ||
| double func(const C& c); | ||
| ``` | ||
|
|
||
| ```c++ | ||
| <template typename T> | ||
| double func(const T& t); | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| # Runtime Polymorphism | ||
|
|
||
| Inheritance-based polymorphism enables runtime dispatch by using base-class interfaces to invoke overridden virtual functions across an extensible, open set of derived types. | ||
|
|
||
| ```c++ | ||
| struct Shape { | ||
| virtual size_t sides() const = 0; | ||
| virtual ~Shape() = default; | ||
| }; | ||
|
|
||
| class Triangle : public Shape { | ||
| public: | ||
| size_t sides() const override { return 3; } | ||
| }; | ||
|
|
||
| class Square : public Shape { | ||
| public: | ||
| size_t sides() const override { return 4; } | ||
| }; | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| # Closed set Runtime Polymorphism | ||
|
|
||
| In C++ we can use `std::variant` and `std::visit` to implement closed-set polymorphism: runtime dispatch over a predefined set of types. | ||
|
|
||
| ```c++ | ||
| template<class... Ts> struct overload : Ts... { using Ts::operator()...; }; | ||
|
|
||
| ... | ||
|
|
||
| std::variant<int, std::string> myData = "Polymorphic String"; | ||
|
|
||
| std::visit(overload { | ||
| [](int val) { | ||
| std::cout << "Processing int: " << val * 2 << "\n"; | ||
| }, | ||
| [](const std::string& val) { | ||
| std::cout << "Processing string: " << val << " (length: " << val.size() << ")\n"; | ||
| } | ||
| }, myData); | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| # Type erasure | ||
|
|
||
| Type erasure enables runtime polymorphism for an open set of unrelated types. | ||
|
|
||
| ```c++ | ||
| class AnyDrawable { | ||
| struct Interface { | ||
| virtual ~Interface() = default; | ||
| virtual void draw() = 0; | ||
| }; | ||
|
|
||
| template<typename T> struct Model : Interface { | ||
| T d; Model(T t) : d(t) {} | ||
| void draw() override { d.draw(); } | ||
| }; | ||
|
|
||
| Interface* i_; | ||
|
|
||
| void draw() { i_->draw(); } | ||
| }; | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| # `polymorphic (C++26)` | ||
|
|
||
| C++ 26 introduces `polymorphic<T, A>`, a value-type which owns an object of a class derived from `T`. | ||
|
|
||
| As it is an owning type, `polymorphic` takes a second template argument for the allocator. If no allocator is specified, the default allocator will be used. | ||
|
|
||
| `polymorphic` is a value-type: only const qualified methods of the owned object can be accessed from a const-access path; copies have their own distinct, non-sliced, copies of the owned object. | ||
|
|
||
| `polymorphic` provides `operator->` and `operator*` to access the owned object. | ||
|
|
||
| --- | ||
|
|
||
| # `protocol (Proposed addition)` | ||
|
|
||
| The class template `protocol<T, A>` owns an object whose interface structurally subtypes the interface of the first template argument `T`. | ||
|
|
||
| As it is an owning type, `protocol` takes a second template argument for the allocator. If no allocator is specified, the default allocator will be used. | ||
|
|
||
| `protocol` is a value-type: only const qualified methods of the owned object can be accessed from a const-access path; copies have their own distinct, non-sliced, copies of the owned object. | ||
|
|
||
| `protocol<T>` has all of the member functions of `T` and forwards them to the owned object. | ||
|
|
||
| --- | ||
|
|
||
| # `protocol_view (Proposed addition)` | ||
|
|
||
| The class template `protocol_view<T>` is a non-owning view on an object whose interface structurally subtypes the interface of the template argument `T`. | ||
|
|
||
| `protocol_view` can be cheaply copied and passed by value. | ||
|
|
||
| `protocol<T>` has all of the member functions of `T` and forwards them to the viewed object. | ||
|
|
||
| `protocol_view<const T>` can be used for const-only access to an object in a similar manner to `std::span`. | ||
|
|
||
| --- | ||
|
|
||
| # Function-like objects | ||
|
|
||
| C++ has a plethora of function-like type-erasing class templates. | ||
|
|
||
| - `std::function` (C++11) | ||
| - `std::function_ref` (C++26) | ||
| - `std::move_only_function` (C++23) | ||
| - `std::copyable_function` (C++26) | ||
|
|
||
| Each of these can be implemented using `protocol<T>` or `protocol_view<T>` by defining a suitable `T`. | ||
|
|
||
| --- | ||
|
|
||
| # Overload sets | ||
|
|
||
| `protocol` and `protocol_view` can be used to implement an overload set that can be used as a class member or passed as a function argument. | ||
|
|
||
| ```c++ | ||
| struct OverloadedFunction { | ||
| R1 operator()(Args1&&... args) const; | ||
| R2 operator()(Args2&&... args); | ||
| R3 operator()(Args3&&... args); | ||
| }; | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| # Indexed containers | ||
|
|
||
| `protocol` and `protocol_view` can be used for indexed container access. | ||
|
|
||
| ```c++ | ||
| struct IndexedContainer<T> { | ||
| const T& operator[](size_t i) const; | ||
| T& operator[](size_t i); | ||
| size_t size() const; | ||
| }; | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| # Implementation | ||
|
|
||
| A reference implementation, using an AST-based Python code generator to simulate | ||
| post-C++26 code injection, is available at <https://github.com/jbcoe/cc-protocol>. | ||
|
|
||
| There is an open PR to illustrate the work (currently) needed to add new types | ||
| <https://github.com/jbcoe/cc-protocol/pull/53> | ||
|
|
||
| --- | ||
|
|
||
| # Future work | ||
|
|
||
| Future extensions to C++ reflection should allow `protocol` and `protocol_view` to be implemented without the need for a custom build step. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.