2

Lambdaをコールバックとして渡す必要があります(特にWinAPIの場合)。アイデアは次のとおりです。

  1. ラムダをシングルトンクラスに格納する(すべてのラムダ、2つの同一のものも異なるタイプを持つ)ので、安全である必要があります

    LambdaSingleton<Lambda_Type>::instance = l;

  2. ラムダインスタンスを呼び出す静的メソッドのアドレスをコールバックとして渡します。

    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);
        }
    };
    

コンパイル時に関数に関する情報を抽出するための作業クラスがすでにあります。

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

だから私はこのようなものを手に入れるでしょう:

//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);

コンパイラにコールバッククラスの特殊化を生成させるだけでよいので(実際にはそのメソッドを呼び出さないでください)、このサイトの他の質問で提案されている反復アンパック手法をどのように適用できますか?

ありがとうございました

4

1 に答える 1

3

あなたの質問(タプルアンパックを行う方法)に対する一般的な答えとして、パラメーターパックはテンプレート引数タイプの推論のコンテキストでのみ暗黙的に生成できるため、タイプをタイプのシーケンスに「アンパック」する場合は、インスタンス化する必要がありますそのタプルとそのインスタンスを関数テンプレートへの入力で提供します。tuple<T1, ..., Tn>T1, ..., Tn

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

ただし、達成したいこと(ラムダからWinAPIコールバックを取得する)を考慮すると、タプルに依存せず、無料の関数テンプレートを使用します。これは、多くのレベルの間接化とラッパーを導入することなく実行できます。考えられる簡単な解決策は次のとおりです。

#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...);
    }
}

これがフレームワークです。そして、これはあなたがそれをどのように使うかです:

#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;
}

マクロを気にせず、いくつかの使用パターン(上記のような)でさらに単純化したい場合は、次のようなマクロを追加できます。

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

これにより、main()関数が次のように変更されます。

#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;
}
于 2013-01-15T18:43:43.670 に答える