Skip to content

Commit

Permalink
Implement accessibility support for reflection (#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
mingxwa authored Nov 11, 2024
1 parent aa8d329 commit d1b9567
Show file tree
Hide file tree
Showing 21 changed files with 434 additions and 207 deletions.
71 changes: 71 additions & 0 deletions docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Macro `PRO_DEF_FREE_AS_MEM_DISPATCH`

```cpp
#define PRO_DEF_FREE_AS_MEM_DISPATCH // see below
```
Macro `PRO_DEF_FREE_AS_MEM_DISPATCH` defines dispatch types for free function expressions with accessibility via a member function. It supports two syntaxes:
```cpp
// (1)
PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name);
// (2)
PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name);
```

`(1)` Equivalent to `PRO_DEF_FREE_AS_MEM_DISPATCH(dispatch_name, func_name, func_name);`

`(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:

```cpp
struct dispatch_name {
template <class T, class... Args>
decltype(auto) operator()(T&& self, Args&&... args)
noexcept(noexcept(func_name(std::forward<T>(self), std::forward<Args>(args)...)))
requires(requires { func_name(std::forward<T>(self), std::forward<Args>(args)...); }) {
return func_name(std::forward<T>(self), std::forward<Args>(args)...);
}

template <class F, class C, class... Os>
struct accessor {
accessor() = delete;
};
template <class F, class C, class... Os>
requires(sizeof...(Os) > 1u && (std::is_trivial_v<accessor<F, C, Os>> && ...))
struct accessor<F, C, Os...> : accessor<F, C, Os>... {
using accessor<F, C, Os>::accessibility_func_name ...;
};
template <class F, class C, class R, class... Args>
struct accessor<F, C, R(Args...) cv ref noex> {
R accessibility_func_name(Args... args) cv ref noex {
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
}
};
}
```
## Example
```cpp
#include <iostream>
#include <string>
#include "proxy.h"
PRO_DEF_FREE_AS_MEM_DISPATCH(FreeToString, std::to_string, ToString);
struct Stringable : pro::facade_builder
::add_convention<FreeToString, std::string()>
::build {};
int main() {
pro::proxy<Stringable> p = pro::make_proxy<Stringable>(123);
std::cout << p->ToString() << "\n"; // Prints: "123"
}
```

## See Also

- [macro `PRO_DEF_FREE_DISPATCH`](PRO_DEF_FREE_DISPATCH.md)
- [alias template `basic_facade_builder::add_convention`](basic_facade_builder/add_convention.md)
3 changes: 2 additions & 1 deletion docs/PRO_DEF_FREE_DISPATCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct dispatch_name {
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
}
};
};
}
```
## Example
Expand All @@ -65,4 +65,5 @@ int main() {
## See Also

- [macro `PRO_DEF_MEM_DISPATCH`](PRO_DEF_MEM_DISPATCH.md)
- [macro `PRO_DEF_FREE_AS_MEM_DISPATCH`](PRO_DEF_FREE_AS_MEM_DISPATCH.md)
- [alias template `basic_facade_builder::add_convention`](basic_facade_builder/add_convention.md)
2 changes: 1 addition & 1 deletion docs/PRO_DEF_MEM_DISPATCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct dispatch_name {
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
}
};
};
}
```
## Example
Expand Down
2 changes: 1 addition & 1 deletion docs/PRO_DEF_WEAK_DISPATCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct dispatch_name : existing_dispatch {
requires(requires { default_func_name(std::forward<Args>(args)...); }) {
return default_func_name(std::forward<Args>(args)...);
}
};
}
```
## Notes
Expand Down
13 changes: 13 additions & 0 deletions docs/ProBasicReflection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Named requirements: *ProBasicReflection*

A type `R` meets the *ProBasicReflection* requirements if the following expressions are well-formed and have the specified semantics.

| Expressions | Semantics |
| ---------------------------- | ------------------------------------------------------------ |
| `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`). |
| `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. |

## See Also

- [*ProBasicFacade* requirements](ProBasicFacade.md)
- [*ProReflection* requirements](ProReflection.md)
8 changes: 4 additions & 4 deletions docs/ProReflection.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Named requirements: *ProReflection*

A type `R` meets the *ProReflection* requirements of a type `P` if the following expressions are well-formed and have the specified semantics.
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`).

| Expressions | Semantics |
| -------------------------- | ------------------------------------------------------------ |
| `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`. |
| Expressions | Semantics |
| --------------------------------------------------- | ------------------------------------------------------------ |
| `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`. |

## See Also

Expand Down
2 changes: 1 addition & 1 deletion docs/basic_facade_builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ using facade_builder = basic_facade_builder<std::tuple<>, std::tuple<>,
| Name | Description |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| [`add_convention`<br />`add_indirect_convention`<br />`add_direct_convention`](basic_facade_builder/add_convention.md) | Adds a convention to the template parameters |
| [`add_reflection`](basic_facade_builder/add_reflection.md) | Adds a reflection to the template parameters |
| [`add_reflection`<br />`add_indirect_reflection`<br />`add_direct_reflection`](basic_facade_builder/add_reflection.md) | Adds a reflection to the template parameters |
| [`add_facade`](basic_facade_builder/add_facade.md) | Adds a facade to the template parameters |
| [`restrict_layout`](basic_facade_builder/restrict_layout.md) | Specifies maximum `max_size` and `max_align` of `C` in the template parameters |
| [`support_copy`](basic_facade_builder/support_copy.md) | Specifies minimum `copyability` of `C` in the template parameters |
Expand Down
4 changes: 2 additions & 2 deletions docs/basic_facade_builder/add_convention.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ The alias templates `add_convention`, `add_indirect_convention`, and `add_direct
- `IC::is_direct` is `false`.
- `typename IC::dispatch_type` is `D`.
- `typename IC::overload_types` is a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type of distinct types in `Os`.
- `typename IC::template accessor<F>` is `typename D::template accessor<F, IC, Os...>`.
- `typename IC::template accessor<F>` is `typename D::template accessor<F, IC, Os...>` if applicable.
- `add_direct_convention` merges an implementation-defined convention type `IC` into `Cs`, where:
- `IC::is_direct` is `true`.
- `typename IC::dispatch_type` is `D`.
- `typename IC::overload_types` is a [tuple-like](https://en.cppreference.com/w/cpp/utility/tuple/tuple-like) type of distinct types in `Os`.
- `typename IC::template accessor<F>` is `typename D::template accessor<F, IC, Os...>`.
- `typename IC::template accessor<F>` is `typename D::template accessor<F, IC, Os...>` if applicable.
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.
Expand Down
63 changes: 39 additions & 24 deletions docs/basic_facade_builder/add_reflection.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,32 @@

```cpp
template <class R>
using add_reflection = basic_facade_builder</* see below */>;
using add_reflection = add_indirect_reflection<R>;

template <class R>
using add_indirect_reflection = basic_facade_builder</* see below */>;

template <class R>
using add_direct_reflection = basic_facade_builder</* see below */>;
```

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`.
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,

- `add_reflection` is equivalent to `add_indirect_reflection`.
- `add_indirect_reflection` merges an implementation-defined reflection type `R2` into `Rs`, where:
- `R2::is_direct` is `false`.
- `typename R2::reflector_type` is `R`.
- `typename R2::template accessor<F>` is `typename R2::template accessor<F, R2>` if applicable.
- `add_direct_reflection` merges an implementation-defined reflection type `R2` into `Rs`, where:
- `R2::is_direct` is `true`.
- `typename R2::reflector_type` is `R`.
- `typename R2::template accessor<F>` is `typename R2::template accessor<F, R2>` if applicable.

When `Rs` already contains `R2`, the template parameters shall not change.

## Notes

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.
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.

## Example

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

#include "proxy.h"

class DebugReflection {
class RttiReflector {
public:
template <class P>
constexpr explicit DebugReflection(std::in_place_type_t<P>)
: pointer_type_(typeid(P)),
element_type_(typeid(typename std::pointer_traits<P>::element_type)) {}
template <class T>
constexpr explicit RttiReflector(std::in_place_type_t<T>) : type_(typeid(T)) {}

void PrintDebugInfo() const {
std::cout << "Pointer type: " << pointer_type_.name() << "\n";
std::cout << "Element type: " << element_type_.name() << "\n";
}
template <class F, class R>
struct accessor {
const char* GetTypeName() const noexcept {
const RttiReflector& self = pro::proxy_reflect<R>(pro::access_proxy<F>(*this));
return self.type_.name();
}
};

private:
const std::type_info& pointer_type_;
const std::type_info& element_type_;
const std::type_info& type_;
};

struct TestFacade : pro::facade_builder
::add_reflection<DebugReflection>
struct RttiAware : pro::facade_builder
::add_direct_reflection<RttiReflector>
::add_indirect_reflection<RttiReflector>
::build {};

int main() {
pro::proxy<TestFacade> p1 = std::make_shared<int>(123);
pro::proxy_reflect<DebugReflection>(p1).PrintDebugInfo(); // Prints: "Pointer type: St10shared_ptrIiE"
// "Element type: i" (assuming GCC)

double v = 3.14;
pro::proxy<TestFacade> p2 = &v;
pro::proxy_reflect<DebugReflection>(p2).PrintDebugInfo(); // Prints: "Pointer type: Pd"
// "Element type: d" (assuming GCC)
int a = 123;
pro::proxy<RttiAware> p = &a;
std::cout << p.GetTypeName() << "\n"; // Prints: "Pi" (assuming GCC)
std::cout << p->GetTypeName() << "\n"; // Prints: "i" (assuming GCC)
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/proxy.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class proxy;

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.

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.
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.

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.

Expand Down
38 changes: 24 additions & 14 deletions docs/proxy_reflect.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

```cpp
template <class R, class F>
const R& proxy_reflect(const proxy<F>& p) noexcept;
/* see below */ proxy_reflect(const proxy<F>& p) noexcept;
```
Retrieves a value of type `R` constructed from [`std::in_place_type<P>`](https://en.cppreference.com/w/cpp/utility/in_place), where `P` is the type of the contained value of `p`. `R` is required to be defined in `typename F::reflection_types`. The behavior is undefined if `p` does not contain a value.
Let `P` be the type of the contained value of `p`. Retrieves a value of type `const typename R::reflector_type&` constructed from [`std::in_place_type<T>`](https://en.cppreference.com/w/cpp/utility/in_place), where `T` is `P` when `R::is_direct` is `true`, or otherwise `T` is `typename std::pointer_traits<P>::element_type` when `R::is_direct` is `false`. `R` is required to be defined in `typename F::reflection_types`. The behavior is undefined if `p` does not contain a value.
The reference obtained from `proxy_reflect()` may be invalidated if `p` is subsequently modified.
Expand All @@ -22,24 +22,34 @@ This function is useful when only metadata deduced from a type is needed. While
#include "proxy.h"
struct TraitsRefl {
template <class P>
constexpr explicit TraitsRefl(std::in_place_type_t<P>)
: Copyable(std::is_copy_constructible_v<P>) {}
const bool Copyable;
class CopyabilityReflector {
public:
template <class T>
constexpr explicit CopyabilityReflector(std::in_place_type_t<T>)
: copyable_(std::is_copy_constructible_v<T>) {}
template <class F, class R>
struct accessor {
bool IsCopyable() const noexcept {
const CopyabilityReflector& self = pro::proxy_reflect<R>(pro::access_proxy<F>(*this));
return self.copyable_;
}
};
private:
bool copyable_;
};
struct TestFacade : pro::facade_builder
::add_reflection<TraitsRefl>
struct CopyabilityAware : pro::facade_builder
::add_direct_reflection<CopyabilityReflector>
::build {};
int main() {
pro::proxy<TestFacade> p1 = std::make_unique<int>();
std::cout << std::boolalpha << pro::proxy_reflect<TraitsRefl>(p1).Copyable << "\n"; // Prints: "false"
pro::proxy<CopyabilityAware> p1 = std::make_unique<int>();
std::cout << std::boolalpha << p1.IsCopyable() << "\n"; // Prints: "false"
pro::proxy<TestFacade> p2 = std::make_shared<int>();
std::cout << pro::proxy_reflect<TraitsRefl>(p2).Copyable << "\n"; // Prints: "true"
pro::proxy<CopyabilityAware> p2 = std::make_shared<int>();
std::cout << p2.IsCopyable() << "\n"; // Prints: "true"
}
```

Expand Down
Loading

0 comments on commit d1b9567

Please sign in to comment.