如何在 std::function 上创建一个钩子

How to create a hook on a std::function?

本文关键字:一个 创建 std function      更新时间:2023-10-16

我正在尝试弄清楚如何用"之前"和"之后"钩子装饰std::function

我在找出正确的语法时遇到了一些麻烦。这是我到目前为止所拥有的:

// create a "before" hook
template<typename Functor, typename Hook>
Functor hook_before(const Functor & original, Hook hook)
{
    // not legal, but illustrates what I want to achieve
    template<typename Args ...args> 
    return [=](Args ...args)
    {
        hook();
        original(args...);
    };
}

我的示例应用程序在 Ideone 上。

谁能帮我弄清楚?

你可以这样做:

#include <functional>
#include <iostream>
template <class Hook, class ReturnType, class... ArgType>
std::function<ReturnType(ArgType...)> hook_before(
    const std::function<ReturnType(ArgType...)>& original, 
    Hook hook)
{
    return [=](ArgType... args){
        hook();
        return original(std::move(args)...);
    };
}
int main()
{
    std::function<int(int, int)> sum = [](int a, int b) { return a + b; };
    std::cout << sum(3, 4) << std::endl;
    auto myhook = []() { std::cout << "Calculating sum" << std::endl; };
    auto hooked_sum = hook_before(sum, myhook);
    std::cout << hooked_sum(3, 4) << std::endl;
}

hook_before函数接受两个函子,并返回另一个接受与第一个函子相同的参数(ArgType 参数包),但首先调用hook函子。

这里是(未经测试):

template <typename HOOK, typename RET, typename... ARGS>
struct BeforeHook {
    std::function<RET(ARGS...)> functor;
    HOOK hook;
    BeforeHook(blah) : blah {};
    RET operator()(ARGS&&... args) const {
        hook();
        return functor(args...);
    }
};
template <typename HOOK, typename RET, typename... ARGS>
BeforeHook<HOOK, RET, ARGS...> hook_before(const std::function<RET(ARGS...)> &original, HOOK hook) {
    return BeforeHook<HOOK, RET, ARGS...>(original, hook);
}

用法:

auto hooked = hook_before(original_functor, hook_functor);
hooked(args_for_original_functor); // calls hook_functor, then original_functor

或者类似的东西。original_functor需要可转换为std::function,但几乎所有可调用的东西都是可调用的。这两个函子都需要成本可调用,但如果您愿意,您可以从operator()中删除const

如果您想尝试返回 lambda 而不是 BeforeHook 的实例,请对模板参数 RET...ARGS 使用相同的技巧,并找出是否可以在 lambda 中使用模板参数包:

template <typename HOOK, typename RET, typename... ARGS>
std::function<RET(ARGS...)> hook_before(const std::function<RET(ARGS...)> &original, HOOK hook) {
    return [=](ARGS&&... args) -> RET {
        hook();
        return original(args...);
    };
}

无论哪种方式,我认为关键技巧是在模板参数推导中使用std::function将返回类型与参数分开。

尝试以下操作。

template<typename Functor, typename Hook>
struct BeforeHooked
{
    Functor f;
    Hook hook;
    template<class... Args>
    typename std::result_of<F(Args&&...)>::type
    operator()(Args&&... args)
    {
        hook();
        return f(std::forward<Args&&>(args)...);
    }
};
template<typename Functor, typename Hook>
Functor hook_before(Functor f, Hook hook)
{
    return BeforeHooked{f, hook};
}

代码未经测试,但假设你有一个可以编译它的编译器,我认为它应该做你想要的。与其他答案不同,它可以接受任何函子,而不仅仅是std::function,如果你给它一个多态函子,它仍然是多态的。

相关文章: