Есть ли способ получить список аргументов вызываемого и затем использовать его в качестве пакета параметров в шаблоне? - PullRequest
0 голосов
/ 19 апреля 2020

вызываемым должен быть любой указатель на функцию, std :: function или lambda. Я хочу получить их список аргументов и использовать их в качестве пакета параметров:

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);
};

Цель состоит в том, чтобы упростить следующие шаблоны, чтобы он работал для всех типов вызываемых элементов без создания псевдонима:

// 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...>;

Мы можем использовать шаблон следующим образом:

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;
}

Результат:

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

Вывод должен работать без явных специализаций параметров шаблона; Я хочу избежать псевдонима; Должен работать с указателем на функцию std :: function и lambda; callable_t должен быть таким, как он задан, вместо того, чтобы приводить их все в std :: function. как следующее:

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);

Ответы [ 2 ]

2 голосов
/ 19 апреля 2020

Вам не нужно выводить параметры вообще. Просто дайте шаблону впитать их и перенаправьте на вызываемый.

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...);
    }
};

Прекрасно работает, даже если operator() перегружен:

int test()
{
    callback_t cc("test", [](auto x, auto y){ return *x + y;}, 42);
    return cc.test_callback(9); // returns 42 + 9 = 51
}
0 голосов
/ 19 апреля 2020

Это был интересный тест =) Хитрость заключалась в том, чтобы передать указатель на lambda::operator() и позволить выводу типа работать:

template <typename Ret, typename ... Args>
struct Callback {
    Callback() {
        std::cout << "Created " << typeid(*this).name() << "\n";
    }
};

template <typename Ret, typename ... Args>
Callback<Ret, Args...> create_callback(std::function<Ret (Args...)> const &fn) {
    std::cout << "Function!\n";
    return Callback<Ret, Args...>();
}

template <typename Ret, typename ... Args>
Callback<Ret, Args...> create_callback(Ret (*fn)(Args...)) {
    std::cout << "Function pointer!\n";
    return Callback<Ret, Args...>();
}

template <typename Lambda, typename Ret, typename ... Args>
Callback<Ret, Args...> create_callback_lambda(Lambda const &, Ret (Lambda::*)(Args...) const) {
    std::cout << "Lambda!\n";
    return Callback<Ret, Args...>();
}

template <typename Callable>
auto create_callback(Callable const &c) {
    return create_callback_lambda(c, &Callable::operator());
}
...