У меня есть несколько C
интерфейсов данных (под-структур) с каждым в разных версиях с функцией десериализации для каждой версии.
(полный код ниже)
пользовательские типы:
typedef struct sInterface_v1_t {
int i;
}Interface_v1_t;
typedef struct sInterface_v2_t {
int i;
int j;
}Interface_v2_t;
typedef struct sInterface_v3_t {
int j;
float u;
}Interface_v3_t;
// latest version
typedef Interface_v3_t Interface_t;
void Interface_v1_deserialize(std::stringstream& io_str, Interface_v1_t* o_d)
{
io_str >> o_d->i;
}
void Interface_v2_deserialize(std::stringstream& io_str, Interface_v2_t* o_d)
{
io_str >> o_d->i;
io_str >> o_d->j;
}
void Interface_v3_deserialize(std::stringstream& io_str, Interface_v3_t* o_d)
{
io_str >> o_d->j;
io_str >> o_d->u;
}
Сериализованный поток данных кодирует тип данных и номер версии, чтобы его можно было легко отправить
// type interfaceversion data...
std::string data_1 = "I 1 5";
std::string data_2 = "I 2 7 2";
std::string data_3 = "I 3 1 3.54";
Я хочу создать общую десериализацию, которая всегда выводит последний тип.
В конце код десериализации должен выглядеть так:
std::stringstream s(data_1); // or data_2 or data_3
s >> ts; // remove type from string (type dispatching will be later one level above)
Interface_t i = { 0 };
Interface_Deserializer<Interface_t>::instance().deserialize(s, &i);
Для этого пользователь должен предоставить функцию преобразования из предыдущей версии в более новую версию.
конвертер пользователя:
static void convert(const Interface_v1_t* i_d, Interface_v2_t* o_d) {
o_d->i = i_d->i;
o_d->j = 0;
}
static void convert(const Interface_v2_t* i_d, Interface_v3_t* o_d) {
o_d->j = i_d->j;
o_d->u = 0.f;
}
Я создал набор шаблонов (см. Полный список ниже), которые могут этого добиться, но пользователь должен создать некоторый код котельной пластины, чтобы заставить их работать.
пользовательская котельная плита
template <>
struct InterfaceVersionHelper<Interface_t, 1> :InterfaceVersionHelperBase<Interface_t, Interface_v1_t, 1, &Interface_v1_deserialize>
{};
template <>
struct InterfaceVersionHelper<Interface_t, 2> :InterfaceVersionHelperBase<Interface_t, Interface_v2_t, 2, &Interface_v2_deserialize>
{};
template <>
struct InterfaceVersionHelper<Interface_t, 3> :InterfaceVersionHelperBase<Interface_t, Interface_v3_t, 3, &Interface_v3_deserialize>
{};
static bool r1 = []() {
InterfaceVersionHelper<Interface_t, 1>::registerMe();
InterfaceVersionHelper<Interface_t, 2>::registerMe();
InterfaceVersionHelper<Interface_t, 3>::registerMe();
return true;
}();
template <>
struct LatestInterface<Interface_t> :public Interface_LatestVersion<Interface_t, 3> {};
Я бы хотел свести их к некоторому декларативному утверждению, например
static bool r =
InterfaceRegistererDefintion<
Interface_t,
InterfaceV<Interface_v3_t, 3, &Interface_v3_deserialize>,
InterfaceV<Interface_v2_t, 2, &Interface_v2_deserialize>,
InterfaceV<Interface_v1_t, 1, &Interface_v1_deserialize>
>::create();
где первый тип - это имя типа интерфейса, первый кортеж - это самая последняя версия (то же самое, что и Interface_t), а остальная часть списка может быть настолько длинной, насколько это необходимо.
Есть ли способ достичь этого (и, возможно, уменьшить или упростить остальную часть кода шаблона)
возможно также шаблон Converter<T, a, b>
можно изменить таким образом, чтобы он использовал подпрограмму ::convert(a, b)
, если таковая имеется.
в настоящее время такие преобразования могут быть сделаны только с помощью специализаций конвертера. (как от 1 до 3 в приведенном ниже примере)
Возможности c ++ 11 разрешены полностью, возможно, также c ++ 14. Новейшие и причудливые функции, появившиеся после c ++ 14, могут быть разрешены только в том случае, если это действительно необходимо (однако - я хотел бы увидеть предложение на их основе для улучшения кода в будущем).
приписка
просто чтобы прояснить. код должен оставаться настолько универсальным, чтобы его можно было использовать для обработки различных интерфейсов с их разными версиями.
Полный код
#include <list>
#include <map>
#include <iostream>
#include <sstream>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <functional>
template <typename T>
using DeserFun = void(std::stringstream&, T*);
template<typename IFACE>
void DummyDeser(std::stringstream& io_str, IFACE* o_d) {};
template <typename T>
struct InterfaceVersionHelperBaseBase {
using value_type_target = T;
virtual int version()const = 0;
virtual void deserialize(std::stringstream& io_str, T* o_d) = 0;
};
template <typename T, int LatestVersion>
struct Interface_LatestVersion {
enum {
version = LatestVersion
};
using value_type = T;
};
template <typename T>
struct LatestInterface :public Interface_LatestVersion<T, 0> {};
template <typename T>
struct Interface_Deserializer {
using value_type = T;
std::map<int, std::shared_ptr<InterfaceVersionHelperBaseBase<T>>> versionMap;
static Interface_Deserializer& instance() {
static Interface_Deserializer s_instance;
return s_instance;
};
void deserialize(std::stringstream& io_str, T* o_d)const {
int v = 0;
io_str >> v;
auto it = versionMap.find(v);
if (it != versionMap.end()) {
(*it).second->deserialize(io_str, o_d);
} else {
std::cerr << "unknown version " << v << " for " << typeid(T).name() << std::endl;
}
}
private:
Interface_Deserializer() {};
};
template <typename T, typename IFACE, int IFACE_VER, DeserFun<IFACE> DESER_FUN>
struct InterfaceVersionHelperBase : public InterfaceVersionHelperBaseBase<T>
{
using value_type_current = IFACE;
enum { ver = IFACE_VER };
int version()const override { return IFACE_VER; };
/// deserializes stream and converts to latest type
void deserialize(std::stringstream& io_str, T* o_d) override {
value_type_current o;
DESER_FUN(io_str, &o);
Converter<T, IFACE_VER, LatestInterface<T>::version>::convert(&o, o_d);
return;
};
/// registers helper for this interface version on the Interface_Deserializer
static void registerMe() {
std::pair<int, std::shared_ptr<InterfaceVersionHelperBaseBase<T>>> v;
v.first = IFACE_VER;
v.second.reset(new InterfaceVersionHelperBase<T, IFACE, IFACE_VER, DESER_FUN>());
Interface_Deserializer<T>::instance().versionMap.insert( v );
}
};
/// this has to be specialized for each interface version to point to the correct types and deserializer
template <typename IFACE, int IFACE_VER>
struct InterfaceVersionHelper :public InterfaceVersionHelperBase < IFACE, IFACE, 0, &DummyDeser<IFACE> >
{
};
/// generic converter that converts between two interface versions by converting the input one version up
/// by calling user convert function
/// this can also be specialized by used to implement specific from x to latest converter
template <typename IFACE, int IFACE_VER, int IFACE_VER2>
struct Converter {
static void convert(const typename InterfaceVersionHelper < IFACE, IFACE_VER>::value_type_current* i_d,
typename InterfaceVersionHelper < IFACE, IFACE_VER2>::value_type_current* o_d)
{
InterfaceVersionHelper < IFACE, IFACE_VER + 1>::value_type_current temp;
::convert(i_d, &temp);
Converter<IFACE, IFACE_VER + 1, IFACE_VER2>::convert(&temp, o_d);
}
};
/// converter specialization that aborts conversion, if both sides are the same interface versions
template <typename IFACE, int IFACE_VER>
struct Converter<IFACE, IFACE_VER, IFACE_VER>{
static void convert(const typename InterfaceVersionHelper < IFACE, IFACE_VER>::value_type_current* i_d,
typename InterfaceVersionHelper < IFACE, IFACE_VER>::value_type_current* o_d)
{
*o_d = *i_d;
}
};
/////////////////////////////
// user types / deserializers
/////////////////////////////
typedef struct sInterface_v1_t {
int i;
}Interface_v1_t;
typedef struct sInterface_v2_t {
int i;
int j;
}Interface_v2_t;
typedef struct sInterface_v3_t {
int j;
float u;
}Interface_v3_t;
// latest version
typedef Interface_v3_t Interface_t;
void Interface_v1_deserialize(std::stringstream& io_str, Interface_v1_t* o_d)
{
io_str >> o_d->i;
}
void Interface_v2_deserialize(std::stringstream& io_str, Interface_v2_t* o_d)
{
io_str >> o_d->i;
io_str >> o_d->j;
}
void Interface_v3_deserialize(std::stringstream& io_str, Interface_v3_t* o_d)
{
io_str >> o_d->j;
io_str >> o_d->u;
}
/////////////////////////////
// user converter
/////////////////////////////
static void convert(const Interface_v1_t* i_d, Interface_v2_t* o_d) {
o_d->i = i_d->i;
o_d->j = 0;
}
static void convert(const Interface_v2_t* i_d, Interface_v3_t* o_d) {
o_d->j = i_d->j;
o_d->u = 0.f;
}
// specialization for specific conversion
template <>
struct Converter<Interface_t, 1, 3> {
static void convert(const Interface_v1_t* i_d,
Interface_v3_t* o_d)
{
o_d->u = float(i_d->i);
o_d->j = 0;
}
};
/////////////////////////////
// user boiler plate
/////////////////////////////
template <>
struct InterfaceVersionHelper<Interface_t, 1> :InterfaceVersionHelperBase<Interface_t, Interface_v1_t, 1, &Interface_v1_deserialize>
{};
template <>
struct InterfaceVersionHelper<Interface_t, 2> :InterfaceVersionHelperBase<Interface_t, Interface_v2_t, 2, &Interface_v2_deserialize>
{};
template <>
struct InterfaceVersionHelper<Interface_t, 3> :InterfaceVersionHelperBase<Interface_t, Interface_v3_t, 3, &Interface_v3_deserialize>
{};
static bool r1 = []() {
InterfaceVersionHelper<Interface_t, 1>::registerMe();
InterfaceVersionHelper<Interface_t, 2>::registerMe();
InterfaceVersionHelper<Interface_t, 3>::registerMe();
return true;
}();
template <>
struct LatestInterface<Interface_t> :public Interface_LatestVersion<Interface_t, 3> {};
/*
// desired user boiler plate:
static bool r =
InterfaceRegistererDefintion<
Interface_t,
InterfaceV<Interface_v3_t, 3, &Interface_v3_deserialize>,
InterfaceV<Interface_v2_t, 2, &Interface_v2_deserialize>,
InterfaceV<Interface_v1_t, 1, &Interface_v1_deserialize>
>::create();
*/
/////////////////////////////
// user test data
/////////////////////////////
std::string data_1 = "I 1 5";
std::string data_2 = "I 2 7 2";
std::string data_3 = "I 3 1 3.54";
std::string data_4 = "I 4 1 3.54 3.14159";
int main(int argc, char* argv[])
{
std::string ts;
int ti;
{ // test deserializer
std::stringstream s1(data_1);
std::stringstream s2(data_2);
std::stringstream s3(data_3);
s1 >> ts >> ti; // remove type and version from string
s2 >> ts >> ti; //
s3 >> ts >> ti; //
Interface_v1_t i1 = { 0 };
Interface_v2_t i2 = { 0 };
Interface_v3_t i3 = { 0 };
Interface_v1_deserialize(s1, &i1);
Interface_v2_deserialize(s2, &i2);
Interface_v3_deserialize(s3, &i3);
}
{// test helper deserialziation and conversion
std::stringstream s1(data_1);
std::stringstream s2(data_2);
std::stringstream s3(data_3);
s1 >> ts >> ti; // remove type and version from string
s2 >> ts >> ti; //
s3 >> ts >> ti; //
Interface_t i1 = { 0 };
Interface_t i2 = { 0 };
Interface_t i3 = { 0 };
InterfaceVersionHelper< Interface_t, 1> h1;
InterfaceVersionHelper< Interface_t, 2> h2;
InterfaceVersionHelper< Interface_t, 3> h3;
h1.deserialize(s1, &i1);
h2.deserialize(s2, &i2);
h3.deserialize(s3, &i3);
}
{// test Interface_Deserializer
std::stringstream s1(data_1);
std::stringstream s2(data_2);
std::stringstream s3(data_3);
std::stringstream s4(data_4);
s1 >> ts; // remove type from string
s2 >> ts; //
s3 >> ts; //
s4 >> ts; //
Interface_t i1 = { 0 };
Interface_t i2 = { 0 };
Interface_t i3 = { 0 };
Interface_t i4 = { 0 };
auto ds = Interface_Deserializer<Interface_t>::instance();
ds.deserialize(s1, &i1);
ds.deserialize(s2, &i2);
ds.deserialize(s3, &i3);
ds.deserialize(s4, &i4); // should output an error
}
return 0;
}