我有一些C
数据接口(pod结构),每个数据接口具有不同的版本,并且每个版本都具有反序列化功能。
(下面的完整代码)
用户类型:
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)
例程(如果存在)。
目前,此类转换只能通过Converter-specializations完成。 (例如以下示例中的1到3)
完全允许使用c ++ 11功能,也许也允许c ++ 14。仅在确实必要时才允许使用最新,最奇特的c ++ 14后功能(但是,我希望看到基于这些功能的建议,以便将来改进代码)。
P.S。
为了清楚起见。该代码应保持通用性,可用于处理具有不同版本的不同接口。
#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;
}