替换(或重新实现?)std::function 进行一些统计和测试

Posted

技术标签:

【中文标题】替换(或重新实现?)std::function 进行一些统计和测试【英文标题】:Replacing (or re-implementing?) std::function for some statistics and testing 【发布时间】:2019-04-18 08:36:36 【问题描述】:

我有一个特殊情况,我想测量关于我的 std::function 使用情况的统计信息。通常诸如花费的时间、调用的数量、经常调用哪个函数以及有时用于测试之类的东西,比如随机抛出 nullptr 以检查我的代码如何处理它。

首先,我考虑从 std::func 继承,然后重写检索指针的函数 - 或类似的东西 - 但在阅读了几个主题和指南后,我得出结论,这是非常不明智的。

我的第二个想法是简单地创建一个包装类。重构我的整个代码以使用这个包装器而不是 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,只需简单地初始化一次,必要时一直调用它。因此,如果它对您有所帮助,您可以考虑这一点并忽略诸如复制构造函数之类的其他东西(当然,自定义实现不缺少的功能越多越好,但就我个人而言,我提到的已经足够了) em>


我不知道如何继续,所以我什至没有附上任何源代码。

【问题讨论】:

【参考方案1】:

你可能会这样做:

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; 
    // ...
;

Demo

【讨论】:

我在编译您的源代码时苦苦挣扎了很长时间,当时我意识到它似乎只适用于 clang。 GCC 和 VC 在函数定义中的 __stdcall 参数存在问题。您知道该问题的任何解决方法吗? @Unc3nZureD:我修正了一些错字,并提供了链接演示。所以在 gcc/clang 上工作。 你是否明确使用__stdcall 我动态链接到的 Windows API 正在使用 stdcall,因此我无法更改它们。以下是我想使用它的方式:godbolt.org/z/X9SLWX我是不是试错了? 使用正确的参数:Demo。当我使用转发引用时,NULL0 所以 int (而某些参数需要指针)。专业化可能允许模仿std::function 的行为,但需要额外的复制/移动【参考方案2】:

我认为这是您可以用来创建包装器的想法:

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::functions - 最好的办法是摆脱std::function 本身以提高性能。如果可能,请考虑切换到功能对象。

【讨论】:

注意模板构造函数,CustomWrapper(CustomWrapper&amp;) 会匹配您的模板导致无效代码。 @Jarod42,是的,复制和移动构造函数应该分开实现 我的意思是应该保护模板构造函数,或者你必须提供复制/移动构造函数的所有变体(常规C(const C&amp;); C(C&amp;&amp;),但也有C(C&amp;); C(const C&amp;&amp;)(我忽略volatile 情况)。 我刚刚尝试过,但未能编译动态导入:godbolt.org/z/vNo4zv - 可能缺少一个构造函数,但我还没有找到确切的内容:) @Unc3nZureD,看起来问题出在 __stdcall 上。 MSVS 标准库中的 std::function 以某种方式存储调用约定,而我的实现 - 不是【参考方案3】:

您希望在您的程序发布前不断检查统计信息还是只检查一次?如果只有一次,我会建议使用分析器/覆盖工具来查看函数被引用了多少次。

如果您需要恒定的统计数据,您需要查看 std::function 接口 (https://en.cppreference.com/w/cpp/utility/functional/function) 并为此创建新的实现 - 实现起来并不难。

【讨论】:

对于一些特殊的构建,我想持续监控它:) 所以重写对我来说似乎是唯一合理的解决方案:(

以上是关于替换(或重新实现?)std::function 进行一些统计和测试的主要内容,如果未能解决你的问题,请参考以下文章

模板函数替换仅在一个参数中未使用std :: function时才起作用

第12课 std::bind和std::function_std::function可调用对象包装器

包装器和绑定器std::bind和std::function的回调技术

包装器和绑定器std::bind和std::function的回调技术

C++11新特性应用--实现延时求值(std::function和std::bind)

C++11之用std::function和std::bind实现观察者模式