How to store and restore the exact type of a type-erased object?

1553 views c++
2

I'm sure there's a name for what I'm looking for, I just don't know it (and if I did, I'd probably find the answer already). Basically, I want to implement my own lightweight version of std::function for sports. I want to initialize it with a lambda, and later invoke it. I can wrap the lambda with my template wrapper class, nothing to it:

class CInvokableAbstract {
   virtual ~CInvokableAbstract() = default;
};

template <class InvokableObject>
struct CInvokableBasic : public CInvokableAbstract
{
    CInvokableBasic(InvokableObject&& target) : _invokable(std::move(target)) {}

    template <typename... Args>
    typename std::result_of<decltype(&InvokableObject::operator())(Args...)>::type operator()(Args... args) const
    {
        return _invokable(std::forward<Args>(args)...);
    }

private:
    InvokableObject _invokable;
};

Now I can make my class that's semantically similar to std::function, but how can I store the exact type of the lambda in order to convert the type-erased object back to its original concrete type?

struct CInvokableDeferred
{
    template <class InvokableObject>
    CInvokableDeferred(InvokableObject&& target) noexcept : _invokable(std::make_unique<CInvokableBasic<InvokableObject>>(std::move(target))) {}

    template <typename... Args>
    void operator()(Args... args) const
    {
        // How can I know the original concrete type to cast this to?
        static_cast<???>(_invokable.get())->(std::forward<Args>(args)...);
    }

private:
    std::unique_ptr<CInvokableAbstract> _invokable;
};

I can't think of any template trickery that could do that, yet we know it's possible (unless std::function uses some compiler built-ins, or otherwise is implemented internally in the compiler rather than being normal C++ code).

Note that I'm using a compiler that doesn't have full C++17 support, so I can't use e. g. auto-deduced return type.

answered question

1 Answer

0

Firstly, operator() should not be a template.
The desired function signature should be a part of the class template itself.

Then, the trick is to store a function pointer that, when called, invokes the type-erased object from std::unique_ptr<CInvokableAbstract>.

In your templated constructor, you assign (presumably) a lambda to that pointer, so the contents of the pointer will depend on the type passed to the constructor.

posted this

Have an answer?

JD

Please login first before posting an answer.