2016-05-31 10 views
6

呼び出し可能関数Cの型が与えられたとき、私はコンパイル時にstd::functionを取得したい。タイプ:制限付きの引数を持つstd :: function型を作成する

  • は、引数の型がこの手段C

関数の最初N引数型であるC

  • 関数の同一の戻り型を有し、その指定された型のvoid(int, char, double)と与えられたNであり、関数のタイプは次の通りである。

    • N = 1 =>結果のタイプ:std::function<void(int)>
    • N = 2 =>結果のタイプ:std::function<void(int, char)>
    • N = 3 =>結果のタイプ:std::function<void(int, char, double)>
    • N > 3 =>コンパイル時エラー

    例:

    template<std::size_t N, typename R, typename... A> 
    constexpr auto get() { 
        return /*(magically somehow)*/ std::function<R(FirstNFromA...)> 
    } 
    
    template<std::size_t N, typename R, typename... A> 
    struct S { 
        using func = decltype(get<N, R, A...>()); 
    }; 
    
  • +0

    ニースQ&その解決策。コーディングの喜びのようです。しかし、私はこれが実際にどこで使われるのか分からないのですか?実際のアプリケーションで、なぜ単純な 'std :: function 'の代わりに 'FuncType <2、void(int、char、double、int)> :: Type'を使用したいのですか?テンプレートの中でさえ、そのようなことをする必要はほとんどありません。厳しい問題を解決するには、2つの方法があります.1つの厳しい解決策を導き出すか、問題を簡単にするかのどちらかです。 :-) – iammilind

    +0

    @iammilindミックスインを使って何かをデザインしようとしたときに問題が発生しました。ミックスインの1つはバリデーショナルなタイプのリストを取得します。最後のタイプは継承するタイプのリストです。最初の 'N-1'は関数型の引数として使われなければならないことは言うまでもない。 :-) – skypjack

    答えて

    6

    それは可能な解決策に従う:

    #include <tuple> 
    #include <utility> 
    #include <functional> 
    #include <type_traits> 
    
    template< 
        typename R, 
        typename... A, 
        std::size_t... I, 
        std::enable_if_t<(sizeof...(I)<=sizeof...(A))>* = nullptr 
    > constexpr auto 
    get(std::integer_sequence<std::size_t, I...>) { 
        return std::function<R(std::tuple_element_t<I, std::tuple<A...>>...)>{}; 
    } 
    
    template<std::size_t, typename> 
    struct FuncType; 
    
    template<std::size_t N, typename R, typename... A> 
    struct FuncType<N, R(A...)> { 
        using Type = decltype(get<R, A...>(std::make_index_sequence<N>{})); 
    }; 
    
    int main() { 
        static_assert(
         std::is_same< 
          FuncType<2, void(int, char, double, int)>::Type, 
          std::function<void(int, char)> 
         >::value, 
         "!" 
        ); 
    } 
    

    基本的な考え方は、tupleと標準テンプレートライブラリの一部であるユーティリティ(例:std::tuple_element)を使用し、それらを適切な場所に配置されたパック展開と混ぜることです。
    これを行うには、与えられた型の空のstd::functionオブジェクトを返すconstexpr関数を使用しました。次に、関数の型はdecltypeによって取得され、エイリアスを使用してFuncTypeオブジェクトの型としてエクスポートされます。
    すべてがコンパイル時に行われます。
    その関数の正しい型を推定するために、私はstd::integer_sequenceというよく知られたパターンを使用し、実際にはインデックスを拡張してタプルの型を解凍しました。

    +0

    私たちはC++ 14-landで、 'tuple_element_t'を使用します。 –

    +0

    @ T.C。それは理にかなっている。一定。 – skypjack

    1

    別の解決策は次のようになります。

    #include <tuple> 
    #include <utility> 
    #include <functional> 
    #include <type_traits> 
    
    template <size_t N, class R, class Pack, class ResultPack, class Voider> 
    struct FuncTypeImpl; 
    
    template <size_t N, class R, template <class...> class Pack, class First, class... Args, class... ResultArgs> 
    struct FuncTypeImpl<N, R, Pack<First, Args...>, Pack<ResultArgs...>, std::enable_if_t<(N > 0)>>: FuncTypeImpl<N-1, R, Pack<Args...>, Pack<ResultArgs..., First>, void> { 
        using typename FuncTypeImpl<N-1, R, Pack<Args...>, Pack<ResultArgs..., First>, void>::Type; 
    }; 
    
    template <size_t N, class R, template <class...> class Pack, class... Args, class... ResultArgs> 
    struct FuncTypeImpl<N, R, Pack<Args...>, Pack<ResultArgs...>, std::enable_if_t<(N == 0)>> { 
        using Type = std::function<R(ResultArgs...)>; 
    }; 
    
    template<std::size_t, typename> 
    struct FuncType; 
    
    template<std::size_t N, typename R, typename... A> 
    struct FuncType<N, R(A...)> { 
        using Type = typename FuncTypeImpl<N, R, std::tuple<A...>, std::tuple<>, void>::Type; 
    }; 
    
    int main() { 
        static_assert(
         std::is_same< 
          FuncType<3, void(int, char, double, int)>::Type, 
          std::function<void(int, char, double)> 
         >::value, 
         "!" 
        ); 
    } 
    

    編集: さらに別の(つまりはstd ::タプルを必要としない)多分少し簡単なソリューション:

    #include <utility> 
    #include <functional> 
    #include <type_traits> 
    
    template <class T> 
    struct ResultOf; 
    
    template <class R, class... Args> 
    struct ResultOf<R(Args...)> { 
        using Type = R; 
    }; 
    
    template<std::size_t N, class Foo, class ResultFoo = typename ResultOf<Foo>::Type() , class Voider = void> 
    struct FuncType; 
    
    template<std::size_t N, class R, class First, class... Args, class... ResultArgs > 
    struct FuncType<N, R(First, Args...), R(ResultArgs...), std::enable_if_t<(N > 0)>>: FuncType<N-1, R(Args...), R(ResultArgs..., First), void> { 
    }; 
    
    template<std::size_t N, class R, class First, class... Args, class... ResultArgs > 
    struct FuncType<N, R(First, Args...), R(ResultArgs...), std::enable_if_t<(N == 0)>> { 
        using Type = std::function<R(ResultArgs...)>; 
    }; 
    
    int main() { 
        static_assert(
         std::is_same< 
          FuncType<3, void(int, char, double*, int)>::Type, 
          std::function<void(int, char, double*)> 
         >::value, 
         "!" 
        ); 
    } 
    
    1

    別のタプルベースのソリューションです。

    C++ 11でも動作するはずです。

    簡略化できますが、boolテンプレートパラメータの使用を避ける方法はわかりません(私はC++ 11を学んでいます)。

    #include <tuple> 
    #include <utility> 
    #include <functional> 
    #include <type_traits> 
    
    
    template<std::size_t N, bool Z, typename R, typename...> 
    struct FTH1; 
    
    template <typename R, typename... A, typename... B> 
    struct FTH1<0U, true, R, std::tuple<A...>, B...> 
    { using type = decltype(std::function<R(A...)>{}); }; 
    
    template <std::size_t N, typename R, typename... A, typename B0, typename... B> 
    struct FTH1<N, false, R, std::tuple<A...>, B0, B...> 
    { using type = typename FTH1<N-1U, (N-1U == 0U), R, std::tuple<A..., B0>, B...>::type; }; 
    
    
    template <std::size_t N, typename> 
    struct FuncType; 
    
    template<std::size_t N, typename R, typename... A> 
    struct FuncType<N, R(A...)> 
    { using Type = typename FTH1<N, (N == 0), R, std::tuple<>, A...>::type; }; 
    
    
    
    int main() { 
        static_assert(
         std::is_same< 
          FuncType<2, void(int, char, double, int)>::Type, 
          std::function<void(int, char)> 
         >::value, 
         "!" 
        ); 
    } 
    

    p .:申し訳ありません。

    1

    コメントに記載されているように、このような問題が発生した場合、私の最初のアプローチは問題自体を変更することです!場合によっては、「厳しい問題」に対して「厳しい解決策」を見つけるのではなく、問題自体を「より単純にする」方が良いでしょう。
    単純なものの代わりにFuncType<2, R(X,Y,Z)>::typeと書かなければならない必要はありません。std::function<R(X,Y)>

    上記は私の本当の答えです。コーディングの喜びとしてあなたの問題を解決するために、私は単純なマクロベースの答えを入れています。コンパイル時間の代わりに、前処理時に必要な型を提供します。

    #define F(R, ...) std::function<R(__VA_ARGS__)> // shorthand for std::function... 
    #define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__) // Main type 
    #define FUNC_0(R, ...) F(R) // overload for 0 arg 
    #define FUNC_1(R, _1, ...) F(R, _1) // overload for 1 arg 
    #define FUNC_2(R, _1, _2, ...) F(R, _1, _2) // overload for 2 args 
    #define FUNC_3(R, _1, _2, _3, ...) F(R, _1, _2, _3) // overload for 3 args 
    

    使用法:

    int main() { 
        static_assert(std::is_same< 
        FuncType(2, void, int, char, double, int), // <--- see usage 
        std::function<void(int, char)> 
        >::value, "!"); 
    } 
    

    あなたが使用量の変化が少ない見ることができるように。 FuncType<2, void(int, char, double, int)>::typeの代わりにFuncType(2, void, int, char, double, int)を使用しています。ここにはdemoがあります。


    今のところ、FUNC_Nのマクロは3つの引数までオーバーロードされています。それは

    std::ofstream funcN("FUNC_N.h"); // TODO: error check for argv & full path for file 
        for(size_t i = 0, N = stoi(argv[1]); i < N; ++i) 
        { 
        funcN << "#define FUNC_" << i << "(R"; // FUNC_N 
        for(size_t j = 1; j <= i; ++j) 
         funcN << ", _" << j; // picking up required args 
        funcN << ", ...) "; // remaining args 
    
        funcN << "F(R"; // std::function 
        for(size_t j = 1; j <= i; ++j) 
         funcN << ", _" << j; // passing only relevant args 
        funcN <<")\n"; 
        } 
    

    単に#include:複数の引数、我々はコピーペーストを避けたい場合は、その後、我々は簡単なプログラムとヘッダファイルを生成することができますについて

    #define F(R, ...) std::function<R(__VA_ARGS__)> 
    #define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__) 
    #include"FUNC_N.h" 
    

    ここではどのようにのためにdemoですヘッダーファイルを生成します。

    +0

    コメントで既に述べたように、 'R(Args ...)'があるときは、実際の型ではないパラメータパックのサイズも知らないところが必要です。あなたが知っている唯一の事は、あなたがそれらすべてを望んでいないということです、それだけです。 :-) ...とにかく、お返事ありがとうございます。ソリューションとしてスケーラビリティはあまりありませんが、機能します。 – skypjack

    +0

    @skypjack、 ''どちらも実際の型ではないパラメータパックのサイズではありません。あなたが知っている唯一のことは、それらのすべてを "' 'したくないということです。私の要点は、そのような要件自体が発生しないような方法でプロジェクトを設計したいと思うかもしれないということです。しかし、私はあなたの必要性が真実かどうかを判断していません。ちょうど意見。あなたのソリューションは、他のものに依存しないので、マクロスタイルよりも魅力的です。しかし、上記のマクロ解もスケーラブルです。すべてのマクロを含む小さなコードを使用してヘッダーファイルを作成するだけです。 – iammilind

    関連する問題