"ScL Feature"/
ScL Feature 0.0.1
C++20 composable proxy wrapper with pluggable executor strategy
Loading...
Searching...
No Matches
ScL Feature Reflection

Compile-time reflection for wrapper types. More...

Files

file  src/scl/feature/reflection/access.h
 Internal accessor macros for executor and value access in reflected methods.
file  src/scl/feature/reflection/method.h
 Compile-time method reflection macros for wrapper types.
file  src/scl/feature/reflection/operator.h
 Compile-time operator reflection macros for wrapper types.
file  src/scl/feature/reflection/property.h
 Compile-time property reflection macro for wrapper types.
file  src/scl/feature/reflection/reflect.h
 Primary template for the reflection mixin chain.
file  src/scl/feature/reflection.h
 ScL Feature compile-time method reflection utilities.
file  src/scl/feature/reflection/type.h
 Macro for declaring the wrapper type used by reflection macros.

Classes

class  scl::feature::reflect< Wrapper, Executor, Type >
 Primary (terminal) template for the reflection mixin chain. More...
class  scl::feature::reflect_operators< Wrapper, Executor >
 CRTP mixin that reflects all standard C++ operators through the executor. More...

Macros

#define SCL_REFLECT_BINARY_OPERATOR(op, name)
 Generates proxy binary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.
#define SCL_REFLECT_FRIEND_BINARY_OPERATOR(op, name)
 Generates hidden-friend proxy binary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.
#define SCL_REFLECT_FRIEND_POSTFIX_UNARY_OPERATOR(op, name)
 Generates hidden-friend proxy postfix-unary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.
#define SCL_REFLECT_FRIEND_PREFIX_UNARY_OPERATOR(op, name)
 Generates hidden-friend proxy prefix-unary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.
#define SCL_REFLECT_MEMBER_BINARY_OPERATOR(op, name)
 Generates member-only proxy binary operators (no hidden-friend variants).
#define SCL_REFLECT_MEMBER_POSTFIX_UNARY_OPERATOR(op, name)
 Generates member-only proxy postfix-unary operators (no hidden-friend variants).
#define SCL_REFLECT_MEMBER_PREFIX_UNARY_OPERATOR(op, name)
 Generates member-only proxy prefix-unary operators (no hidden-friend variants).
#define SCL_REFLECT_METHOD(name)
 Generates proxy methods that reflect method from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.
#define SCL_REFLECT_OPERATOR_WITH_ARGUMENTS(op, name)
 Generates proxy call/subscript operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.
#define SCL_REFLECT_POSTFIX_UNARY_OPERATOR(op, name)
 Generates proxy postfix-unary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.
#define SCL_REFLECT_PREFIX_UNARY_OPERATOR(op, name)
 Generates proxy prefix-unary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.
#define SCL_REFLECT_TYPE(type, executor)
 Declares the wrapper and executor types for use by SCL_REFLECT_METHOD.

Detailed Description

Compile-time reflection for wrapper types.

Provides the macros and types needed to generate proxy members that transparently forward calls from a wrapper to the held object through an executor, preserving all cv-ref qualifiers.

Symbol Purpose
SCL_REFLECT_TYPE(Wrapper, Executor) Declares the type aliases required by SCL_REFLECT_METHOD and SCL_REFLECT_PROPERTY. Must appear first inside the wrapper class body.
SCL_REFLECT_METHOD(method) Generates 24 proxy overloads (3 × 8 cv-ref) that dispatch through the executor.
SCL_REFLECT_PROPERTY(prop) Generates a wrapper data member that proxies a single field through the executor, with optional executor override via property_<name>.
scl::feature::reflect CRTP mixin base class; specialisations inject reflected members.

Macro Definition Documentation

◆ SCL_REFLECT_BINARY_OPERATOR

#define SCL_REFLECT_BINARY_OPERATOR ( op,
name )

Generates proxy binary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.

For each of the 8 cv-ref qualifiers (&, &&, const&, const&&, volatile&, volatile&&, const volatile&, const volatile&&) three overloads are generated:

  • one executor-override overload — active when Executor::operator_##name exists
  • one execute-path overload — active when no executor override is found
  • one with explicit template arguments
Executor override
If the executor provides a static operator_##name(Executor cv_ref, args...) member whose first parameter matches the wrapper's cv-ref qualification exactly (detected via a function-pointer cast), the reflected operator calls that member directly, bypassing Executor::execute.
noexcept propagation
When the executor-override path is selected, noexcept is propagated from the override. When the execute path is taken, noexcept follows Executor::execute.
Reverse-operand symmetry (hidden friends)
In addition to the 24 member overloads (wrapper as the left operand), 8 constrained hidden-friend overloads are generated — one per cv-ref qualifier — with the wrapper as the right operand:
w + x; // member overload: reflects value.operator+(x)
x + w; // hidden friend: reflects x + value (ADL-found, executor-routed)
The reverse friend is constrained out when the left operand is itself a wrapper (via is_wrapper_v), so w1 + w2 resolves unambiguously to the member overload. The reverse path has no executor-override shortcut — the operator_##name override convention applies to the wrapper-left (member) overloads only — but it still routes through Executor::execute, so cross-cutting behaviour applies on both sides.
Constraint — different return types required
Because SCL_HAS_QUALIFIED_METHOD relies on return-type discrimination, overloads of the target operator with different cv-ref qualifiers must return different types.
Example
struct Target {
short operator+(int rhs) &;
int operator+(int rhs) const &;
};
struct MyWrapper {
TargetExecutor m_exec;
SCL_REFLECT_TYPE(MyWrapper, TargetExecutor);
};
MyWrapper w{10};
w + 5; // calls Target::operator+(int) & → short(15)
MyWrapper const cw{10};
cw + 5; // calls Target::operator+(int) const & → int(65)
#define SCL_REFLECT_TYPE(type, executor)
Declares the wrapper and executor types for use by SCL_REFLECT_METHOD.
Definition type.h:27
#define SCL_REFLECT_BINARY_OPERATOR(op, name)
Generates proxy binary operators that reflect op from the wrapped object through the executor,...
Definition operator.h:526
Parameters
opThe C++ binary operator token (e.g. +, -, ==).
nameShort unique name identifying the operator (plain identifier, e.g. op_add).
See also
SCL_REFLECT_MEMBER_BINARY_OPERATOR
SCL_REFLECT_PREFIX_UNARY_OPERATOR
SCL_REFLECT_POSTFIX_UNARY_OPERATOR
SCL_REFLECT_OPERATOR_WITH_ARGUMENTS
SCL_REFLECT_TYPE
scl::feature::executor_trait

◆ SCL_REFLECT_FRIEND_BINARY_OPERATOR

#define SCL_REFLECT_FRIEND_BINARY_OPERATOR ( op,
name )

Generates hidden-friend proxy binary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.

Identical in structure to SCL_REFLECT_BINARY_OPERATOR but generates friend free functions instead of member functions. Each generated overload takes the wrapper itself as its first (cv-ref-qualified) parameter and is only findable via Argument-Dependent Lookup (ADL).

Key difference from SCL_REFLECT_BINARY_OPERATOR
  • w.operator op(args) — NOT valid (no member function generated).
  • w op args — valid via ADL.
Constraint — different return types required
Same as SCL_REFLECT_BINARY_OPERATOR: overloads with different cv-ref qualifiers must return distinct types so that SCL_HAS_QUALIFIED_METHOD can distinguish them.
Do not combine with SCL_REFLECT_BINARY_OPERATOR for the same name
Both macros emit the same class-level helper constructs (QUALS, CALLER, EXEC_HELPERS). Using both for identical name in the same class body produces duplicate static member definitions.
Parameters
opThe C++ binary operator token (e.g. +, -, ==).
nameShort unique name identifying the operator (plain identifier, e.g. op_add).
See also
SCL_REFLECT_BINARY_OPERATOR
SCL_REFLECT_FRIEND_PREFIX_UNARY_OPERATOR
SCL_REFLECT_FRIEND_POSTFIX_UNARY_OPERATOR
SCL_REFLECT_TYPE
scl::feature::executor_trait

◆ SCL_REFLECT_FRIEND_POSTFIX_UNARY_OPERATOR

#define SCL_REFLECT_FRIEND_POSTFIX_UNARY_OPERATOR ( op,
name )

Generates hidden-friend proxy postfix-unary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.

Identical in structure to SCL_REFLECT_POSTFIX_UNARY_OPERATOR but generates friend free functions. Each overload takes a dummy int parameter (standard C++ postfix-operator signature).

Do not combine with SCL_REFLECT_POSTFIX_UNARY_OPERATOR for the same name
Both macros emit the same class-level helper constructs.
Parameters
opThe C++ postfix-unary operator token (++ or --).
nameShort unique name identifying the operator (plain identifier).
See also
SCL_REFLECT_POSTFIX_UNARY_OPERATOR
SCL_REFLECT_FRIEND_BINARY_OPERATOR
SCL_REFLECT_FRIEND_PREFIX_UNARY_OPERATOR

◆ SCL_REFLECT_FRIEND_PREFIX_UNARY_OPERATOR

#define SCL_REFLECT_FRIEND_PREFIX_UNARY_OPERATOR ( op,
name )

Generates hidden-friend proxy prefix-unary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.

Identical in structure to SCL_REFLECT_PREFIX_UNARY_OPERATOR but generates friend free functions. The wrapper itself is passed as the cv-ref-qualified operand; the operator is only ADL-findable.

Do not combine with SCL_REFLECT_PREFIX_UNARY_OPERATOR for the same name
Both macros emit the same class-level helper constructs.
Parameters
opThe C++ prefix-unary operator token (e.g. -, ++, *).
nameShort unique name identifying the operator (plain identifier).
See also
SCL_REFLECT_PREFIX_UNARY_OPERATOR
SCL_REFLECT_FRIEND_BINARY_OPERATOR
SCL_REFLECT_FRIEND_POSTFIX_UNARY_OPERATOR

◆ SCL_REFLECT_MEMBER_BINARY_OPERATOR

#define SCL_REFLECT_MEMBER_BINARY_OPERATOR ( op,
name )

Generates member-only proxy binary operators (no hidden-friend variants).

Use when the operator must be a non-static member function in C++20 (e.g. operator=, compound assignment operators). Generates the same 24 member overloads as the member subset of SCL_REFLECT_BINARY_OPERATOR without any friend counterparts.

Example operators that require this macro
operator=, operator*=, operator/=, operator%=, operator+=, operator-=, operator<<=, operator>>=, operator&=, operator|=, operator^=.
Parameters
opThe C++ binary operator token.
nameShort unique name identifying the operator (plain identifier).
See also
SCL_REFLECT_BINARY_OPERATOR
SCL_REFLECT_MEMBER_PREFIX_UNARY_OPERATOR
SCL_REFLECT_OPERATOR_WITH_ARGUMENTS

◆ SCL_REFLECT_MEMBER_POSTFIX_UNARY_OPERATOR

#define SCL_REFLECT_MEMBER_POSTFIX_UNARY_OPERATOR ( op,
name )

Generates member-only proxy postfix-unary operators (no hidden-friend variants).

Use when the operator must be a non-static member function. Generates 8 member overloads (one per cv-ref qualifier) without the additional hidden-friend overloads that SCL_REFLECT_POSTFIX_UNARY_OPERATOR produces.

Parameters
opThe C++ postfix-unary operator token (++ or --).
nameShort unique name identifying the operator (plain identifier).
See also
SCL_REFLECT_POSTFIX_UNARY_OPERATOR
SCL_REFLECT_MEMBER_PREFIX_UNARY_OPERATOR

◆ SCL_REFLECT_MEMBER_PREFIX_UNARY_OPERATOR

#define SCL_REFLECT_MEMBER_PREFIX_UNARY_OPERATOR ( op,
name )

Generates member-only proxy prefix-unary operators (no hidden-friend variants).

Use when the operator must be a non-static member function in C++20 (e.g. operator->). Generates 8 member overloads (one per cv-ref qualifier) with the same dispatch logic as SCL_REFLECT_PREFIX_UNARY_OPERATOR, but without the additional hidden-friend overloads.

Parameters
opThe C++ prefix-unary operator token.
nameShort unique name identifying the operator (plain identifier).
See also
SCL_REFLECT_PREFIX_UNARY_OPERATOR
SCL_REFLECT_MEMBER_BINARY_OPERATOR

◆ SCL_REFLECT_METHOD

#define SCL_REFLECT_METHOD ( name)

Generates proxy methods that reflect method from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.

For each of the 8 cv-ref qualifiers (&, &&, const&, const&&, volatile&, volatile&&, const volatile&, const volatile&&) three overloads are generated:

  • one executor-override overload — template<typename... ScLArgs>, active when Executor::method_##name exists
  • one execute-path overload — template<typename... ScLArgs>, active when no executor override is found
  • one with explicit template arguments — template<typename\ ScLParam,\ typename... ScLParams,\ typename... ScLArgs>

This gives 24 overloads in total (3 × 8); the executor-override and execute-path overloads are mutually exclusive via their requires clauses, so at most 16 are active for any given executor.

Executor override
If the executor provides a static method_##name(Executor cv_ref, args...) member whose first parameter matches the wrapper's cv-ref qualification exactly (detected via a function-pointer cast, the same technique as has_execute_v), the reflected method calls that member directly, bypassing Executor::execute. This lets the executor provide a custom implementation for specific methods.

If no such member exists, the execute-path overload is activated instead.

Executor dispatch (execute path)
When no executor override is found, the call is routed through Executor::execute(exec, callable, args...) so that executor strategies (e.g. locking, tracing) can intercept it. The callable receives the same args... and invokes the method on the wrapped value.

The executor is obtained at runtime via scl::feature::executor_trait<WrapperType>::executor(self). There is no default implementation of executor_trait — every wrapper type must provide an explicit specialization.

The wrapped value is then obtained by calling Executor::access(executor_ref).

Constraint — callability
Each non-template overload has one requires clause with two conditions:
  1. The wrapped object's method must be callable with the given arguments (after scl::wrapper_cast) for the particular cv-ref qualification.
  2. SCL_HAS_QUALIFIED_METHOD must confirm that the wrapped object has a dedicated overload for that qualifier (not an implicit cv-widening fallback). This prevents, for example, a const& proxy from being generated when only a mutable & overload exists on the target.

Explicit-template overloads use method_##name##_scl_template_quals for qualifier discrimination instead of method_##name##_scl_quals. The struct template forwards to SCL_HAS_QUALIFIED_METHOD with the token sequence template method<P,Ps...> (assembled via SCL_FORWARD) so it works even for purely-template methods where deduction without explicit arguments would fail — see SCL_REFLECT_TEMPLATE_METHOD_BASE for details.

Constraint — different return types required
Because SCL_HAS_QUALIFIED_METHOD relies on return-type discrimination, overloads of the target method with different cv-ref qualifiers must return different types. If two overloads return the same type, the macro will produce a false negative.
struct Good {
short get() &; // short ≠ int → distinguishable
int get() const &;
};
struct Bad {
int get() &; // int == int → macro cannot distinguish
int get() const &;
};
Template methods
If method is a template on the wrapped object (e.g. template<typename\ T>\ T\ convert()), users can call the reflected method with explicit template arguments: wrapper.convert<double>().

Internally this is handled by two helper constructs generated inside the enclosing class by SCL_REFLECT_METHOD_CALLER and SCL_REFLECT_METHOD_QUALS:

  • method_##name##_scl_caller — a struct whose static call<P,Ps...>(obj, args...) wraps the .template method<P,Ps...>(args...) call. The object parameter (ScLObj) is dependent, so the template keyword appears only in a dependent context and name lookup is deferred to instantiation. Without this indirection, .template foo<...> on a non-dependent type would be a hard parse error (not SFINAE) when foo is not a template.
  • method_##name##_scl_quals — a variable template that computes the qualifier-discrimination predicate for the deduced-args overload.
  • method_##name##_scl_template_quals — a struct template parameterised over <P,Ps...> with an inner value<V,As...> variable template. Delegates to SCL_HAS_QUALIFIED_METHOD with the token sequence template method<P,Ps...> so that the predicate evaluates the method call with concrete template arguments, handling purely-template methods that do not have any deduction-friendly non-template overload.

When method is not a template, the call function fails to instantiate (SFINAE via trailing return type), the requires clause evaluates to false, and the explicit-template-args overload is silently discarded.

Example
struct Target {
short get() &;
int get() const &;
float get() &&;
template <typename T>
T convert() const &;
};
struct MyWrapper;
template <>
struct scl::feature::executor_trait<MyWrapper> {
template <typename Self>
static constexpr decltype(auto) executor(Self && self)
{ return ::scl::forward_like<Self>(self.m_executor); }
};
struct MyWrapper {
::scl::feature::inplace::plain<Target> m_executor;
SCL_REFLECT_TYPE(MyWrapper, m_executor);
explicit MyWrapper(int v) : m_executor{v} {}
SCL_REFLECT_METHOD(get) // 24 generated (3×8), active for &, const&, &&
SCL_REFLECT_METHOD(convert) // 24 generated (3×8), only const& survives constraints
};
MyWrapper w{42};
w.get(); // calls Target::get() & → short(42)
MyWrapper const cw{42};
cw.get(); // calls Target::get() const & → int(92)
MyWrapper{42}.get(); // calls Target::get() && → float(142)
cw.convert<double>(); // calls Target::convert<double>() const & → 42.0
#define SCL_REFLECT_METHOD(name)
Generates proxy methods that reflect method from the wrapped object through the executor,...
Definition method.h:427
Root namespace of the ScL project.
Parameters
methodUnqualified method name (plain identifier).
See also
SCL_REFLECT_TYPE
scl::feature::executor_trait

◆ SCL_REFLECT_OPERATOR_WITH_ARGUMENTS

#define SCL_REFLECT_OPERATOR_WITH_ARGUMENTS ( op,
name )

Generates proxy call/subscript operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.

Identical in structure to SCL_REFLECT_BINARY_OPERATOR. Intended for operators whose syntax requires the argument list to be enclosed in brackets — specifically operator[] and operator() — but works for any operator accepted by SCL_REFLECT_OPERATOR_IMPL.

Parameters
opThe C++ operator token ([] or ()).
nameShort unique name identifying the operator (plain identifier, e.g. op_call).
See also
SCL_REFLECT_BINARY_OPERATOR
SCL_REFLECT_TYPE
scl::feature::executor_trait

◆ SCL_REFLECT_POSTFIX_UNARY_OPERATOR

#define SCL_REFLECT_POSTFIX_UNARY_OPERATOR ( op,
name )

Generates proxy postfix-unary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.

Identical in structure to SCL_REFLECT_PREFIX_UNARY_OPERATOR but generates overloads that take a dummy int parameter (standard C++ postfix signature: operator op(int)).

Only member-function overloads are generated. Use SCL_REFLECT_FRIEND_POSTFIX_UNARY_OPERATOR explicitly if an ADL-only (member-less) postfix operator is required.

Parameters
opThe C++ postfix-unary operator token (++ or --).
nameShort unique name identifying the operator (plain identifier, e.g. op_postinc).
See also
SCL_REFLECT_MEMBER_POSTFIX_UNARY_OPERATOR
SCL_REFLECT_PREFIX_UNARY_OPERATOR
SCL_REFLECT_BINARY_OPERATOR
SCL_REFLECT_TYPE
scl::feature::executor_trait

◆ SCL_REFLECT_PREFIX_UNARY_OPERATOR

#define SCL_REFLECT_PREFIX_UNARY_OPERATOR ( op,
name )

Generates proxy prefix-unary operators that reflect op from the wrapped object through the executor, for all 8 cv-ref qualifier combinations.

For each of the 8 cv-ref qualifiers a single overload is generated that uses if constexpr to dispatch to either the executor override or the execute path (unlike binary operators which generate two separate constrained overloads).

Only member-function overloads are generated. Unary operators have a single operand, so there is no reverse-operand case for a hidden friend to cover; use SCL_REFLECT_FRIEND_PREFIX_UNARY_OPERATOR explicitly if an ADL-only (member-less) unary operator is required.

Constraint — callability
The overload is active only when either:
  1. The executor provides an operator_##name(Executor cv_ref) override, or
  2. The wrapped object has a dedicated op overload for the given qualifier (checked via SCL_HAS_QUALIFIED_METHOD).
Executor override
If the executor provides a static operator_##name(Executor cv_ref) member (detected via a function-pointer cast), the generated operator calls that member directly, bypassing Executor::execute.
noexcept propagation
When the executor override path is selected, noexcept is propagated from the override. When the execute path is taken, noexcept follows Executor::execute.
Parameters
opThe C++ prefix-unary operator token (e.g. -, ++, *).
nameShort unique name identifying the operator (plain identifier, e.g. op_neg).
See also
SCL_REFLECT_MEMBER_PREFIX_UNARY_OPERATOR
SCL_REFLECT_BINARY_OPERATOR
SCL_REFLECT_POSTFIX_UNARY_OPERATOR
SCL_REFLECT_TYPE
scl::feature::executor_trait

◆ SCL_REFLECT_TYPE

#define SCL_REFLECT_TYPE ( type,
executor )

Declares the wrapper and executor types for use by SCL_REFLECT_METHOD.

Must appear inside the class body before any SCL_REFLECT_METHOD invocations.

A specialization of scl::feature::executor_trait must also be provided for type.

Example
SCL_REFLECT_TYPE(MyWrapper, MyExecutor);
Parameters
typeThe unqualified wrapper class name.
executorThe unqualified executor type name.
See also
scl::feature::executor_trait