具有c ++模板的自动结构反序列化/转换

时间:2019-06-05 09:24:26

标签: c++11 templates c++14

我有一些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;
}

0 个答案:

没有答案