Я буду опираться на предложенное вами решение, но вместо этого используйте boost :: fusion :: tuples (при условии, что это разрешено). Давайте предположим, что ваши типы данных
struct Foo{};
struct Bar{};
struct Baz{};
struct Fbr{};
и ваши данные
struct some_data {
Foo foo;
Bar bar;
Baz baz;
Fbr fbr;
};
Из комментариев я понимаю, что вы не контролируете классы SerialisedXYZ, но у них есть определенный интерфейс. Я буду считать, что что-то вроде этого достаточно близко (?):
struct SerializedFooBar {
void set_foo(const Foo&){
std::cout << "set_foo in SerializedFooBar" << std::endl;
}
void set_bar(const Bar&){
std::cout << "set_bar in SerializedFooBar" << std::endl;
}
};
// another protobuf-generated class
struct SerializedBarBaz {
void set_bar(const Bar&){
std::cout << "set_bar in SerializedBarBaz" << std::endl;
}
void set_baz(const Baz&){
std::cout << "set_baz in SerializedBarBaz" << std::endl;
}
};
Теперь мы можем уменьшить шаблон и ограничить его одним typedef для каждой перестановки типов данных и одной простой перегрузкой для каждого члена set_XXX класса SerializedXYZ следующим образом:
typedef boost::fusion::tuple<Foo, Bar> foobar;
typedef boost::fusion::tuple<Bar, Baz> barbaz;
//...
template <class S>
void serialized_set(S& s, const Foo& v) {
s.set_foo(v);
}
template <class S>
void serialized_set(S& s, const Bar& v) {
s.set_bar(v);
}
template <class S>
void serialized_set(S& s, const Baz& v) {
s.set_baz(v);
}
template <class S, class V>
void serialized_set(S& s, const Fbr& v) {
s.set_fbr(v);
}
//...
Хорошо, что вам больше не нужно специализировать свои serialization_traits. Следующее использует функцию boost :: fusion :: fold, которую, я полагаю, можно использовать в вашем проекте:
template <class SerializedX>
class serialization_traits {
struct set_functor {
template <class V>
SerializedX& operator()(SerializedX& s, const V& v) const {
serialized_set(s, v);
return s;
}
};
public:
template <class Tuple>
static SerializedX encode(const Tuple& t) {
SerializedX s;
boost::fusion::fold(t, s, set_functor());
return s;
}
};
А вот несколько примеров того, как это работает. Обратите внимание, что если кто-то попытается связать элемент данных из some_data, который не совместим с интерфейсом SerializedXYZ, компилятор сообщит вам об этом:
void send_msg(const SerializedFooBar&){
std::cout << "Sent SerializedFooBar" << std::endl;
}
void send_msg(const SerializedBarBaz&){
std::cout << "Sent SerializedBarBaz" << std::endl;
}
void send(const some_data& data) {
send_msg( serialization_traits<SerializedFooBar>::encode(boost::fusion::tie(data.foo, data.bar)) );
send_msg( serialization_traits<SerializedBarBaz>::encode(boost::fusion::tie(data.bar, data.baz)) );
// send_msg( serialization_traits<SerializedFooBar>::encode(boost::fusion::tie(data.foo, data.baz)) ); // compiler error; SerializedFooBar has no set_baz member
}
int main() {
some_data my_data;
send(my_data);
}
Код здесь
EDIT:
К сожалению, это решение не решает проблему № 1 ОП. Чтобы исправить это, мы можем определить серию тегов, по одному для каждого элемента данных, и следовать аналогичному подходу. Вот теги вместе с измененными функциями serialized_set
:
struct foo_tag{};
struct bar1_tag{};
struct bar2_tag{};
struct baz_tag{};
struct fbr_tag{};
template <class S>
void serialized_set(S& s, const some_data& data, foo_tag) {
s.set_foo(data.foo);
}
template <class S>
void serialized_set(S& s, const some_data& data, bar1_tag) {
s.set_bar1(data.bar1);
}
template <class S>
void serialized_set(S& s, const some_data& data, bar2_tag) {
s.set_bar2(data.bar2);
}
template <class S>
void serialized_set(S& s, const some_data& data, baz_tag) {
s.set_baz(data.baz);
}
template <class S>
void serialized_set(S& s, const some_data& data, fbr_tag) {
s.set_fbr(data.fbr);
}
Шаблон снова ограничен одним serialized_set
на элемент данных и масштабируется линейно, как и в моем предыдущем ответе. Вот модифицированные serialization_traits:
// the serialization_traits doesn't need specialization anymore :)
template <class SerializedX>
class serialization_traits {
class set_functor {
const some_data& m_data;
public:
typedef SerializedX& result_type;
set_functor(const some_data& data)
: m_data(data){}
template <class Tag>
SerializedX& operator()(SerializedX& s, Tag tag) const {
serialized_set(s, m_data, tag);
return s;
}
};
public:
template <class Tuple>
static SerializedX encode(const some_data& data, const Tuple& t) {
SerializedX s;
boost::fusion::fold(t, s, set_functor(data));
return s;
}
};
и вот как это работает:
void send(const some_data& data) {
send_msg( serialization_traits<SerializedFooBar>::encode(data,
boost::fusion::make_tuple(foo_tag(), bar1_tag())));
send_msg( serialization_traits<SerializedBarBaz>::encode(data,
boost::fusion::make_tuple(baz_tag(), bar1_tag(), bar2_tag())));
}
Обновленный код здесь