C++函数装饰器
C++ function decorator
我正在寻找一种在 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 中使用 by template type 就好了,并且当我将指针传递给函数或 std::bind 的结果时它可以以某种方式推断出它。
在C++中有可能吗?
直接使用完整模板,没有 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));
}
嗯。我可能会也可能不会矫枉过正。
#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 static_cast<T &&>(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
[注意: 在初始发布后几个小时编辑]
这可能不完全是 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),您会发现它确实在每个包装器中内联了所有内容,即 foo1
和 foo2
不以独立形式存在,仅包装形式。
最初,除了 wrapped<...>
模板之外,我还使用了一个 lambda(利用没有捕获的 lambda 可以转换为函数指针这一事实)但后来我意识到额外的包装是多余的在这种情况下。
此方法应该适用于传递 运行 时已知的任何内容,甚至可以包括指向可变全局变量的指针(尽管这变得相当混乱)。
#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;
}
我正在寻找一种在 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 中使用 by template type 就好了,并且当我将指针传递给函数或 std::bind 的结果时它可以以某种方式推断出它。 在C++中有可能吗?
直接使用完整模板,没有 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));
}
嗯。我可能会也可能不会矫枉过正。
#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 static_cast<T &&>(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
[注意: 在初始发布后几个小时编辑]
这可能不完全是 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),您会发现它确实在每个包装器中内联了所有内容,即 foo1
和 foo2
不以独立形式存在,仅包装形式。
最初,除了 wrapped<...>
模板之外,我还使用了一个 lambda(利用没有捕获的 lambda 可以转换为函数指针这一事实)但后来我意识到额外的包装是多余的在这种情况下。
此方法应该适用于传递 运行 时已知的任何内容,甚至可以包括指向可变全局变量的指针(尽管这变得相当混乱)。
#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;
}