Class template proxy
Header:
proxy.h
Module:proxy
Namespace:pro::inline v4
template <facade 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), and offers best-in-class code generation quality, extendibility and accessibility.
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.
As per facade<F>
, typename F::convention_types
shall be a tuple-like type containing any number of distinct types Cs
, and typename F::reflection_types
shall be a tuple-like type containing any number of distinct types Rs
.
- For each type
C
inCs
, ifC::is_direct
istrue
andtypename C::dispatch_type
meets the ProAccessible requirements ofproxy<F>, typename C::dispatch_type, substituted-overload-types...
,typename C::dispatch_type::template accessor<proxy<F>, typename C::dispatch_type, substituted-overload-types...>
is inherited byproxy<F>
. LetOs...
be the element types oftypename C::overload_types
,substituted-overload-types...
issubstituted-overload<Os, F>...
. - For each type
R
inRs
, ifR::is_direct
istrue
andtypename R::reflector_type
meets the ProAccessible requirements ofproxy<F>, typename R::reflector_type
,typename R::reflector_type::template accessor<proxy<F>, typename R::reflector_type
is inherited byproxy<F>
.
Member Types
Name | Description |
---|---|
facade_type (since 3.3.1) |
F |
Member Functions
Name | Description |
---|---|
(constructor) | constructs a proxy object |
(destructor) | destroys a proxy object |
emplace |
constructs the contained value in-place |
operator bool has_value |
checks if the proxy contains a value |
operator-> operator* |
accesses the accessors of the indirect conventions |
operator= |
assigns a proxy object |
reset |
destroys any contained value |
swap |
exchanges the contents |
Non-Member Functions
Name | Description |
---|---|
operator== |
compares a proxy with nullptr |
swap |
overload the std::swap algorithm |
Comparing with Other Standard Polymorphic Wrappers
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.
Example
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <proxy/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> container1{{1, "hello"}};
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"
}