Частично специализируются методы вариационных шаблонных классов.

Я хочу написать unmarshaller для извлечения аргументов, хранящихся в массиве msgpack для отдельных аргументов для вызова sigc::signal::emit(...), Я попробовал это:

template<class... T> class MsgpackAdapter: public MsgpackAdapterBase {
public:
MsgpackAdapter (sigc::signal<void, T...> &sig)
: MsgpackAdapterBase (), signal_ (sig)
{}
protected:
virtual void do_emit (const msgpack::object_array &mp_args) override;
private:
sigc::signal<void, T...> signal_;
};

template<class T1>
void MsgpackAdapter<T1>::do_emit (const msgpack::object_array &mp_args)
{
T1 a1;
mp_args.ptr[0].convert (a1);
signal_.emit (a1);
}

template<class T1, class T2>
void MsgpackAdapter<T1, T2>::do_emit (const msgpack::object_array &mp_args)
{
T1 a1;
T2 a2;
mp_args.ptr[0].convert (a1);
mp_args.ptr[1].convert (a2);
signal_.emit (a1, a2);
}

и так далее до 4 аргументов. Но я получаю это сообщение об ошибке (из vim-youcompleteme, используя clang 3.9):

'MsgpackAdapter<T1>::' for declaration does not refer into a class, class template or class template partial specialization

Кажется, я могу сделать частичные специализации всего класса:

template<class T1> class MsgpackAdapter<T1>: public MsgpackAdapterBase { ... };

но я бы предпочел специализироваться только на emit способ уменьшить количество копий & вставьте код. Я что-то упустил очевидное? Я думаю, что основная трудность заключается в том, что do_emit не принимает шаблонные аргументы.

Другое любопытство заключается в том, что если я попытаюсь сделать это без шаблона variadic, и использовать:

template<class T1> class MsgpackAdapter: public MsgpackAdapterBase { ... };
template<class T1, class T2> class MsgpackAdapter: public MsgpackAdapterBase { ... };

Я получаю ошибку, что определение второго класса конфликтует с первым. Это понятно, но мне интересно, как sigc справился с подобной вещью без шаблонов с переменным числом аргументов.

0

Решение

Я всегда чувствовал, что наиболее приемлемый способ специализировать функцию-член — это отнести к специализированному объекту функции:

#include <iostream>

struct object_array
{
};template<class...Ts>
struct implement_do_emit
{
template<class This>
void operator ()(This *that, const object_array& arg) const;
};template<class... Ts>
struct MsgpackAdapter
{
friend class implement_do_emit<Ts...>;

virtual void do_emit(const object_array& mp_args)
{
auto impl = implement_do_emit<Ts...>();
impl(this, mp_args);
}

};

template<class T>
struct implement_do_emit<T>
{
template<class This>
void operator ()(This *that, const object_array& arg) const
{
std::cout << "one type version\n";
}
};

template<class T1, class T2>
struct implement_do_emit<T1, T2>
{
template<class This>
void operator ()(This *that, const object_array& arg) const
{
std::cout << "two type version\n";
}
};int main()
{
MsgpackAdapter<int> pi {};
pi.do_emit(object_array());

MsgpackAdapter<int, int> pii {};
pii.do_emit(object_array());
}
2

Другие решения

В C ++ 14:

template<class F>
auto foreach( F&& f ) {
return [f=std::forward<F>(f)](auto&&...args)mutable{
using discard=int[];
(void)discard{0,(void(
f(decltype(args)(args))
),0)...};
};
}
template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f)->decltype(auto){
return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}template<class...Ts>
void MsgpackAdapter<Ts...>::do_emit (const msgpack::object_array &mp_args)
{
std::tuple<Ts...> args;
index_upto<sizeof...(Ts)>()(
foreach(
[&](auto I){
mp_args.ptr[I].convert(std::get<I>(args));
}
)
);
index_upto<sizeof...(Ts)>()(
[&](auto...Is){
signal_.emit(std::get<Is>(args)...);
}
);
}

или что-то подобное. Живой пример.

В основном, сделать кортеж.

Создайте пакет индексов в этом кортеже.

Для каждого индекса в кортеже вызовите convert.

Затем вызовите emit для получения каждого элемента кортежа.

Есть много примеров кода при переполнении стека, которые включают передачу каждого аргумента кортежа в вызов функции. Это часть излучения.

Существует много примеров переполнения стека при выполнении чего-либо для каждого элемента кортежа. Делать это с индексом немного сложнее, но в худшем случае вы можете посчитать, если для каждого элемента все в порядке.

Это может быть сделано в C ++ 11, но в C ++ 14 я могу сделать все это в функции без вспомогательных функций.


Описание приведенного выше магического кода. index_upto возвращает лямбду Эта лямбда принимает другую лямбду, затем вызывает ее с константами времени компиляции от 0 до N-1. Это делается путем вызова index_over, который принимает список индексов.

foreach принимает лямбда ф. Затем он возвращает лямбду, которая принимает любое количество аргументов, и вызывает f один раз для каждого из этих аргументов. Его реализация — немного глубокий mojo, включающий пакеты параметров и инициализацию массива.

составление index_upto а также foreach позволяет вам сделать что-то для каждого значения времени компиляции из 0 в N-1, Это как мы называем .convert,

Просто звоню index_upto позволяет передать все аргументы одновременно emit,


Мы можем сделать нечто подобное в C ++ 11, но вместо этого мы бы написали вспомогательные функции, которые принимают пакеты параметров и тому подобное. Это больше, чем боль.

1

По вопросам рекламы [email protected]