Defer/cancel 执行函数并分析它们的参数

Defer/cancel execution of functions and analyze their arguments

我正在尝试编写外部绘图调用优化,为此我需要收集钩子调用,存储它们的参数以便稍后分析它们。 我已经能够进行延迟调用和一些可读的参数,存储在 tuple 中,但我需要从基础 class 中读取参数,并且在彻底谷歌搜索后我找不到任何适用的东西。 我将主要使用 IDelegateBase 数组,将它们转换为具有完整签名的 Delegate<...> 会非常不方便,因为我通常只会读取一个参数。因此,我需要 IDelegateBase 中的 virtual 模板化方法,这将是 return 第 n 个参数。但是虚拟模板化方法是不可能的,所以我想我可能必须在基 class 中有模板化方法,这将调用非模板 (boost::any?) 虚拟方法并转换它的结果,我想。但是,无论如何,我无法通过运行时变量从 tuple 获取第 n 个元素。

#include <functional>
#include <iostream>

class IDelegateBase
{
public:
    virtual void invoke() { }
};
template <typename T, typename... Args>
class Delegate : public IDelegateBase
{
private:
    void (*_f)(Args...);
    std::tuple<Args...> _args;
public:
    Delegate(T& f, Args &...args)
        : _f(f), _args(args...) { }
    void invoke() final
    {
        std::apply(_f, _args);
    }
};
void a() { std::cout << "a called;\n"; }
void b(int x) { std::cout << "b called with " << x << "\n"; }
void c(int x, float y) { std::cout << "c called with " << x << ", " << y << "\n"; }
int main()
{
    
    IDelegateBase* d = new Delegate(a);
    d->invoke();
    int i = 42;
    d = new Delegate(b, i);
    d->invoke();
    i = 21;
    float f = 0.999;
    d = new Delegate(c, i, f);
    d->invoke();
    // I need something like this:
    auto test = d->getArgument<float>(1);
};

更新: 带有类型检查的最终解决方案:https://godbolt.org/z/xeEWTeosx

您可以提供一个返回 void* 的虚函数并在模板中使用它,但类型安全性会付之东流:如果您 ever 类型错误,你最终会得到未定义的行为。

要使用索引获取元素,您可以使用递归辅助模板,该模板在每次递归调用时与一个索引进行比较。

class IDelegateBase
{
public:
    virtual void invoke() { }

    template<class T>
    T const& getArgument(size_t index) const
    {
        return *static_cast<T const*>(getArgumentHelper(index));
    }
protected:
    virtual void const* getArgumentHelper(size_t index) const = 0;
};

template <typename T, typename... Args>
class Delegate : public IDelegateBase
{
private:
    void (*_f)(Args...);
    std::tuple<Args...> _args;
public:
    Delegate(T& f, Args &...args)
        : _f(f), _args(args...) { }
    void invoke() final
    {
        std::apply(_f, _args);
    }
protected:
    void const* getArgumentHelper(size_t index) const override
    {
        return GetHelper<0>(index, _args);
    }
private:
    template<size_t index>
    static void const* GetHelper(size_t i, std::tuple<Args...> const& args)
    {
        if constexpr (sizeof...(Args) > index)
        {
            if (index == i)
            {
                return &std::get<index>(args);
            }
            else
            {
                return GetHelper<index + 1>(i, args);
            }
        }
        else
        {
            throw std::runtime_error("index out of bounds");
        }
    }
};

这里需要使用if constexpr,因为如果使用超出范围的元组索引,std::get将无法编译。