Finally, we need to modify the delegate to add support for non-member functions. Check out this example:
bool attack(int x, int y)
{
return x == 42 && y == 42 ? true : false;
}
To do this, we simply need to add another wrapper as follows:
template<
typename RET,
typename... ARGS
>
class fun_wrapper :
public base<RET, ARGS...>
{
RET (*m_func)(ARGS...);
public:
fun_wrapper(RET (*func)(ARGS...)) :
m_func{func}
{ }
RET func(ARGS... args) override
{
return m_func(args...);
}
};
As shown in the preceding, as with our original wrapper, we store a pointer to the function we wish to call, but in this case, we do not need to store a pointer to an object as there is no object (as this is a non-member function wrapper). To use this new wrapper, we must add another delegate constructor as follows:
delegate(RET (func)(ARGS...)) :
m_wrapper{
std::make_unique<fun_wrapper<RET, ARGS...>>(func)
}
{ }
This means we must also provide another user-defined type deduction guide as follows:
template<
typename RET,
typename... ARGS
>
delegate(RET(*)(ARGS...)) -> delegate<RET(ARGS...)>;
With all of the modifications, we are finally able to use our delegate as defined at the beginning of this recipe:
int main(void)
{
spiderman s;
captain_america c;
std::array<delegate<bool(int, int)>, 3> heros {
delegate(attack),
delegate(&s, &spiderman::attack),
delegate(&c, &captain_america::attack)
};
for (auto &h : heros) {
std::cout << h(0, 42) << ' ';
}
return 0;
}
When this is executed, we get the following output:
This delegate could further be extended to support lambda functions by adding yet another set of wrappers, and the need for std::unique_pointer in the delegate could be removed in favor of a placement new, using a small buffer the size of the member function wrapper (or, in other words, removing the dynamic memory allocation), which is sometimes referred to as a small size optimization.