3

Pythonは、function decoratorsという非常に便利な機能を備えており、さらに構成が可能です。合成可能なC++関数のデコレータ

@lru_cache 
@retry 
def foo(...): 
:機能 fooを書く場合たとえば、あなたは、あなたがで、 foofooも例外を発生させているキャッシュミスの場合は、単一の時間よりも memoized、だけでなく、 retried以上になりたいことを述べることができます

デコレータのコンポジット機能では、fooや個々の関数デコレータなどの機能を個別に開発し、必要に応じてそれらを混合することができます。可能ならば、C++でもそうすることができればうれしいでしょう。

関数デコレータに関するStackOverflowに関するいくつかの質問がありますが、デコレータ関数のシグネチャの厳格な前提のため、すべての関数はデコンストラクタを生成できないようです。たとえば、優れたトップ投票の回答をthis questionとします。装飾は

template <typename R, typename... Args> 
std::function<R (Args...)> memo(R (*fn)(Args...)) { 

したがって、cannot be applied to the result of itself(メモ化の具体的なデコレータを使用するために、問題の確かにあまりない)の形式です。

次に、合成可能な関数デコレータを書くことができますか?

+0

あなたが与えた例はあまり説得力がありません。その実装に関数ポインタを必要とするものは何もありません。 –

答えて

0

コンポジット可能な関数デコレータを作成する1つの方法は、デコレータによって取得されたシグネチャに関する仮定を緩和することです。図から分かるように、我々は

template<class Fn> 
struct foo_decorator 
{ 
    template<typename ...Args> 
    auto operator()(Args &&...args) const -> 
     typename std::result_of<Fn(Args...)>::type; 
}; 

template<class Fn> 
foo_decorator<Fn> make_foo(const Fn &fn) {return foo_decorator<Fn>();} 

を持っていると言う、make_foofoo_decoratorの両方がその時点で事実上何もすることができたclass Fnによってパラメータ化されています。したがって、それらは例えばラムダ関数またはファンクタの両方をとることができる。取得された引数(および戻り値の型)は呼び出しに遅延されます(コンパイル時)。C++関数呼び出しの推定テンプレートパラメータによって、必要に応じて残りの詳細が設定されます。これを使用する

は、ここでは簡単なログデコレータです:

#include <type_traits> 
#include <cmath> 
#include <iostream>  

template<class Fn> 
struct logger 
{ 
    logger(const Fn &fn, const std::string &name) : m_fn(fn), m_name{name}{} 

    template<typename ...Args> 
    auto operator()(Args &&...args) const -> 
     typename std::result_of<Fn(Args...)>::type 
    { 
     std::cout << "entering " << m_name << std::endl; 
     const auto ret = m_fn(std::forward<Args>(args)...); 
     std::cout << "leaving " << m_name << std::endl; 
     return ret; 
    } 

private: 
    Fn m_fn; 
    std::string m_name; 
}; 

template<class Fn> 
logger<Fn> make_log(const Fn &fn, const std::string &name) 
{ 
    return logger<Fn>(fn, name); 
} 

int main() 
{ 
    auto fn = make_log([](double x){return std::sin(x);}, "sin"); 
    std::cout << fn(4.0) << std::endl; 
} 

Hereは、このデコレータのビルドで、hereは再試行デコレータのビルドで、hereは、それらの成分のビルドです。

このアプローチの1つの欠点は、デコレータが機能のシグネチャ、例えば元のメモ化の場合に依存する状態を有する場合である。コンパイル時に対処することが可能です(ビルドhere参照)が、これにはいくつかの欠点があります。その1つは、概念的にコンパイル時に捕捉されたエラーで、実行時に捕捉される可能性があります(タイプ消去不正使用を検出する)。

3

構成可能なデコレータを作成する別の方法は、mixinクラスのセットを使用することです。私は実際に解決し、あなたがそれを言及した問題のかわからないんだけど、変更をお願いすること自由に感じ、私は可能であればそれを更新しようとするでしょう

#include<iostream> 
#include<functional> 
#include<utility> 
#include<type_traits> 

template<class T> 
struct LoggerDecoratorA: public T { 
    template<class U> 
    LoggerDecoratorA(const U &u): T{u} { } 

    template<typename... Args> 
    auto operator()(Args&&... args) const -> 
     typename std::enable_if< 
      not std::is_same< 
       typename std::result_of<T(Args...)>::type, 
       void 
      >::value, 
     typename std::result_of<T(Args...)>::type>::type 
    { 
     using namespace std; 
     cout << "> logger A" << endl; 
     auto ret = T::operator()(std::forward<Args>(args)...); 
     cout << "< logger A" << endl; 
     return ret; 
    } 

    template<typename... Args> 
    auto operator()(Args&&... args) const -> 
     typename std::enable_if< 
      std::is_same< 
       typename std::result_of<T(Args...)>::type, 
       void 
      >::value, 
     typename std::result_of<T(Args...)>::type>::type 
    { 
     using namespace std; 
     cout << "> logger A" << endl; 
     T::operator()(std::forward<Args>(args)...); 
     cout << "< logger A" << endl; 
    } 
}; 

template<class T> 
struct LoggerDecoratorB: public T { 
    template<class U> 
    LoggerDecoratorB(const U &u): T{u} { } 

    template<typename... Args> 
    auto operator()(Args&&... args) const -> 
     typename std::enable_if< 
      not std::is_same< 
       typename std::result_of<T(Args...)>::type, 
       void 
      >::value, 
     typename std::result_of<T(Args...)>::type>::type 
    { 
     using namespace std; 
     cout << "> logger B" << endl; 
     auto ret = T::operator()(std::forward<Args>(args)...); 
     cout << "< logger B" << endl; 
     return ret; 
    } 

    template<typename... Args> 
    auto operator()(Args&&... args) const -> 
     typename std::enable_if< 
      std::is_same< 
       typename std::result_of<T(Args...)>::type, 
       void 
      >::value, 
     typename std::result_of<T(Args...)>::type>::type 
    { 
     using namespace std; 
     cout << "> logger B" << endl; 
     T::operator()(std::forward<Args>(args)...); 
     cout << "< logger B" << endl; 
    } 
}; 

int main() { 
    std::function<int()> fn = [](){ 
     using namespace std; 
     cout << 42 << endl; 
     return 42; 
    }; 

    std::function<void()> vFn = [](){ 
     using namespace std; 
     cout << "void" << endl; 
    }; 

    using namespace std; 

    decltype(fn) aFn = 
     LoggerDecoratorA<decltype(fn)>(fn); 
    aFn(); 

    cout << "---" << endl; 

    decltype(vFn) bVFn = 
     LoggerDecoratorB<decltype(vFn)>(vFn); 
    bVFn(); 

    cout << "---" << endl; 

    decltype(fn) abFn = 
     LoggerDecoratorA<LoggerDecoratorB<decltype(fn)>>(fn); 
    abFn(); 

    cout << "---" << endl; 

    decltype(fn) baFn = 
     LoggerDecoratorB<LoggerDecoratorA<decltype(fn)>>(fn); 
    baFn(); 
} 


それは、最小限、実施例に従います。

+0

答えをありがとう! –

+0

あなたは歓迎です、私はそれがあなたの期待に合うことを願っています。それは機能だけでうまく動作しますが、それはあなたが探していたものでしたか? – skypjack

+0

@AmyTavoryを少し更新しました。 – skypjack

関連する問題