template <typename …>
erasure function;
template <typename R, typename ...Args>
erasure function<R(Args…)>
{
using _Me = function<R(Args…)>;
// erasure implies these are pure virtual
R operator()(Args…);
template <typename Callable>
_Me& operator=(Callable&&);
};
template <typename Callable, typename R, typename ...Args>
class callable_container : public function<R(Args…)>
{
// The implementation of erased templates is hidden from users. Users can only see the public erasure interface.
R operator()(Args… args) override { /* Implement */ }
// The set of concrete types that are passed to operator= anywhere in the program,
// which are determined through template argument deduction,
// determines the amount of memory to be allocated for _c.
_Me& operator=(Callable&& c) override { /* Implement */ }
Callable _c;
};
class A
{
public:
template <typename Handler>
void SetHandler(Handler&& h) { mHandler = std::forward<Handler>(h); }
void Op()
{
// Do work
auto ret = mHandler(1, 2);
}
private:
Callable<int(int, int)> mHandler;
};
// As a first iteration, objects containing erased types can only be constructed in global scope.
A globalA;
class B
{
public:
void f()
{
globalA.SetHandler([&i, f = f, &d](auto&&…) {});
globalA.Op();
}
private:
float f;
int i;
double d;
};
class C
{
public:
void f()
{
globalA.SetHandler([this](auto&&…) {});
globalA.Op();
}
private:
float f;
int i;
double d;
};
Hi,Many times we create a variable from a specific instantiation of a class template, but then need to assign a value from a different instantiation. This is the classic case of mixing custom allocators for standard containers with the default allocator. C++17 solves this through polymorphic memory resources. But it would be nice to have an easy solution available for programmers. Generally, the 'type erasure' pattern involves the use of:1. boost/std any - which is inefficient because of the need to query the object or it forces use of the visitor pattern which scatters the logic all over the place.2. A custom abstract interface - which leads to runtime memory allocation.It would be nice if there was a mechanism by which the compiler could defer the determination of size till link time, and then choose a size that fits the largest size needed. In effect, it allows the programmer to control the tradeoff between higher static memory usage vs performance cost of memory allocation. Here, I would like to explore such an option, using type erasure of lambdas as a specific example.
Hi,Many times we create a variable from a specific instantiation of a class template, but then need to assign a value from a different instantiation. This is the classic case of mixing custom allocators for standard containers with the default allocator. C++17 solves this through polymorphic memory resources. But it would be nice to have an easy solution available for programmers. Generally, the 'type erasure' pattern involves the use of:1. boost/std any - which is inefficient because of the need to query the object or it forces use of the visitor pattern which scatters the logic all over the place.2. A custom abstract interface - which leads to runtime memory allocation.
It would be nice if there was a mechanism by which the compiler could [...]
Hi Arthur,Many thanks for your insightful reply. Having thought more about this, I really like the idea of a non-owning function wrapper. And I still insist on not having to perform the skulduggery of figuring out the right small buffer size. I want C++ code to be clean and expressive, reflecting the programmer's intent and automatically translating to efficient implementation. So, I would like to propose the following:For class methods defined inside the class declaration, allow a way to declare method local variables as class member variables. Thus, they have storage within each object of the class, and can be safely passed as a type erased reference. The size of the class includes enough aligned space for storing these variables.If you have the time, could you please take a look at this hypothetical code?
I know that the upcoming coroutines feature allows cleaner expression in this particular case
, but this is only an example. There are many places where this could be useful. With libraries such as Boost.Hana and rxcpp, we are increasingly seeing instances were the actual type is not known in advance. All we have is the return value from a function call. Persisting such objects without the need for dynamic memory allocation should be possible.