Сохранить и передать функции список аргументов шаблона в C

#c #templates #callback #variadic-templates

#c #шаблоны #обратный вызов #переменные-шаблоны

Вопрос:

Я хочу сохранить и передать список аргументов шаблона функции.
Like std::thread передает аргументы потоку. Типы аргументов являются шаблонными, а количество аргументов не является статическим.

Пример, как это будет работать:

 class CallbackList {
public:
    Callback(/* Type of list of template args */ args) {
        this->saved_args = args;
    }


    void Call() {
        this->callback(saved_args);
    }
private:
    /* Type of list of template args */ saved_args;

    CallbackType callback;
}   
  

Или как я могу это реализовать:

 template<typename ...Args>
class CallbackList {
public:
    using CallbackPrototype = /* some prototype */;

    void RegisterCallback(CallbackPrototype callback, Args... args) {
        CallbackInfo callback_info;
        callback_info.callback = callback;
        callback_info.args = { args... };
        this->callbacks.push_back(callback_info);
    }

    void Call() {
        for (CallbackInfoamp; callback_info : this->callbacks)
            callback_info.callback(callback_info.args);
    }

private:
    struct CallbackInfo {
        CallbackPrototype callback;
        /* what type should be here? tuple? args count are not static */ args;
    };

    std::vector<CallbackInfo> callbacks;
}   
  

Это возможно?
Как я могу это реализовать?

Комментарии:

1. Вы согласны с созданием Callback шаблона класса или собираетесь хранить разные объекты обратного вызова в одном контейнере?

2. вы ищете переменные шаблоны. Это широкая тема, я предлагаю вам взглянуть на некоторые примеры, например, здесь: en.cppreference.com/w/cpp/language/parameter_pack

3. Каков фактический callback набор? Здесь он никогда не инициализируется.

4. @NathanOliver, я могу сделать класс шаблонным @AndyG, это пример, callback поле будет инициализировано, я также добавил второй пример, ближе к моей реальной задаче

Ответ №1:

Если вы не хотите, чтобы ваш обратный вызов зависел от типов аргументов, вы должны использовать какое-то удаление типов. Вы можете, например, использовать std::function из <functional> :

 #include <functional>
#include <iostream>


class Lazy_Callback
{
public:
  template <typename F, typename ...Args>
  Lazy_Callback(F amp;amp; f, Args amp;amp; ...args)
  : _fun([=]() { return f(args...); })
  { }
    
  void call() const
  {
    _fun();
  }
protected:
private:
  std::function<void()> _fun;
};

void print_int(int x)
{
  std::cout << "x = " << x << "n";
}

int main()
{
  Lazy_Callback lc(print_int, 5);
  
  lc.call();
}
  

Если обратный вызов может быть шаблонным, вы можете использовать std::tuple для хранения своих аргументов:

 #include <tuple>
#include <iostream>



template <typename F, typename ...Args>
class Lazy_Callback
{
public:
  template <typename ...Ts>
  Lazy_Callback(F f, Ts amp;amp; ...ts)
  : _f(f), _args(ts...)
  { }
  
  void call() const
  {
    return std::apply(_f, _args);
  }
  
protected:
private:
  F _f;
  std::tuple<Args...> _args;
};



template <typename F, typename ...Ts>
Lazy_Callback<F, std::decay_t<Ts>...> make_callback(F amp;amp; f, Ts amp;amp; ...ts)
{
  return { std::forward<F>(f), std::forward<Ts>(ts)... };
}

void print_int(int x)
{
  std::cout << "x = " << x << "n";
}

int main()
{
  auto lc = make_callback(print_int, 5);
  
  lc.call();
}
  

Ответ №2:

Вы ищете что-то вроде std::bind ? Вот простой пример, который вы, вероятно, могли бы расширить:

 #include <iostream>
#include <functional>

template <typename T1, typename T2>
void printSum(const T1amp; a, const T2amp; b)
{
    std::cout << a   b << std::endl;
}

int main()
{
    const auto callback = std::bind(amp;printSum<int, int>, 1, 2);

    // ...
    
    callback();
}
  

Комментарии:

1. Я добавил второй пример, ближе к моей реальной проблеме. Он должен быть динамически сохранен в памяти. Это возможно?