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
CinCs, ifC::is_directistrueandtypename C::dispatch_typemeets 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
RinRs, ifR::is_directistrueandtypename R::reflector_typemeets the ProAccessible requirements ofproxy<F>, typename R::reflector_type,typename R::reflector_type::template accessor<proxy<F>, typename R::reflector_typeis 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 boolhas_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"
}