Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.

Commit d1b9567

Browse files
authored
Implement accessibility support for reflection (#191)
1 parent aa8d329 commit d1b9567

21 files changed

Lines changed: 434 additions & 207 deletions
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Macro `PRO_DEF_FREE_AS_MEM_DISPATCH`
2+
3+
```cpp
4+
#define PRO_DEF_FREE_AS_MEM_DISPATCH // see below
5+
```
6+
7+
Macro `PRO_DEF_FREE_AS_MEM_DISPATCH` defines dispatch types for free function expressions with accessibility via a member function. It supports two syntaxes:
8+
9+
```cpp
10+
// (1)
11+
PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name);
12+
13+
// (2)
14+
PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name);
15+
```
16+
17+
`(1)` Equivalent to `PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, func_name);`
18+
19+
`(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility via a member function. `dispatch_name` meets the [*ProAccessible*](ProAccessible.md) requirements of types `F`, `C`, and `Os...`, where `F` models concept [`facade`](facade.md), `C` is a tuple element type defined in `typename F::convention_types`, and each type `O` (possibly qualified with *cv ref noex*) in `Os...` is a tuple element type defined in `typename C::overload_types`. The member functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name`. Let `SELF` be `std::forward<accessor cv ref>(*this)`, effectively equivalent to:
20+
21+
```cpp
22+
struct dispatch_name {
23+
template <class T, class... Args>
24+
decltype(auto) operator()(T&& self, Args&&... args)
25+
noexcept(noexcept(func_name(std::forward<T>(self), std::forward<Args>(args)...)))
26+
requires(requires { func_name(std::forward<T>(self), std::forward<Args>(args)...); }) {
27+
return func_name(std::forward<T>(self), std::forward<Args>(args)...);
28+
}
29+
30+
template <class F, class C, class... Os>
31+
struct accessor {
32+
accessor() = delete;
33+
};
34+
template <class F, class C, class... Os>
35+
requires(sizeof...(Os) > 1u && (std::is_trivial_v<accessor<F, C, Os>> && ...))
36+
struct accessor<F, C, Os...> : accessor<F, C, Os>... {
37+
using accessor<F, C, Os>::accessibility_func_name ...;
38+
};
39+
template <class F, class C, class R, class... Args>
40+
struct accessor<F, C, R(Args...) cv ref noex> {
41+
R accessibility_func_name(Args... args) cv ref noex {
42+
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
43+
}
44+
};
45+
}
46+
```
47+
48+
## Example
49+
50+
```cpp
51+
#include <iostream>
52+
#include <string>
53+
54+
#include "proxy.h"
55+
56+
PRO_DEF_FREE_AS_MEM_DISPATCH(FreeToString, std::to_string, ToString);
57+
58+
struct Stringable : pro::facade_builder
59+
::add_convention<FreeToString, std::string()>
60+
::build {};
61+
62+
int main() {
63+
pro::proxy<Stringable> p = pro::make_proxy<Stringable>(123);
64+
std::cout << p->ToString() << "\n"; // Prints: "123"
65+
}
66+
```
67+
68+
## See Also
69+
70+
- [macro `PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md)
71+
- [alias template `basic_facade_builder::add_convention`](basic_facade_builder/add_convention.md)

docs/PRO_DEF_FREE_DISPATCH.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct dispatch_name {
3939
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
4040
}
4141
};
42-
};
42+
}
4343
```
4444
4545
## Example
@@ -65,4 +65,5 @@ int main() {
6565
## See Also
6666

6767
- [macro `PRO_DEF_MEM_DISPATCH`](PRO_DEF_MEM_DISPATCH.md)
68+
- [macro `PRO_DEF_FREE_AS_MEM_DISPATCH`](PRO_DEF_FREE_AS_MEM_DISPATCH.md)
6869
- [alias template `basic_facade_builder::add_convention`](basic_facade_builder/add_convention.md)

docs/PRO_DEF_MEM_DISPATCH.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ struct dispatch_name {
4242
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
4343
}
4444
};
45-
};
45+
}
4646
```
4747
4848
## Example

docs/PRO_DEF_WEAK_DISPATCH.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct dispatch_name : existing_dispatch {
2121
requires(requires { default_func_name(std::forward<Args>(args)...); }) {
2222
return default_func_name(std::forward<Args>(args)...);
2323
}
24-
};
24+
}
2525
```
2626
2727
## Notes

docs/ProBasicReflection.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Named requirements: *ProBasicReflection*
2+
3+
A type `R` meets the *ProBasicReflection* requirements if the following expressions are well-formed and have the specified semantics.
4+
5+
| Expressions | Semantics |
6+
| ---------------------------- | ------------------------------------------------------------ |
7+
| `R::is_direct` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) of type `bool`, specifying whether the reflection applies to a pointer type itself (`true`), or the element type of a pointer type (`false`). |
8+
| `typename R::reflector_type` | A [trivial type](https://en.cppreference.com/w/cpp/named_req/TrivialType) that defines the data structure reflected from the type. |
9+
10+
## See Also
11+
12+
- [*ProBasicFacade* requirements](ProBasicFacade.md)
13+
- [*ProReflection* requirements](ProReflection.md)

docs/ProReflection.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Named requirements: *ProReflection*
22

3-
A type `R` meets the *ProReflection* requirements of a type `P` if the following expressions are well-formed and have the specified semantics.
3+
A type `R` meets the *ProReflection* requirements of a type `P` if `R` meets the [*ProBasicReflection* requirements](ProBasicReflection.md), and the following expressions are well-formed and have the specified semantics (let `T` be `P` when `R::is_direct` is `true`, or otherwise `typename std::pointer_traits<P>::element_type`).
44

5-
| Expressions | Semantics |
6-
| -------------------------- | ------------------------------------------------------------ |
7-
| `R{std::in_place_type<P>}` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) that constructs a value of type `R`, reflecting implementation-defined metadata of type `P`. |
5+
| Expressions | Semantics |
6+
| --------------------------------------------------- | ------------------------------------------------------------ |
7+
| `typename R::reflector_type{std::in_place_type<T>}` | A [core constant expression](https://en.cppreference.com/w/cpp/language/constant_expression) that constructs a value of type `typename R::reflector_type`, reflecting implementation-defined metadata of type `T`. |
88

99
## See Also
1010

docs/basic_facade_builder.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ using facade_builder = basic_facade_builder<std::tuple<>, std::tuple<>,
3636
| Name | Description |
3737
| ------------------------------------------------------------ | ------------------------------------------------------------ |
3838
| [`add_convention`<br />`add_indirect_convention`<br />`add_direct_convention`](basic_facade_builder/add_convention.md) | Adds a convention to the template parameters |
39-
| [`add_reflection`](basic_facade_builder/add_reflection.md) | Adds a reflection to the template parameters |
39+
| [`add_reflection`<br />`add_indirect_reflection`<br />`add_direct_reflection`](basic_facade_builder/add_reflection.md) | Adds a reflection to the template parameters |
4040
| [`add_facade`](basic_facade_builder/add_facade.md) | Adds a facade to the template parameters |
4141
| [`restrict_layout`](basic_facade_builder/restrict_layout.md) | Specifies maximum `max_size` and `max_align` of `C` in the template parameters |
4242
| [`support_copy`](basic_facade_builder/support_copy.md) | Specifies minimum `copyability` of `C` in the template parameters |

docs/basic_facade_builder/add_convention.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ The alias templates `add_convention`, `add_indirect_convention`, and `add_direct
1818
- `IC::is_direct` is `false`.
1919
- `typename IC::dispatch_type` is `D`.
2020
- `typename IC::overload_types` is a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type of distinct types in `Os`.
21-
- `typename IC::template accessor<F>` is `typename D::template accessor<F, IC, Os...>`.
21+
- `typename IC::template accessor<F>` is `typename D::template accessor<F, IC, Os...>` if applicable.
2222
- `add_direct_convention` merges an implementation-defined convention type `IC` into `Cs`, where:
2323
- `IC::is_direct` is `true`.
2424
- `typename IC::dispatch_type` is `D`.
2525
- `typename IC::overload_types` is a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type of distinct types in `Os`.
26-
- `typename IC::template accessor<F>` is `typename D::template accessor<F, IC, Os...>`.
26+
- `typename IC::template accessor<F>` is `typename D::template accessor<F, IC, Os...>` if applicable.
2727
2828
When `Cs` already contains a convention type `IC2` where `IC2::is_direct == IC::is_direct && std::is_same_v<typename IC2::dispatch_type, typename IC::dispatch_type>` is `true`, `Os` merges with `typename IC2::overload_types` and removes duplicates, and `std::tuple_size_v<Cs>` shall not change.
2929

docs/basic_facade_builder/add_reflection.md

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,32 @@
22

33
```cpp
44
template <class R>
5-
using add_reflection = basic_facade_builder</* see below */>;
5+
using add_reflection = add_indirect_reflection<R>;
6+
7+
template <class R>
8+
using add_indirect_reflection = basic_facade_builder</* see below */>;
9+
10+
template <class R>
11+
using add_direct_reflection = basic_facade_builder</* see below */>;
612
```
713

8-
The alias template `add_reflection` of `basic_facade_builder<Cs, Rs, C>` incorporates reflection types (see [named requirements: *ProReflection*](../ProReflection.md)) into the template parameters. It merges `R` into `Rs` if `Rs` does not already contain `R`.
14+
The alias templates `add_reflection`, `add_indirect_reflection` and `add_direct_reflection` of `basic_facade_builder<Cs, Rs, C>` add reflection types to the template parameters. Specifically,
15+
16+
- `add_reflection` is equivalent to `add_indirect_reflection`.
17+
- `add_indirect_reflection` merges an implementation-defined reflection type `R2` into `Rs`, where:
18+
- `R2::is_direct` is `false`.
19+
- `typename R2::reflector_type` is `R`.
20+
- `typename R2::template accessor<F>` is `typename R2::template accessor<F, R2>` if applicable.
21+
- `add_direct_reflection` merges an implementation-defined reflection type `R2` into `Rs`, where:
22+
- `R2::is_direct` is `true`.
23+
- `typename R2::reflector_type` is `R`.
24+
- `typename R2::template accessor<F>` is `typename R2::template accessor<F, R2>` if applicable.
25+
26+
When `Rs` already contains `R2`, the template parameters shall not change.
927

1028
## Notes
1129

12-
Adding duplicate reflection types is well-defined, whether done directly via `add_reflection` or indirectly via [`add_facade`](add_facade.md). This process does not affect the behavior of [`build`](build.md) at either compile-time or runtime.
30+
Adding duplicate reflection types is well-defined, whether done directly via `add_reflection`, `add_indirect_reflection`, `add_direct_reflection`, or indirectly via [`add_facade`](add_facade.md). This process does not affect the behavior of [`build`](build.md) at either compile-time or runtime.
1331

1432
## Example
1533

@@ -19,36 +37,33 @@ Adding duplicate reflection types is well-defined, whether done directly via `ad
1937

2038
#include "proxy.h"
2139

22-
class DebugReflection {
40+
class RttiReflector {
2341
public:
24-
template <class P>
25-
constexpr explicit DebugReflection(std::in_place_type_t<P>)
26-
: pointer_type_(typeid(P)),
27-
element_type_(typeid(typename std::pointer_traits<P>::element_type)) {}
42+
template <class T>
43+
constexpr explicit RttiReflector(std::in_place_type_t<T>) : type_(typeid(T)) {}
2844

29-
void PrintDebugInfo() const {
30-
std::cout << "Pointer type: " << pointer_type_.name() << "\n";
31-
std::cout << "Element type: " << element_type_.name() << "\n";
32-
}
45+
template <class F, class R>
46+
struct accessor {
47+
const char* GetTypeName() const noexcept {
48+
const RttiReflector& self = pro::proxy_reflect<R>(pro::access_proxy<F>(*this));
49+
return self.type_.name();
50+
}
51+
};
3352

3453
private:
35-
const std::type_info& pointer_type_;
36-
const std::type_info& element_type_;
54+
const std::type_info& type_;
3755
};
3856

39-
struct TestFacade : pro::facade_builder
40-
::add_reflection<DebugReflection>
57+
struct RttiAware : pro::facade_builder
58+
::add_direct_reflection<RttiReflector>
59+
::add_indirect_reflection<RttiReflector>
4160
::build {};
4261

4362
int main() {
44-
pro::proxy<TestFacade> p1 = std::make_shared<int>(123);
45-
pro::proxy_reflect<DebugReflection>(p1).PrintDebugInfo(); // Prints: "Pointer type: St10shared_ptrIiE"
46-
// "Element type: i" (assuming GCC)
47-
48-
double v = 3.14;
49-
pro::proxy<TestFacade> p2 = &v;
50-
pro::proxy_reflect<DebugReflection>(p2).PrintDebugInfo(); // Prints: "Pointer type: Pd"
51-
// "Element type: d" (assuming GCC)
63+
int a = 123;
64+
pro::proxy<RttiAware> p = &a;
65+
std::cout << p.GetTypeName() << "\n"; // Prints: "Pi" (assuming GCC)
66+
std::cout << p->GetTypeName() << "\n"; // Prints: "i" (assuming GCC)
5267
}
5368
```
5469

docs/proxy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class proxy;
77

88
Class template `proxy` is a general-purpose polymorphic wrapper for C++ objects. Unlike other polymorphic wrappers in the C++ standard (e.g., [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function), [`std::move_only_function`](https://en.cppreference.com/w/cpp/utility/functional/move_only_function), [`std::any`](https://en.cppreference.com/w/cpp/utility/any), etc.), `proxy` is based on pointer semantics. It supports flexible lifetime management without runtime [garbage collection (GC)](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)) at runtime, and offers best-in-class code generation quality, extendibility and accessibility.
99

10-
To instantiate `proxy<F>`, `F` shall model [concept `facade`](facade.md). As per `facade<F>`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`. For each type `C` in `Cs`, if `C` meets the [*ProAccessible* requirements](ProAccessible.md) of `F`, `typename C::template accessor<F>` is inherited by `proxy<F>` when `C::is_direct` is `true`. Otherwise, it is inherited by the return type of [`operator*`](proxy/indirection.md) when `C::is_direct` is `false`. Implementation of accessors can call [`access_proxy`](access_proxy.md) to access the `proxy` object. It is recommended to use [`facade_builder`](basic_facade_builder.md) to define a facade type.
10+
To instantiate `proxy<F>`, `F` shall model [concept `facade`](facade.md). As per `facade<F>`, `typename F::convention_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Cs`, and `typename F::reflection_types` shall be a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type containing any number of distinct types `Rs`. For each type `T` in `Cs` or `Rs`, if `T` meets the [*ProAccessible* requirements](ProAccessible.md) of `F`, `typename T::template accessor<F>` is inherited by `proxy<F>` when `T::is_direct` is `true`. Otherwise, it is inherited by the return type of [`operator*`](proxy/indirection.md) when `T::is_direct` is `false`. Implementation of accessors can call [`access_proxy`](access_proxy.md) to access the `proxy` object. It is recommended to use [`facade_builder`](basic_facade_builder.md) to define a facade type.
1111

1212
Any instance of `proxy<F>` at any given point in time either *contains a value* or *does not contain a value*. If a `proxy<F>` *contains a value*, the type of the value shall be a pointer type `P` where [`proxiable<P, F>`](proxiable.md) is `true`, and the value is guaranteed to be allocated as part of the `proxy` object footprint, i.e. no dynamic memory allocation occurs. However, `P` may allocate during its construction, depending on its implementation.
1313

0 commit comments

Comments
 (0)