proxy
template <class F>
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
, std::move_only_function
, std::any
, etc.), proxy
is based on pointer semantics. It supports flexible lifetime management without runtime garbage collection (GC) at runtime, and offers best-in-class code generation quality, extendibility and accessibility.
To instantiate proxy<F>
, F
shall model concept facade
. As per facade<F>
, typename F::convention_types
shall be a tuple-like type containing any number of distinct types Cs
. For each type C
in Cs
, if C
meets the ProAccessible requirements 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*
when C::is_direct
is false
. Implementation of accessors can call access_proxy
to access the proxy
object. It is recommended to use facade_builder
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>
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.
Name | Description |
---|---|
(constructor) | constructs a proxy object |
(destructor) | destroys a proxy object |
operator= |
assigns a proxy object |
operator bool has_value |
checks if the proxy contains a value |
reset |
destroys any contained value |
swap |
exchanges the contents |
emplace |
constructs the contained value in-place |
operator-> operator* |
accesses the accessors of the indirect conventions |
Name | Description |
---|---|
swap |
overload the std::swap algorithm |
operator== |
compares a proxy with nullptr |
The C++ standard includes several polymorphic wrappers, such as std::function
, std::packaged_task
, std::any
, and std::move_only_function
(as of C++23). proxy
offers all their useful features and more, with equal or better code generation compared to various STL implementations.
A key difference is that proxy
is based on pointer semantics, allowing flexible lifetime management without runtime GC overhead. In C++11, std::function
and std::packaged_task
had constructors that accepted custom allocators for performance tuning, but these were removed in C++17 because “the semantics are unclear, and there are technical issues with storing an allocator in a type-erased context and then recovering that allocator later for any allocations needed during copy assignment”. These issues do not apply to proxy
which fully supports custom allocators via allocate_proxy
.
Another major difference is that proxy
is open to abstractions. Unlike std::function
, std::packaged_task
and std::move_only_function
, which only abstracts operator()
, and std::any
, which only abstracts casting, proxy
allows users to define any runtime abstraction requirements via facade
. It is recommended to use facade_builder
to define a custom facade with any conventions, reflections, or constraints.
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "proxy.h"
PRO_DEF_MEM_DISPATCH(MemAt, at);
struct Dictionary : pro::facade_builder
::add_convention<MemAt, std::string(int)>
::build {};
// This is a function, rather than a function template
void PrintDictionary(pro::proxy<Dictionary> dictionary) {
std::cout << dictionary->at(1) << "\n";
}
int main() {
static std::map<int, std::string> container11;
auto container2 = std::make_shared<std::vector<const char*>>();
container2->push_back("hello");
container2->push_back("world");
PrintDictionary(&container1); // Prints: "hello"
PrintDictionary(container2); // Prints: "world"
}