我正在将Python指标库移植到C++.Python库提供的一个API是一个函数修饰器,可以轻松地在函数上记录时序数据.通过修改函数定义来实现
@timed('timing.foo')
def foo():
...
Run Code Online (Sandbox Code Playgroud)
foo_result = foo() 基本上变成了
start = time.time()
foo_result = foo()
post_metric_to_endpoint('timing.foo', time.time() - start)
Run Code Online (Sandbox Code Playgroud)
在C++中的函数挂钩中,我们发现这个包装实例并将调用定时函数的负担放在调用者身上的答案,这意味着我们不会在代码库中获得性能数据(首先更新很烦人,而且很容易后来被遗忘了).同样,这个答案要定时在C++中一种优雅的方式需要改变调用点. 这个问题的另一个答案提供了一种包装任意代码块的方法,理论上这意味着我们可以缩进我们想要计时的函数的整个主体并将其嵌套在一个范围内.这是我发现的最接近我想要的东西,但是非常难看,相当干扰,而且我不确定性能影响.
由于这是用作库,我们可以修改我们想要的函数的来源; 事实上,这是更可取的,因为我希望每次调用该函数的时间都是定时的.大多数讨论似乎都侧重于开发中的临时分析,而我正在尝试构建一个在生产环境中始终处于活动状态的系统.
虽然C++没有对装饰器的显式语言支持,但事实证明你可以使用C++ 14泛型lambda非常好地"模拟"它们.这是我的看法:
#include <iostream>
template<class T>
auto decorator(T&& func)
{
// we create a closure below
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END decorating\n";
return result;
};
return new_function;
}
int f(int x)
{
std::cout << x * x << '\n';
return x * x;
}
auto decorated = decorator(f);
auto double_decorated = decorator(decorator(f));
int main()
{
decorated(5);
double_decorated(10);
}
Run Code Online (Sandbox Code Playgroud)
当然,在装饰器内你可以添加你想要的任何东西(包括时间等),上面只是一个最小的例子.如果它看起来太令人生畏,你可以忽略mumbo-jumbo std::forward和C++ 14广义lambda捕获并简单地拥有
#include <iostream>
template<class T>
auto decorator(T func)
{
// we create a closure below
auto new_function = [func](auto... args)
{
std::cout << "BEGIN decorating...\n";
auto result = func(args...);
std::cout << "END decorating\n";
return result;
};
return new_function;
}
int f(int x)
{
std::cout << x * x << '\n';
return x * x;
}
auto decorated = decorator(f);
auto double_decorated = decorator(decorator(f));
int main()
{
decorated(5);
double_decorated(10);
}
Run Code Online (Sandbox Code Playgroud)