callable should be any function pointer, std::function or lambda. I want to obtain their argument list and use them as parameter pack:
template <typename callable_T>
class callback2_t
{
public:
using callable_t = callable_T;
using ret_T = some_magic<callable_T>::ret_t;
using data_T = the_first_type<argTs>;
...
static ret_T _callback(some_magic<callable_T>::argTs... args);
};
The purpose is to simplify follow templates to make it work for all kind of callable without creating alias:
// other library only accept function pointer as callback, I want to wrap it to remove the callback when data go out of scope.
template <typename callable_T, typename ret_T, typename data_T, typename ...arg_Ts>
class callback_t
{
using callable_t = callable_T;
public:
callback_t(const char* signal, callable_t callable, data_T data)
: f_{std::move(callable)}, data_{std::move(data)}
{
std::cout << signal << " " << typeid(callable).name() << std::endl;
//register_callback(signal, _callback, this);
}
~callback_t()
{
//unregister_callback(signal);
}
void test_callback(arg_Ts... args)
{
_callback(args..., this);
}
private:
callable_t f_;
data_T data_;
static ret_T _callback(arg_Ts... args, callback_t * self)
{
return self->f_(&self->data_, args...);
}
};
// I donot want convert function pointer to std::function
// if possible. std::function is a heavy class.
template <typename ret_T, typename data_T, typename ...arg_Ts>
using fp_callback_t = callback_t<ret_T(*)(void *, arg_Ts...), ret_T, data_T, arg_Ts...>;
template <typename ret_T, typename data_T, typename ...arg_Ts>
using func_callback_t = callback_t<std::function<ret_T(void *, arg_Ts...)>, ret_T, data_T, arg_Ts...>;
We can use the template like this:
struct A{float x;};
struct B{int x;};
struct C{uint x;};
int func1(void * data, A* a)
{
auto c = reinterpret_cast<C*>(data);
std::cout<< a->x << ", " << c->x << std::endl;
return a->x + c->x;
}
void func2(void *data, B* b, C* c)
{
auto a = reinterpret_cast<A*>(data);
std::cout << b->x << ", " << c->x << ", " << a->x << std::endl;
}
int main()
{
A a1{-10.5f};
B b1 {5};
C c1{300};
auto callback1 = fp_callback_t<int, C, A*>("signal1", &func1, c1);
callback1.test_callback(&a1);
auto callback2 = fp_callback_t<void, A, B*, C*>("signal2", &func2, a1);
callback2.test_callback(&b1, &c1);
std::function<int(void*, A*)> fc1 = [=](void* data, A* a){
auto c = reinterpret_cast<C*>(data);
std::cout<< a->x << ", " << c->x << ", " << a1.x << std::endl;
return (int)a1.x;
};
std::function<void(void*, B*, C*)> fc2 = [=](void* data, B* b, C* c){
auto a = reinterpret_cast<A*>(data);
std::cout << b->x << ", " << c->x << ", " << a->x << ", " << c1.x << std::endl;
};
auto callback3 = func_callback_t<int, C, A*>("signal3", fc1, c1);
callback3.test_callback(&a1);
auto callback4 = func_callback_t<void, A, B*, C*>("signal4", fc2, a1);
callback4.test_callback(&b1, &c1);
return 0;
}
The out put is:
signal1 PFiPvP1AE
-10.5, 300
signal2 PFvPvP1BP1CE
5, 300, -10.5
signal3 NSt3__18functionIFiPvP1AEEE
-10.5, 300, -10.5
signal4 NSt3__18functionIFvPvP1BP1CEEE
5, 300, -10.5, 300
The deduction should work without specialize template parameters explicitly; I want to avoid alias; It should work with function pointer, std::function and lambda; the callable_t should be as it is given instead of casting all of them to std::function. like following:
auto callback1 = callback2_t("signal1", &func1, c1);
callback1.test_callback(&a1);
auto callback2 = callback2_t("signal2", &func2, a1);
callback2.test_callback(&b1, &c1);
std::function<int(void*, A*)> fc1 = [=](void* data, A* a){
auto c = reinterpret_cast<C*>(data);
std::cout<< a->x << ", " << c->x << ", " << a1.x << std::endl;
return (int)a1.x;
};
auto callback3 = callback2_t("signal3", fc1, c1);
callback3.test_callback(&a1);
auto lambda1 = [=](void* data, B* b, C* c){
auto a = reinterpret_cast<A*>(data);
std::cout << b->x << ", " << c->x << ", " << a->x << ", " << c1.x << std::endl;
};
auto callback4 = callback2_t("signal4", lambda1, a1);
callback4.test_callback(&b1, &c1);
You don't need to deduce the parameters at all. Just let the template soak them up and forward them to the callable.
template <typename callable_T, typename data_T>
class callback_t
{
using callable_t = callable_T;
public:
callback_t(const char* signal, callable_t callable, data_T data)
: f_{std::move(callable)}, data_{std::move(data)}
{
}
template<typename...arg_Ts>
auto test_callback(arg_Ts... args)
{
return _callback(this, args...);
}
private:
callable_t f_;
data_T data_;
template<typename...arg_Ts>
static auto _callback(callback_t * self, arg_Ts... args)
{
return self->f_(&self->data_, args...);
}
};
Works great even if operator()
is overloaded:
int test()
{
callback_t cc("test", [](auto x, auto y){ return *x + y;}, 42);
return cc.test_callback(9); // returns 42 + 9 = 51
}