C ++函数装饰器

时间:2015-02-13 11:44:36

标签: c++ templates c++11

我正在寻找一种在C ++中装饰函数或lambda的方法。目标是在函数调用之前和之后执行某些操作。正如我所看到的,最接近使用的是std :: function,但它需要具有其参数的类型。

class FunctionDecorator
{
public:
    FunctionDecorator( std::function func )
        : m_func( func )

    void operator()()
    {
        // do some stuff prior to function call

        m_func();

        // do stuff after function call
    }

private:
    std::function m_func;
};

如果我可以在std :: function中使用模板类型,那么当我将指针传递给函数或std :: bind的结果时,它可能会以某种方式推导出它。 在C ++中是否可以这样做。

4 个答案:

答案 0 :(得分:8)

嗯。我可能有也可能没有过度杀伤。

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

template <class T>
struct RetWrapper {
    template <class Tfunc, class... Targs>
    RetWrapper(Tfunc &&func, Targs &&... args)
    : val(std::forward<Tfunc>(func)(std::forward<Targs>(args)...)) {}

    T &&value() { return std::move(val); }

private:
    T val;
};

template <>
struct RetWrapper<void> {
    template <class Tfunc, class... Targs>
    RetWrapper(Tfunc &&func, Targs &&... args) {
        std::forward<Tfunc>(func)(std::forward<Targs>(args)...);
    }

    void value() {}
};

template <class Tfunc, class Tbefore, class Tafter>
auto decorate(Tfunc &&func, Tbefore &&before, Tafter &&after) {
    return [

        func = std::forward<Tfunc>(func),
        before = std::forward<Tbefore>(before),
        after = std::forward<Tafter>(after)

    ] (auto &&... args) -> decltype(auto) {

        before(std::forward<decltype(args)>(args)...);
        RetWrapper<std::result_of_t<Tfunc(decltype(args)...)>> ret(
            func, std::forward<decltype(args)>(args)...
        );
        after(std::forward<decltype(args)>(args)...);

        return ret.value();
    };
}

/*
 * Tests
 */

float test1(float a, float b) {
    std::cout << "Inside test1\n";
    return a * b;
}

void test2() {
    std::cout << "Inside test2\n";
}

int i = 0;

int &test3() {
    return i;
}

int main() {

    auto test1Deco = decorate(
        test1,
        [] (float a, float b) {
            std::cout << "Calling test1 with " << a << " and " << b << '\n';
        },
        [] (float a, float b) {
            std::cout << "Called test1 with " << a << " and " << b << '\n';
        }
    );

    float c = test1Deco(3.5f, 5.1f);

    std::cout << "Yields " << c << '\n';

    auto test2Deco = decorate(
        test2,
        [] () {
            std::cout << "Calling test2\n";
        },
        [] () {
            std::cout << "Called test2\n";
        }
    );

    test2Deco();

    auto test3Deco = decorate(
        test3,
        [] () {
            std::cout << "Calling test3\n";
        },
        [] () {
            std::cout << "Called test3\n";
        }
    );

    auto &i2 = test3Deco();
    i2 = 42;

    std::cout << "Global i = " << i << '\n';

    return 0;
}

输出:

Calling test1 with 3.5 and 5.1
Inside test1
Called test1 with 3.5 and 5.1
Yields 17.85
Calling test2
Inside test2
Called test2
Calling test3
Called test3
Global i = 42

答案 1 :(得分:6)

只需要完整的模板,没有std :: function:

template< typename Func >
class FunctionDecorator
{
public:
  FunctionDecorator( Func func )
    : m_func( std::move(func) )

  void operator()()
  {
    // do some stuff prior to function call
    m_func();
    // do stuff after function call
  }

private:
  Func m_func;
};

template< typename Func >
FunctionDecorator<Func> decorate(Func func) {
  return FunctionDecorator<Func>(std::move(func));
}

答案 2 :(得分:0)

[注意:在首次发布后几个小时进行了编辑]

这或许不是OP正在寻找的东西,但它仍然具有相关性,并希望对寻求答案的其他人有用。

假设你有几个功能略有不同的签名:

void foo1(int& x){ cout << "foo1(" << x << ")\n";}
void foo2(double& x){ cout << "foo2(" << x << ")\n";}

并且您希望将装饰器包裹在它们周围以便标准化它们的签名,例如将它们都转换为void (*)(int)

然后你可以做到以下几点:

template<typename Q, void (*foo_p)(Q&)>
void wrapped(int x){
  Q v = 42.2 + x;
  foo_p(v);  
}

int main(){
   using foo_t = void (*)(int); // we coerce foo1 and foo2 into this type 

   foo_t k_int = wrapped<int, foo1>;
   foo_t k_double = wrapped<double, foo2>;

   k_int(-1); //cout: foo1(41)
   k_double(-1); //cout: foo2(41.2)   
   return 0;
}

使用我在这里给出的示例main,clang概述了整个事情,这是一个好兆头,但不是我们想要检查的内容。如果您使示例更复杂(see live here),您可以看到它确实内联每个包装器中的所有内容,即foo1foo2不以独立形式存在,只能以包裹的形式。

最初,除了wrapped<...>模板之外我还使用了lambda(利用了带有no-capture的lambdas可以转换为函数指针这一事实)但后来我意识到额外的包装在这里是多余的案件。

此方法适用于传递运行时已知的任何内容,甚至可能包含指向可变全局的指针(尽管它变得非常混乱)。

答案 3 :(得分:0)

    #include  <iostream>
    #include <string>
    using namespace std;

    template <typename TResult, typename TParams>
    class CClassGenerique
    {
        typedef TResult (*uneFonction) (TParams);
    public :
        CClassGenerique (uneFonction f){ m_function = f; }
        void operator () (TParams t)    {  m_function (t); }
private :    
        uneFonction m_function;
    };

    template <typename TResult, typename TParams>
    TResult BasicFunction (TParams p)
    {
        TResult t=0;
        std::cout<<" Value = " << p <<endl;
        return t;
    }

    int main (int argc, char* argv[])
    {

        CClassGenerique<int, int>       c1 (BasicFunction<int, int>);
        CClassGenerique<int, char*>     c2 (BasicFunction<int, char*>);
        CClassGenerique<char*, char*>   c3 (BasicFunction<char*, char*>);


        c1(3);
        c2("bonsoir");
        c3("hello");
        return 0;
    }