2016-05-25 8 views
0

このパズルには何週間も費やしてきましたが、ここではC++バリデーションテンプレートの群衆への挑戦として提供するかもしれないと考えました。それができれば、私はあなたに約10秒でどのように表示されるのかと賭けるでしょう。C++ - 17バリデーションテンプレート:コールバック引数の戻り値の型と引数の型の両方を取得します

私はC++で関数のコレクションを持っていますが、それぞれ独自の戻り型(またはvoid)を持ち、それぞれ独自の引数型を持っています。簡単にするため、などのクラス、引数の変数番号にPODの引数とポインタのミックスので、2つの例:

int somefunc(int x, double d) { return x + (int)d; } 
void otherfunc(Foo *a, Bar *b, int x) { ... } 

私の目標は、キャプチャ可変引数の関数を構築することにより、注釈なしで静的なコンパイル時の反射を行うことですここでは完全な型のセットです。議論のために、私たちの関数はRT f(T1 a、T2 b、...)型であると言うことができます。私のコンテキストは、RPCとマルチキャストシステムのための新しい(そしてはるかに優れた)データマーシャリングレイヤを構築していることです。これらのコールバックは実際にはバイト配列の受信時に行われます。適切な型:バイト配列の最初のバイトから抽出されたint、またはFoo自身が持ち上げを行うファクトリメソッドを持つ新しいFoo(char *)

静的な反射は何を意味しますか?私はconst std :: listを必要とします。ここでは、typeid(RT).hash_code()をInfoクラスに入れたり、引数のカテゴリごとにコンストラクタへのポインタを設定したりします(PODコンストラクタは基本的にint *に入ってくるバイトシーケンスを返し、intを返します;クラスコンストラクタはファクトリメソッドを呼び出します)。

OK、長いプリアンブル、失敗した試行:これは私の試行です。 C++ 17では、RTが正しくバインドされているように見えますが、Restはバインドできません。なぜなら、Restは実際にRTがキャプチャする関数型全体の引数型のリストなのかもしれないからです。何か案は?

class Info 
{ 
     int rt, at; Info *next; 
     Info(int r, int a, Info* nxt) { rt = r; at = a; next = nxt; } 
}; 

template<typename RT> 
Info *Scan(RT cb()) 
{ 
     return nullptr; 
} 

template<typename RT, typename T, typename Rest...> Info* Scan(RT cb(T x, Rest... args)) 
{ 
     return new Info(typeid(RT).hash_code(), typeid(T).hash_code(), Scan<RT, Rest...>(cb(args...)); 
}; 

int TestMethod(int x, int y) 
{ 
     return 0; 
} 

int main() 
{ 
     Scan(TestMethod); 
     return 0; 
} 
+0

'template ' < - '〜'は* Rest *の前にあるべきです。あなたがしたいことは、C++ 11で実行可能で、atmで作業しています。 – coyotte508

答えて

4

あなたは、あなただけのhash_codeコール上引数パラメータパックを展開することによって、ハッシュコードのstd::arrayを返すことができ、このための別のクラスまたは再帰を必要としません:

template <typename RT, typename... Args> 
std::array<size_t, (sizeof...(Args) + 1)> 
Scan (RT (*) (Args...)) 
{ 
    return { typeid(RT).hash_code(), typeid(Args).hash_code()... }; 
} 
+0

非常にクールです。私はこれを試してみて、それを私が後にしているものに変えることができるかどうかを見ます。 –

+0

(ハッシュコード以外に、ファクトリメソッドのリストを作成する必要もあります。これは、面倒なことがない限り、C++ 17ではコンストラクタが呼び出される前にメモリを割り当てる必要があるというルールのため、クラス内の(char *)コンストラクタを使用しています。これについてnewを呼び出すlambdaを使用すると思います。) –

+1

@KenBirman任意の種類のコンストラクタのアドレスを取得しません。コンストラクターには名前がないため、直接アクセスすることはできません。ラムダを使うことは可能ですが、 'new'を呼び出すのではなく、単に値を返すことをお勧めします。ただし、クロージャの種類は不特定なので、テンプレートクラスに格納するか、 'std :: function'などを使用する必要があります。 – TartanLlama

0
#include <typeinfo> 

class Info 
{ 
    int rt, at; Info *next; 
public: 
    Info(int r, int a, Info* nxt) { rt = r; at = a; next = nxt; } 
}; 

template<typename RT> 
Info *Scan() { 
    return nullptr; 
} 

template<typename RT, typename arg, typename ...args> 
Info *Scan() { 
    return new Info(typeid(RT).hash_code(), typeid(arg).hash_code(), Scan<RT, args...>()); 
} 

template<typename RT, typename ...Rest> Info* Extracter(RT (&)(Rest...)) 
{ 
    return Scan<RT, Rest...>(); 
} 

int TestMethod(int, int) 
{ 
    return 0; 
} 

int main() 
{ 
    Extracter(TestMethod); 
    return 0; 
} 

これは、リンクされたInfo構造を使用します。私は、目的の関数のすべての引数を抽出するために1つの関数を使用し、次にそれらを1つずつ列挙する別の関数を使用しました。すべてのコールで機能を実行する必要はありません。

タータンラマの答えはよりエレガントですが。

関連する問題