web-dev-qa-db-ja.com

格納タイプが異なるエンコーダー/デコーダークラスのC ++設計

ベクター内のさまざまな要素を処理する必要があります。各要素は特定のパラメーター(整数または文字列)を所有しているため、一連の要素のエンコード/デコードを簡単に処理できます。

  • 要素のリストをエンコードすると、要素の間にセパレーターが挿入され、要素がシリアル化されます。

  • 文字列のデコードは、各部分文字列のセパレーターを見つけ、IDとタイプ固有の値を抽出することからなります。例( "FOO"は整数に関連付けられ、 "BAR"は文字列に関連付けられていることを知っている): "FOO = 54、BAR = hello"は{{ID_FOO、54}、{ID_BAR、 "hello"}}を返します。

私は現在次の裸のデザインを持っています:

#include <cstdint>
#include <cstdlib>
#include <string>
#include <memory>

enum eID { /*...*/ };

// Base class
class Base {
private:
    enum eID m_Id;
public:
    Base(enum eID = 0): m_Id(eID) {}
    virtual std::string Encode() {
        // Return encoding of m_Id
    }
    static Base* Decode(const std::string &str) {
        Base *ptr;
        // Extract id
        // According to the kind of id, create a new Derived<T>
        // giving the substring to the constructor
        // ptr = new Derived<...>(id, str.substr(...));
        return ptr;
    }
};

template <class T>
class Derived<T> : public Base {
private:
    T m_Value;
public:
    Derived(enum eID id, const std::string &str); // participates to decoding
    std::string Encode();
};

template<>
Derived<std::string>::Derived(enum eID id, const std::string &str) {
    m_Value = str; // easy
}

template<>
std::string Derived<std::string>::Encode() {
    return static_cast<Base*>(this)->Encode() + m_Value;
}

template<>
Derived<long>::Derived(enum eID id, const std::string &str) {
    m_Value = strtol(str.c_str(), nullptr, 10);
}

template<>
std::string Derived<long>::Encode() {
    // Encode id and integer param to a string
}

class BaseList {
private:
    std::vector<std::unique_ptr<Base>> m_List;
public:
    void Append(Base *ptr) {
        m_List.Push_back(std::unique_ptr<Base>(ptr));
    }
    std::string Encode() {
        // Call each Encode() function and concatenate strings together
        // Insert a delimiter in-between substrings
    }
    void Decode(const std::string &sToDecode) {
        // Look for delimiters in sToDecode
        // For each delimited substring:
        // - call Base::Decode() to allocate a new type-specific object
        // - Push back into m_List
    }
}

(これは完全なコンパイル例ではありません。)

ロジック設計ですか?既存の設計パターンに一致するように最適化するにはどうすればよいですか?特に、ここではBaseList :: Decode()と静的メソッドBase :: Decode()に分割されているデコード手法に関心があります。

4
Teuxe

さて、実際の質問は、{FOO、54}と{BAR、 "hello"}の違いをどのように処理するかということでした。これはユニオンの古典的な使用例です:

struct A { int id; union { long val; std::string s; } un; };

残念ながら、ユニオンはコンストラクターをうまく処理しないため、std :: stringはユニオン内ではうまく機能しません(最近変更した場合を除きます:-)。ただし、ユニオンを実装する別の方法があります。

class Union {
public:
  Union() : m_s(0), m_val(0) {}
  Union(std::string s) : m_s(new string(s)), m_val(0) { }
  Union(long val) : m_s(0), m_val(new long(val)) { }
  ~Union() { delete m_s; delete m_val; }
  Union(const Union &u) 
  : m_s(u.m_s ? new string(*u.m_s) : 0), 
    m_val(u.m_val ? new long(*u.m_val) : 0) { }
  void operator=(const Union &u) { 
        delete m_s; m_s=0;
        delete m_val; m_val=0;
        if (u.m_s) m_s = new std::string(*u.m_s); 
        if (u.m_val) m_val = new long(*u.m_val);
        }
 private:
  std::string *m_s; 
  long *m_val;
 };

NULLポインターは、値が使用されていないことを示しています。 (このUnionクラスは、さらに別の型を追加する必要がある場合、維持するのが悪夢になることに注意してください)

必要な構造体は次のようになります。

struct A { int id; Union u; };

これができたら、配列を作成するのは非常に簡単です。

std::vector<A> vec;

{{ID_FOO、33}、{ID_BAR、 "hello"}}のような値を保存できるようになります

1
tp1

シリアライゼーションを書くときに私が結んだのは次のとおりです:

class ParserPairBase 
{
public:
//virtual std::type_info TypeInfo() const=0;
virtual size_t SizeOf() const=0;
struct PtrSize
{
  char *ptr;
  size_t size;
};
virtual PtrSize BaseParse(std::string s, bool &success) const=0;
virtual std::string BaseCreate(PtrSize psz) const=0;
};

template<class T>
class ParserPair : public ParserPairBase 
{
public:
   virtual std::string Create(T t) const=0;
   virtual T Parse(std::string s, bool &success) const=0;
   //std::type_info TypeInfo() const { return typeid(T); }
   virtual size_t SizeOf() const { return sizeof(T); }
   virtual PtrSize BaseParse(std::string s, bool &success) const
      {
      PtrSize sz;
      T *ptr = new T;
      sz.ptr = (char*)ptr;
      sz.size = sizeof(T);
      *ptr = Parse(s, success);
      return sz;
      }
    virtual std::string BaseCreate(PtrSize psz) const
       {
       T *ptr = (T*)psz.ptr;
       std::string s = Create(*ptr);
       return s;
       }
   };

そして、派生クラスだけがCreate()とParse()を実装します。

class CharParser : public ParserPair<char>
{
public: 
   std::string Create(char c) const { std::string s; s+=c; return s; }
   char Parse(std::string s, bool &success) const { 
      if (s.length()!=1) { success = false; return ' '; } 
      success = true; 
      return s[0]; 
   }
};

残念ながら、これはサイトにとって長すぎますが、これらの基本クラスを使用すると、たとえば任意の構造体のパーサーを作成することが可能です。詳細については、このリンクを参照してください: http://sourceforge.net/p/gameapi/code/ci/master/tree/GameApi/Parser.hh

あなたの実際の質問に答えるために別の答えを作成します。

0
tp1