Skip to content
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

Implement accessibility support for reflection #191

Merged
merged 7 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)...);
}
};
};
mingxwa marked this conversation as resolved.
Show resolved Hide resolved
}
```

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