Question

I need to pass a Lambda as callback (in particular for WinAPI). The idea is the following:

  1. Store the lambda in a singleton class (every Lambda, also two identical ones, have different types) so it should be safe

    LambdaSingleton<Lambda_Type>::instance = l;

  2. Pass as callback the address of static method that invokes the lambda instance.

    template <
        typename Lambda,
        typename Callback_Signature_R,
        typename... Callback_Signature_Args>
    struct LambdaCallbackSupport{
    
        /**
        *   Callback method
        *
        *   @param  args
        *               The parameters to feed to the lambda
        *   @return 
        *               The return value of the execution of the lambda
        */
        static Callback_Signature_R __stdcall callback(Callback_Signature_Args... args){
            return LambdaSingleton<Lambda>::instance(args);
        }
    };
    

I already have a working class for extracting informations about functions at compile time es:

template<
    typename C,
    typename R,
    typename... Args>
struct Traits<R(__stdcall *)(Args...) const>{
      //various typedefs for R, tuple of args, arity etc..
};

So i would get something like this:

//Example lambda
int toBeCaptured = 8;
auto lambda =
    [&](std::string& str) -> size_t{
        return toBeCaptured + str.length();
    };

typedef decltype(lambda) Lambda;

//Expected callback signature
typedef size_t(__stdcall *CallbackSignature)(std::string&);

//Configure a callback support and pass its method
typedef Traits<CallbackSignature> callbackTraits;

typedef LambdaCallbackSupport<
    Lambda,
    callbackTraits::Result_Type,
    callbackTraits::Args_Tuple_Pack> CallbackSupportType;
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  //How to unpack the tuple without actually have the arguments??

//Store the lambda instance statically
Singleton<Lambda>::instance = lambda;

//Pass the callback
void* pFunc = &CallbackSupportType::callback;

//Simulate invocation of callback
std::string str("may work?");
size_t ret = (*pFunc)(str);

Since i need only to let the compiler generate a callback class specialization (and not actually invoke its method) how can i apply the iterative unpacking technique proposed in other questions on this site?

Thank you

Was it helpful?

Solution

As a general answer to your question (how to do tuple unpacking), parameter packs can only be generated implicitly in the context of template argument type deduction, so if you want to "unpack" a type tuple<T1, ..., Tn> into a sequence of types T1, ..., Tn you have to instantiate that tuple and supply that instance in input to some function template:

template<typename... Ts>
void unpack(tuple<Ts...> const&) // Now you have an argument pack...

However, considering what you want to achieve (get a WinAPI callback from a lambda), I would not rely on tuples, and rather use a free function template. That can be done without introducing many levels of indirections and wrappers. Here is a possible simple solution:

#include <type_traits>
#include <memory>

template<typename F>
struct singleton
{
    static void set_instance(F f) { instance.reset(new F(f)); }
    static std::unique_ptr<F> instance;
};

template<typename F>
std::unique_ptr<F> singleton<F>::instance;

template<typename F, typename... Ts>
typename std::result_of<F(Ts...)>::type __stdcall lambda_caller(Ts... args)
{
    if (singleton<F>::instance == nullptr)
    {
        // throw some exception...
    }
    else
    {
        return (*(singleton<F>::instance))(args...);
    }
}

This is the framework. And this is how you would use it:

#include <iostream>

int main()
{
    //Example lambda
    int toBeCaptured = 8;
    auto lambda =
        [&](std::string& str) -> size_t{
            return toBeCaptured + str.length();
        };

    singleton<decltype(lambda)>::set_instance(lambda);
    size_t (__stdcall *pfn)(std::string&) = &lambda_caller<decltype(lambda)>;

    std::string str = "hello";
    int out = pfn(str);
    std::cout << out;

    return 0;
}

If you don't mind macros and want to simplify that further for some usage patterns (like the one above), you can add a macro like this:

#define get_api_callback(lambda) \
    &lambda_caller<decltype(lambda)>; singleton<decltype(lambda)>::set_instance(lambda);

That would change your main() function into the following:

#include <iostream>

int main()
{
    //Example lambda
    int toBeCaptured = 8;
    auto lambda =
        [&](std::string& str) -> size_t{
            return toBeCaptured + str.length();
        };

    // As simple as that...
    size_t (__stdcall *pfn)(std::string&) = get_api_callback(lambda);

    std::string str = "hello";
    int out = pfn(str);
    std::cout << out;

    return 0;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top