Варьировать список параметров функции на основе параметра шаблона?

0

Я пытаюсь создать класс шаблона, который выполняет заданную пользователем функцию N -ary с аргументами типа C. Для этого мне нужно каким-то образом указать тип этой функции на основе параметров шаблона. Следующий код иллюстрирует мою проблему:

template <typename C, size_t N>
class NaryDispatch {

    typedef typename std::function<void(/* N parameters of type C& */)> NaryFn;

    public:
        NaryDispatch(NaryFn f) : m_function(std::forward<NaryFn>(f)) {}

    private:
        NaryFn m_function;
};

Мне не удалось найти способ создания типа std :: function с сигнатурой соответствующей арности. Я использую С++ 11 и Boost :: MPL, поэтому решения, связанные с ними, более чем приветствуются. Я попытался использовать вывод параметра SFINAE/шаблона в параметре конструктора следующим образом:

template <
    class... Args,
    typename std::enable_if<sizeof...(Args) == N, C>::type = 0
>
NaryDispatch(std::function<void(Args&...)> fn) : m_function(std::forward<???>(fn)) {}

Как вы можете видеть, проблема здесь в том, что, поскольку я не смог определить тип, который будет выполнять функция, учитывая параметры шаблона C и N, я не могу определить тип члена класса, в котором должна храниться функция.

Чтобы немного упростить мое намерение, для параметров шаблона C и N, конструктор класса должен принять (и сохранить в частном члене) функцию std::function которая возвращает void и принимает N параметров типа C&. Например, следующее должно компилироваться:

NaryDispatch<int, 3> disp([](int a, int b, int c) {});

Заранее благодарим за любые идеи, которые вы могли бы предложить.

Теги:
c++11
templates
boost
template-meta-programming

2 ответа

4
Лучший ответ

Это не должно быть слишком сложно. Начните с верхнего уровня:

template <typename C, std::size_t N>
struct NaryDispatch
{
    // details, see below

    using f_type = typename function_maker<C &, N>::type;

    template <typename F>
    NaryDispatch(F && f) : fn_(std::forward<F>(f)) {}

    f_type fn_;
};

Теперь нам просто нужно реализовать function_maker:

template <typename T, std::size_t K, typename ...Args>
struct function_maker
{
    using type = typename function_maker<T, K - 1, T, Args...>::type;
};

template <typename T, typename ...Args>
struct function_maker<T, 0, Args...>
{
    using type = std::function<void(Args...)>;
};

Наконец, вы также можете предоставить некоторую функцию ограниченных вызовов. Возможно, вот так:

template <typename ...Args,
          typename = typename std::enable_if<sizeof...(Args) == N>::type>
void run(Args &&... args)
{
    fn_(std::forward<Args>(args)...);
}
  • 0
    Измените Args... на Args&... так как он хочет ссылки.
  • 0
    демонстрация
Показать ещё 13 комментариев
0

Следующая проблема будет: "Как передать параметры N в содержащуюся std::function?" Я думаю, что вы могли бы существенно упростить использование шаблона класса диспетчера, который работает с любым старым списком типов параметров:

template <typename...Args>
class Dispatcher {
    typedef typename std::function<void(Args...)> Fn;

    public:
        Dispatcher(Fn f) : m_function(std::move(f)) {}

        void operator()(Args...args) {
          m_function(std::forward<Args>(args)...);
        }

    private:
        Fn m_function;
};

наряду с небольшим метапрограммированием, чтобы вычислить надлежащую специализацию Dispatcher для обработки N параметров типа C&:

template <typename C, size_t N, typename...Args>
struct NaryDispatch_ {
  using type = typename NaryDispatch_<C, N-1, Args..., C&>::type;
};
template <typename C, typename...Args>
struct NaryDispatch_<C, 0, Args...> {
  using type = Dispatcher<Args...>;
};

template <typename C, size_t N>
using NaryDispatch = typename NaryDispatch_<C, N>::type;

ДЕМО НА IDEONE

Ещё вопросы

Сообщество Overcoder
Наверх
Меню