I'm trying to write external draw call optimization, and for that I need to collect hooked calls, store their arguments to analyze them later on.
I've been able to make deferred calls, and somewhat readable arguments, stored in tuple
, but I need to read arguments from base class and after thorough googling I can't find anything applicable.
I'll work with array of IDelegateBase
mostly, and it would be very inconvenient to convert them to Delegate<...>
with full signature, when I mostly would read just one argument. Therefore, I need virtual
templated method in IDelegateBase
, which would return n-th argument. But virtual templated methods are impossible, so probably I'd have to have templated method in base class, which would call non-template (boost::any
?) virtual method and cast it's result, I suppose. But, anyway, I can't get n-th element from tuple
via runtime variable.
#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);
};
CodePudding user response:
You could provide a virtual function returning void*
and use it in a template, but type safety goes down the drain: Should you ever get the type wrong, you'll end up with undefined behaviour.
For getting element using an index you can use a recursive helper template that compares with one index per recursive call.
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");
}
}
};
The use of if constexpr
is needed here, since std::get
does not compile if a of tuple index out of bounds is used.