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

Fix ADL for free function accessors #220

Merged
merged 2 commits into from
Dec 16, 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
4 changes: 2 additions & 2 deletions docs/PRO_DEF_FREE_AS_MEM_DISPATCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ 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:
`(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`. Effectively equivalent to:

```cpp
struct dispatch_name {
Expand All @@ -39,7 +39,7 @@ struct dispatch_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)...);
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(std::forward<accessor cv ref>(*this)), std::forward<Args>(args)...);
}
};
}
Expand Down
6 changes: 3 additions & 3 deletions docs/PRO_DEF_FREE_DISPATCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ PRO_DEF_FREE_DISPATCH(dispatch_name, func_name, accessibility_func_name);

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

`(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility. `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 functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name` and can be found by [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl) when `accessor` is an associated class of the arguments. Let `SELF` be `std::forward<accessor cv ref>(self)`, effectively equivalent to:
`(2)` Defines a class named `dispatch_name` of free function call expressions of `func_name` with accessibility. `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`. Let `accessor_arg` be `std::conditional_t<C::is_direct, proxy<F>, proxy_indirect_accessor<F>>`. The functions provided by `typename dispatch_name::template accessor<F, C, Os...>` are named `accessibility_func_name` and can be found by [argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl) when `accessor_arg` is an associated class of the arguments. Effectively equivalent to:

```cpp
struct dispatch_name {
Expand All @@ -35,8 +35,8 @@ struct dispatch_name {
struct accessor<F, C, Os...> : accessor<F, C, Os>... {};
template <class F, class C, class R, class... Args>
struct accessor<F, C, R(Args...) cv ref noex> {
friend R accessibility_func_name(accessor cv ref self, Args... args) noex {
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(SELF), std::forward<Args>(args)...);
friend R accessibility_func_name(accessor_arg cv ref self, Args... args) noex {
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(std::forward<accessor_arg cv ref>(self)), std::forward<Args>(args)...);
}
};
}
Expand Down
4 changes: 2 additions & 2 deletions docs/PRO_DEF_MEM_DISPATCH.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ PRO_DEF_MEM_DISPATCH(dispatch_name, func_name, accessibility_func_name);

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

`(2)` Defines a class named `dispatch_name` of member function call expressions of `func_name` with accessibility. `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:
`(2)` Defines a class named `dispatch_name` of member function call expressions of `func_name` with accessibility. `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`. Effectively equivalent to:

```cpp
struct dispatch_name {
Expand All @@ -39,7 +39,7 @@ struct dispatch_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)...);
return pro::proxy_invoke<C, R(Args...) cv ref noex>(pro::access_proxy<F>(std::forward<accessor cv ref>(*this)), std::forward<Args>(args)...);
}
};
}
Expand Down
4 changes: 1 addition & 3 deletions docs/explicit_conversion_dispatch/accessor.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ struct accessor<F, C, T() cv ref noex> {
};
```

Let `SELF` be `std::forward<accessor cv ref>(*this)`.

`(1)` The default implementation of `accessor` is not constructible.

`(2)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are default-constructible, inherits all `accessor<F, C, Os>...` types and `using` their `operator return-type-of<Os>`. `return-type-of<O>` denotes the *return type* of the overload type `O`.

`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an explicit `operator T()` with the same *cv ref noex* specifiers. `accessor::operator T()` is equivalent to `return proxy_invoke<C, T() cv ref noex>(access_proxy<F>(SELF))`.
`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an explicit `operator T()` with the same *cv ref noex* specifiers. `accessor::operator T()` is equivalent to `return proxy_invoke<C, T() cv ref noex>(access_proxy<F>(std::forward<accessor cv ref>(*this)))`.
4 changes: 1 addition & 3 deletions docs/implicit_conversion_dispatch/accessor.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ struct accessor<F, C, T() cv ref noex> {
};
```

Let `SELF` be `std::forward<accessor cv ref>(*this)`.

`(1)` The default implementation of `accessor` is not constructible.

`(2)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are default-constructible, inherits all `accessor<F, C, Os>...` types and `using` their `operator return-type-of<Os>`. `return-type-of<O>` denotes the *return type* of the overload type `O`.

`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an implicit `operator T()` with the same *cv ref noex* specifiers. `accessor::operator T()` is equivalent to `return proxy_invoke<C, T() cv ref noex>(access_proxy<F>(SELF))`.
`(3)` When `sizeof...(Os)` is `1` and the only type `O` in `Os` is `T() cv ref noex`, provides an implicit `operator T()` with the same *cv ref noex* specifiers. `accessor::operator T()` is equivalent to `return proxy_invoke<C, T() cv ref noex>(access_proxy<F>(std::forward<accessor cv ref>(*this)))`.
18 changes: 9 additions & 9 deletions docs/operator_dispatch/accessor.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct accessor<F, C, Os...> : accessor<F, C, Os>... {

`(2)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are default-constructible types, inherits all `accessor<F, C, Os>...` types and `using` their `operator sop`.

When `Rhs` is `false`, the other specializations are defined as follows, where `sizeof...(Os)` is `1` and the only type `O` qualified with `cv ref noex` (let `SELF` be `std::forward<accessor cv ref>(*this)`):
When `Rhs` is `false`, the other specializations are defined as follows, where `sizeof...(Os)` is `1` and the only type `O` qualified with `cv ref noex` (let `ACCESS_PROXY_EXPR` be `access_proxy<F>(std::forward<accessor cv ref>(*this))`):

### Regular SOPs

Expand All @@ -39,7 +39,7 @@ struct accessor<F, C, R(Args...) cv ref noex> {
}
```

`(3)` Provides an `operator sop(Args...)` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop(Args...)` is equivalent to `return proxy_invoke<C, R(Args...) cv ref noex>(access_proxy<F>(SELF), std::forward<Args>(args)...)`.
`(3)` Provides an `operator sop(Args...)` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop(Args...)` is equivalent to `return proxy_invoke<C, R(Args...) cv ref noex>(ACCESS_PROXY_EXPR, std::forward<Args>(args)...)`.

### `!` and `~`

Expand All @@ -53,7 +53,7 @@ struct accessor<F, C, R() cv ref noex> {
}
```

`(4)` Provides an `operator sop()` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop()` is equivalent to `return proxy_invoke<C, R() cv ref noex>(access_proxy<F>(SELF))`.
`(4)` Provides an `operator sop()` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop()` is equivalent to `return proxy_invoke<C, R() cv ref noex>(ACCESS_PROXY_EXPR)`.

### Assignment SOPs

Expand All @@ -67,7 +67,7 @@ struct accessor<F, C, R(Arg) cv ref noex> {
}
```

`(4)` Provides an `operator sop(Arg)` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop(Arg)` calls `proxy_invoke<C, R(Arg) cv ref noex>(access_proxy<F>(SELF), std::forward<Arg>(arg))` and returns `access_proxy<F>(SELF)` when `C::is_direct` is `true`, or otherwise, returns `*access_proxy<F>(SELF)` when `C::is_direct` is `false`.
`(4)` Provides an `operator sop(Arg)` with the same *cv ref noex* specifiers as of the overload type. `accessor::operator sop(Arg)` calls `proxy_invoke<C, R(Arg) cv ref noex>(ACCESS_PROXY_EXPR, std::forward<Arg>(arg))` and returns `ACCESS_PROXY_EXPR` when `C::is_direct` is `true`, or otherwise, returns `*ACCESS_PROXY_EXPR` when `C::is_direct` is `false`.

## Right-Hand-Side Operand Specializations

Expand All @@ -80,7 +80,7 @@ struct accessor<F, C, Os...> : accessor<F, C, Os>... {};

`(6)` When `sizeof...(Os)` is greater than `1`, and `accessor<F, C, Os>...` are default-constructible types, inherits all `accessor<F, C, Os>...` types.

When `Rhs` is `true`, the other specializations are defined as follows, where `sizeof...(Os)` is `1` and the only type `O` qualified with `cv ref noex` (let `SELF` be `std::forward<accessor cv ref>(self)`):
When `Rhs` is `true`, the other specializations are defined as follows, where `sizeof...(Os)` is `1` and the only type `O` qualified with `cv ref noex` (let `accessor_arg` be `std::conditional_t<C::is_direct, proxy<F>, proxy_indirect_accessor<F>>`, `ACCESS_PROXY_EXPR` be `access_proxy<F>(std::forward<accessor_arg cv ref>(self))`):

### Regular SOPs

Expand All @@ -90,11 +90,11 @@ When `Sign` is one of `"+"`, `"-"`, `"*"`, `"/"`, `"%"`, `"=="`, `"!="`, `">"`,
// (7)
template <class F, class C, class R, class Arg>
struct accessor<F, C, R(Arg) cv ref noex> {
friend R operator sop (Arg arg, accessor cv ref self) noex;
friend R operator sop (Arg arg, accessor_arg cv ref self) noex;
}
```

`(7)` Provides a `friend operator sop(Arg arg, accessor cv ref)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor cv ref)` is equivalent to `return proxy_invoke<C, R(Arg) cv ref noex>(access_proxy<F>(SELF), std::forward<Arg>(arg))`.
`(7)` Provides a `friend operator sop(Arg arg, accessor_arg cv ref self)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor_arg cv ref self)` is equivalent to `return proxy_invoke<C, R(Arg) cv ref noex>(ACCESS_PROXY_EXPR, std::forward<Arg>(arg))`.

### Assignment SOPs

Expand All @@ -104,8 +104,8 @@ When `Sign` is one of `"+="`, `"-="`, `"*="`, `"/="`, `"&="`, `"|="`, `"^="`, `"
// (8)
template <class F, class C, class R, class Arg>
struct accessor<F, C, R(Arg) cv ref noex> {
friend /* see below */ operator sop (Arg arg, accessor cv ref self) noex;
friend /* see below */ operator sop (Arg arg, accessor_arg cv ref self) noex;
}
```

`(8)` Provides a `friend operator sop(Arg arg, accessor cv ref)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor cv ref)` calls `proxy_invoke<C, R(Arg) cv ref noex>(access_proxy<F>(SELF), std::forward<Arg>(arg))` and returns `access_proxy<F>(SELF)` when `C::is_direct` is `true`, or otherwise, returns `*access_proxy<F>(SELF)` when `C::is_direct` is `false`.
`(8)` Provides a `friend operator sop(Arg arg, accessor_arg cv ref self)` with the same *noex* specifiers as of the overload type. `accessor::operator sop(Arg arg, accessor_arg cv ref self)` calls `proxy_invoke<C, R(Arg) cv ref noex>(ACCESS_PROXY_EXPR, std::forward<Arg>(arg))` and returns `ACCESS_PROXY_EXPR` when `C::is_direct` is `true`, or otherwise, returns `*ACCESS_PROXY_EXPR` when `C::is_direct` is `false`.
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`, 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.
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 [`proxy_indirect_accessor`](proxy_indirect_accessor.md), 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
20 changes: 10 additions & 10 deletions docs/proxy/indirection.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
# `proxy::operator->`, `proxy::operator*`

The definitions of `proxy::operator->` and `proxy::operator*` make use of the following exposition-only constant and type alias:
The definitions of `proxy::operator->` and `proxy::operator*` make use of the following exposition-only constant:

```cpp
static constexpr bool has-indirection = see below; // exposition only
using indirect-accessor = see below; // exposition only
```

As per [`facade<F>`](../facade.md), `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`. Let `Cs2` be the types in `Cs` where each type `C` meets the [*ProAccessible* requirements](../ProAccessible.md) of `F` and `C::is_direct` is `false`. *has-indirection* is `true` if `Cs2` contains at least one type; otherwise, it is `false`. *indirect-accessor* is a non-copyable type that inherits from every type in `Cs2`.

```cpp
// (1)
indirect-accessor* operator->() noexcept requires(has-indirection);
const indirect-accessor* operator->() const noexcept requires(has-indirection);
proxy_indirect_accessor<F>* operator->() noexcept requires(has-indirection);
const proxy_indirect_accessor<F>* operator->() const noexcept requires(has-indirection);

// (2)
indirect-accessor& operator*() & noexcept requires(has-indirection);
const indirect-accessor& operator*() const& noexcept requires(has-indirection);
indirect-accessor&& operator*() && noexcept requires(has-indirection);
const indirect-accessor&& operator*() const&& noexcept requires(has-indirection);
proxy_indirect_accessor<F>& operator*() & noexcept requires(has-indirection);
const proxy_indirect_accessor<F>& operator*() const& noexcept requires(has-indirection);
proxy_indirect_accessor<F>&& operator*() && noexcept requires(has-indirection);
const proxy_indirect_accessor<F>&& operator*() const&& noexcept requires(has-indirection);
```

These operators access the accessors of the indirect conventions, as if dereferencing the contained value.

- `(1)` Returns a pointer to the *indirect-accessor*.
- `(2)` Returns a reference to the *indirect-accessor*.
- `(1)` Returns a pointer to the `proxy_indirect_accessor<F>`.
- `(2)` Returns a reference to the `proxy_indirect_accessor<F>`.

The behavior is undefined if `*this` does not contain a value.

Expand Down Expand Up @@ -68,3 +67,4 @@ int main() {

- [function template `access_proxy`](../access_proxy.md)
- [function template `proxy_invoke`](../proxy_invoke.md)
- [class template `proxy_indirect_accessor`](../proxy_indirect_accessor.md)
Loading