Я хочу написать 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 справился с подобной вещью без шаблонов с переменным числом аргументов.
Я всегда чувствовал, что наиболее приемлемый способ специализировать функцию-член — это отнести к специализированному объекту функции:
#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());
}
В 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, но вместо этого мы бы написали вспомогательные функции, которые принимают пакеты параметров и тому подобное. Это больше, чем боль.