2016-11-04 5 views
-2

Pythonの便利な機能の1つは、プロパティを使用して関数をラップすることができることです。 たとえば、引数と戻り値のそれぞれに関数を適用して、関数が処理できるものまたは呼び出し可能なコマンドで処理できるものに変換する必要があります。C++で関数をラップする

コンパイル時にどのようにC++を使ってこれを行うのですか? つまり、2つの テンプレート化されたファンクタを型とファンクタfuncとし、上記の方法でラップされたラムダを返す関数テンプレートラップをどのように定義しますか?

アイデアを少し具体的にするために、私たちは以下を持っています。 ここで考えているのは、Args ...をとり、ReturnValsを返す関数fです。 h1(Args)....をとり、h2(ReturnVals)を返す関数gが必要です。

つまり、g = h2(f(h1(Args)...)....)。

Python-like-C-decoratorsと似ています。違いは、マクロでコンパイル時にこれを完全に行うことです。そうすれば、オーバーヘッドと型セーフではありません。関連情報がすべてコンパイラに認識されているので、これを実行できるはずです。

+0

C++からPython関数を呼び出したいと思っていますか? –

+4

擬似コード(Python)の例が良いでしょう。 – Lunaweaver

+1

@ChrisBritt、いいえ、私はそうは思わない。質問は、「ここにPythonの持つ機能があります.C++にもこの機能がありますか?」 – Kevin

答えて

0

私は実際にこれを行う方法を理解していて、スタックオーバーフローの人はこのメソッドに興味があると思っていました。コードはGitlab repositoryで公開されています。

悲しいことに、完全に一般的な意味では複雑ですが、以下のコードは機能します。 最初に、いくつかのメタプログラミングヘッダーとtype_traitを入れて、 タイプが別のタイプの特殊化であるかどうかを確認します。

#include <type_traits>                        
#include <tuple>                         
#include <utility>                         
#include <functional>                        
#include "arma_wrapper.h"                       

/* Tests if U is a specialization of T */                   
template<template<typename...> typename T, typename U>                
struct is_specialization_of : std::false_type {};                 


template<template <typename ...> typename T, typename... Args>              
struct is_specialization_of<T, T<Args...>> : std::true_type {};  

私たちは、その後、私たちは 引数の型や関数の引数の数を推定することができますclosure_traits関数を定義します。 これを行う方法について私のquestionに答えた種類の人々には感謝しています。

/* For generic types use the type signature of their operator() */             
template <typename T>                        
struct closure_traits : 
    public closure_traits<decltype(&T::operator())> {};           


/*                             
* This is adapted from the stack overflow question                 
* Is it possible to figure out the parameter type and return type 
* of a lambda.          
*/                             
template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>           
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) 
         const>           
{                             
    using arity = std::integral_constant<std::size_t, 
             sizeof...(ArgTypes)>;           
    using Ret = ReturnType;                       

    template <std::size_t I>                      
    struct Args {                         
     using type = typename std::tuple_element<I, 
           std::tuple<ArgTypes...>>::type;         

    };                            

};                             


template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>           
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args)>             
{                             
    using arity = std::integral_constant<std::size_t, 
             sizeof...(ArgTypes)>;           
    using Ret = ReturnType;                       

    template <std::size_t I>                      
    struct Args {                         
     using type = typename std::tuple_element<I, 
          std::tuple<ArgTypes...>>::type;         

    };                            

};   

は今、私はあなたがアンパックタプル、にあなたを可能にすること、および foreach_in_tuple(STDのジャストバージョンは:: C++ 17から適用されます) に関数を適用することを可能にするヘルパー関数を定義しますタプルの の各要素にファンクタを適用します。これは再帰的に行われるため、ヘルパー関数が必要です。

namespace detail {                         


/*                            
    * This function defines a generic lambda that takes can be applied 
    * to every one of the arguments in the tuple. It requires that 
    * Func has a overload for operator() that can be applied to each of 
    * the parameters. Then it applies this lambda to each of the 
    * parameters in the tuple.           
    */                            
    template<typename Func, typename TupleType, std::size_t... I>             
    decltype(auto) for_each_impl(TupleType&& tup, 
           std::index_sequence<I...>) {          

     auto func_impl = [] (auto&& x) {                   
      Func func;                        
      return func(std::forward<decltype(x)>(x));                
     };                           

     return std::make_tuple(func_impl(std::get<I> 
           (std::forward<TupleType>(tup)))...);       

}                            


/* My version of c++17 apply_impl method. */                 
template<typename FuncType, typename TupleType, std::size_t... I>            
decltype(auto) apply_impl(FuncType&& func, TupleType&& tup, 
          std::index_sequence<I...>) {      

    return func(std::get<I>(std::forward<TupleType>(tup))...);             

}                            

}                             

今、私はその引数を取り、それが既に1でない場合、タプルにラップ関数を定義し、それがある場合はそれだけで同じことを残します。これは素晴らしいことです。なぜなら、別の関数(f)をラップして呼び出すことができ、ラップされた関数がタプルを返すと仮定できるからです。

template<typename T>                        
auto idempotent_make_tuple(T&& arg) ->  
    std::enable_if_t<is_specialization_of<std::tuple,T>::value, T&&> {   

    return arg;                          

}                             


template<typename T>                        
auto idempotent_make_tuple(T&& arg) -> 
    std::enable_if_t<! is_specialization_of<std::tuple, T>::value,    

     decltype(std::make_tuple(std::forward<T>(arg)))> {      

    return std::make_tuple(std::forward<T>(arg));                 

}                             

これらの2つの機能は、単にC++ 17 のstdのわずかに少ないジェネリックバージョン::適用し、タプルの各要素にファンクタを適用する機能です。

template<typename FuncType, typename TupleType>                  
decltype(auto) apply(FuncType&& func, TupleType&& tup) {                             
    return detail::apply_impl(std::forward<FuncType>(func), 
           std::forward<TupleType>(tup), 
          std::make_index_sequence<std::tuple_size< 
          std::decay_t<TupleType>>::value>{});     

}                             



/* Applies a Functor Func to each element of the tuple. As you might 
    * expect, the Functor needs overloads for all of the types that in 
    * the tuple or the code will not compile.            
    */                             
template<typename Func, typename TupleType>                   
decltype(auto) for_each_in_tuple(TupleType&& tup) {                 

    return detail::for_each_impl<Func>(std::forward<TupleType>(tup),            

          std::make_index_sequence<std::tuple_size< 
          std::decay_t<TupleType>>::value>{});   

}              

次の関数は、再帰的方法を使用してアンパックすることにより、引数のそれぞれに関数を適用します。上記のapply_impメソッドとforeach_in_tupl_implメソッドで使用されているのと同じメソッドです。 戻り値もラップします。

namespace detail {                         

    /*                            
    * This function takes a function and an index sequence with its 
    * number of arguments. It then figures out the types of its 
    * arguments, and creates a new function with each of the 
    * arguments and each of the returned values converted to the 
    * new types.                   
    */                            
    template<typename ArgWrapper, typename ReturnWrapper, 
      typename FuncType, size_t...I>       
    auto wrap_impl(FuncType&& func, std::index_sequence<I...>) {             

     /* 
     * This is used to figure out what the argument types of 
     * func are 
     */          
     using traits = closure_traits< 
          typename std::decay_t<FuncType>>;            

     auto wrapped_func = [=] (std::result_of_t<ReturnWrapper(             
      typename traits:: template Args<I>::type)>... args) {             

      /* 
      * Apply the argument wrapper function to each of the 
      * arguments of the new function. 
      */     
      decltype(auto) tup1 = for_each_in_tuple<ArgWrapper> 
        (std::forward_as_tuple(args...));     
      /* Apply the old function to the wrapped arguments. */             
      decltype(auto) tup2 = idempotent_make_tuple(apply(func,             
       std::forward<std::decay_t<decltype(tup1)>>(tup1)));             
      /* 
      * Apply the Return wrapper to the return value of the 
      * old function 
      */         
      decltype(auto) tup3 = for_each_in_tuple<ReturnWrapper>(            
       std::forward<std::decay_t<decltype(tup2)>>(tup2));             

      return tup3;                       
     };                           

     return wrapped_func;                      

    }                            


}                             

実際にラッピングする機能。

template<typename ArgWrapper, typename ReturnWrapper, 
     typename FuncType>      
auto wrap(FuncType&& func) {                      

    return detail::wrap_impl<ArgWrapper, ReturnWrapper>(               
       std::forward<FuncType>(func), 
       std::make_index_sequence<closure_traits< 
         FuncType>::arity::value> {}); 
} 
関連する問題