ベクター内のさまざまな要素を処理する必要があります。各要素は特定のパラメーター(整数または文字列)を所有しているため、一連の要素のエンコード/デコードを簡単に処理できます。
要素のリストをエンコードすると、要素の間にセパレーターが挿入され、要素がシリアル化されます。
文字列のデコードは、各部分文字列のセパレーターを見つけ、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()に分割されているデコード手法に関心があります。
さて、実際の質問は、{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"}}のような値を保存できるようになります
シリアライゼーションを書くときに私が結んだのは次のとおりです:
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
あなたの実際の質問に答えるために別の答えを作成します。