替换(或重新实现?)std::function 用于一些统计和测试
Replacing (or re-implementing?) std::function for some statistics and testing
我有一个特例,我想测量有关 std::function 使用情况的统计信息。通常像花费的时间、调用量、经常调用哪个函数,有时也用于测试,比如随机抛出 nullptr 来检查我的代码如何处理它。
首先,我考虑从 std::func 继承,然后覆盖检索指针的函数 - 或类似这样的东西 - 但在阅读了几个主题和指南后,我得出的结论是非常不明智的。
我的第二个想法是简单地创建一个包装器 class。重构我的整个代码以使用此包装器而不是 std::function 并不难,所以没关系。我只想避免使用不必要的和不舒服的用法,就像任何额外的功能一样。例如:
std::function<...> dummyvar (...);
// Later I can call this dummy var as simply as dummyvar(...)
// I don't want to add extra complexity, like this:
CustomWrapper<...> customdummy (...);
customdummy.getVal()(...);
// Best would be if I could actually "simply" replace all std::function to the Custom Wrapper.
在我当前非常简单的情况下,不会发生 std::function 的复制,只是简单地初始化一次,必要时一直调用它。因此,如果它对您有帮助,您可以考虑这一点并忽略其他东西,例如复制构造函数等(当然,自定义实现不缺少的功能越多越好,但就我个人而言,我提到的已经足够了)
我不知道如何继续,这就是为什么我什至没有附上任何源代码的原因。
您希望在您的程序发布之前不断检查统计数据还是只检查一次?如果只有一次我会推荐使用 profiler/coverage 工具来查看函数被引用了多少次。
如果您需要持续的统计数据,您需要查看 std::function 接口 (https://en.cppreference.com/w/cpp/utility/functional/function) 并为此创建新的实现 - 实现起来并不难。
我认为这是您可以用来创建包装器的想法:
template <typename T>
class CustomWrapper;
template <typename Result, typename ...Args>
class CustomWrapper<Result(Args...)>
{
public:
template <typename ...SomeArgs>
CustomWrapper(SomeArgs&&... args)
: func(std::forward<SomeArgs>(args)...)
{
}
CustomWrapper(const CustomWrapper&) = default;
CustomWrapper(CustomWrapper&&) = default;
auto operator()(Args&&... args)
{
return func(std::forward<Args>(args)...);
}
private:
std::function<Result(Args...)> func;
};
void foo()
{
CustomWrapper<void(int, int)> func([](int x1, int x2) {});
func(1, 2);
}
我没有实现所有的方法,但是以这个作为示例添加它们非常容易。
但我还想提一下,如果您经常调用 std::function
s - 最好的办法是摆脱 std::function
本身以提高性能。如果可能,请考虑切换到功能对象。
你可以这样做:
template <typename Sig>
class CustomWrapper
{
private:
std::function<Sig> f;
mutable std::size_t nbCall = 0;
// ...
public:
CustomWrapper() = default;
CustomWrapper(const CustomWrapper&) = default;
CustomWrapper(CustomWrapper&&) = default;
CustomWrapper& operator=(const CustomWrapper&) = default;
CustomWrapper& operator=(CustomWrapper&&) = default;
template <typename T,
std::enable_if_t<!std::is_same<CustomWrapper, std::decay_t<T>>::value
&& std::is_constructible<std::function<Sig>, T&&>::value, bool> = false>
CustomWrapper(T&& arg) : f(std::forward<T>(arg)){}
template <typename ... Ts,
std::enable_if_t<(sizeof...(Ts) >= 2)
&& std::is_constructible<std::function<Sig>, Ts&&...>::value, bool> = false>
CustomWrapper(Ts&& ... args) : f(std::forward<Ts>(args)...){}
template <typename ... Ts>
auto operator ()(Ts&&... args) const -> decltype(f(std::forward<Ts>(args)...))
{
++nbCall; // Statistics you want
// ...
return f(std::forward<Ts>(args)...);
}
std::size_t getNbCall() const { return nbCall; }
// ...
};
我有一个特例,我想测量有关 std::function 使用情况的统计信息。通常像花费的时间、调用量、经常调用哪个函数,有时也用于测试,比如随机抛出 nullptr 来检查我的代码如何处理它。
首先,我考虑从 std::func 继承,然后覆盖检索指针的函数 - 或类似这样的东西 - 但在阅读了几个主题和指南后,我得出的结论是非常不明智的。
我的第二个想法是简单地创建一个包装器 class。重构我的整个代码以使用此包装器而不是 std::function 并不难,所以没关系。我只想避免使用不必要的和不舒服的用法,就像任何额外的功能一样。例如:
std::function<...> dummyvar (...);
// Later I can call this dummy var as simply as dummyvar(...)
// I don't want to add extra complexity, like this:
CustomWrapper<...> customdummy (...);
customdummy.getVal()(...);
// Best would be if I could actually "simply" replace all std::function to the Custom Wrapper.
在我当前非常简单的情况下,不会发生 std::function 的复制,只是简单地初始化一次,必要时一直调用它。因此,如果它对您有帮助,您可以考虑这一点并忽略其他东西,例如复制构造函数等(当然,自定义实现不缺少的功能越多越好,但就我个人而言,我提到的已经足够了)
我不知道如何继续,这就是为什么我什至没有附上任何源代码的原因。
您希望在您的程序发布之前不断检查统计数据还是只检查一次?如果只有一次我会推荐使用 profiler/coverage 工具来查看函数被引用了多少次。
如果您需要持续的统计数据,您需要查看 std::function 接口 (https://en.cppreference.com/w/cpp/utility/functional/function) 并为此创建新的实现 - 实现起来并不难。
我认为这是您可以用来创建包装器的想法:
template <typename T>
class CustomWrapper;
template <typename Result, typename ...Args>
class CustomWrapper<Result(Args...)>
{
public:
template <typename ...SomeArgs>
CustomWrapper(SomeArgs&&... args)
: func(std::forward<SomeArgs>(args)...)
{
}
CustomWrapper(const CustomWrapper&) = default;
CustomWrapper(CustomWrapper&&) = default;
auto operator()(Args&&... args)
{
return func(std::forward<Args>(args)...);
}
private:
std::function<Result(Args...)> func;
};
void foo()
{
CustomWrapper<void(int, int)> func([](int x1, int x2) {});
func(1, 2);
}
我没有实现所有的方法,但是以这个作为示例添加它们非常容易。
但我还想提一下,如果您经常调用 std::function
s - 最好的办法是摆脱 std::function
本身以提高性能。如果可能,请考虑切换到功能对象。
你可以这样做:
template <typename Sig>
class CustomWrapper
{
private:
std::function<Sig> f;
mutable std::size_t nbCall = 0;
// ...
public:
CustomWrapper() = default;
CustomWrapper(const CustomWrapper&) = default;
CustomWrapper(CustomWrapper&&) = default;
CustomWrapper& operator=(const CustomWrapper&) = default;
CustomWrapper& operator=(CustomWrapper&&) = default;
template <typename T,
std::enable_if_t<!std::is_same<CustomWrapper, std::decay_t<T>>::value
&& std::is_constructible<std::function<Sig>, T&&>::value, bool> = false>
CustomWrapper(T&& arg) : f(std::forward<T>(arg)){}
template <typename ... Ts,
std::enable_if_t<(sizeof...(Ts) >= 2)
&& std::is_constructible<std::function<Sig>, Ts&&...>::value, bool> = false>
CustomWrapper(Ts&& ... args) : f(std::forward<Ts>(args)...){}
template <typename ... Ts>
auto operator ()(Ts&&... args) const -> decltype(f(std::forward<Ts>(args)...))
{
++nbCall; // Statistics you want
// ...
return f(std::forward<Ts>(args)...);
}
std::size_t getNbCall() const { return nbCall; }
// ...
};