すべてを一覧表示せずに、構造体のメンバーを反復処理する方法はありません。
::std::tuple
C++ 11を使用して、コンパイル時に構造体のようなものを反復処理できます。
また、その方法でタイプを実際に切り替えることもできません。それを行うことはできますが、その方法は、それぞれが異なるパラメーター型を取る同じ名前の関数をいくつか持つことです。何かのようなもの:
void doRead(StreamType &stream, int &data)
{
data = stream.readInt32();
}
void doRead(StreamType &stream, char &data)
{
data = stream.readChar();
}
// etc...
次にdoRead
、構造体メンバーで呼び出すだけで、コンパイラーは型に基づいて適切なものを魔法のように選択します。
C++ では、ここで解決している問題を解決する方法がシリアライゼーション ライブラリです。書き込みフォーマットと読み取りフォーマットの両方を制御できる場合は、protobufやboost:: serialization などを使用して、独自のコードを大量に記述することなく、比較的簡単にこれを行うことができます。
さらに、コードにいくつかの問題があります。_
識別子に先頭文字を使用しないでください。先頭に付いている識別子は_
、コンパイラまたは標準ライブラリの実装で使用するために予約されています。多くのコンパイラには、文字で始まるコンパイラ固有の言語拡張である特別なキーワードがあり_
ます。先頭_
文字を含む識別子を使用すると、環境によっては、あらゆる種類の奇妙な不可解なエラーが発生して、不思議なことにコードがコンパイルに失敗する可能性があります。
コンパイル時に列挙可能な構造体のようなものを取得できます。しかし、それは醜いです:
#include <tuple>
#include <string>
#include <vector>
#include <type_traits>
class asset : public ::std::tuple< ::std::string, ::std::vector<BYTE> >
{
public:
::std::string &name() { return ::std::get<0>(*this); }
const ::std::string &name() const { return ::std::get<0>(*this); }
::std::vector<BYTE> &data() { return ::std::get<1>(*this); }
const ::std::vector<BYTE> &data() const { return ::std::get<1>(*this); }
};
void writeToStream(Stream *out, const ::std::string &field)
{
out->writeString(field);
}
void writeToStream(Stream *out, const ::std::vector<BYTE> &field)
{
out->writeInt(field.size());
out->writeRaw(field.data(), field.size());
}
template <unsigned int fnum, typename... T>
typename ::std::enable_if< (fnum < sizeof...(T)), void >::type
writeToStream_n(Stream *out, const::std::tuple<T...> &field)
{
writeToStream(out, ::std::get<fnum>(field));
writeToStream_n<fnum+1, T...>(out, field);
}
template <unsigned int fnum, typename... T>
typename ::std::enable_if< (fnum >= sizeof...(T)) >::type
writeToStream_n(Stream *, const::std::tuple<T...> &)
{
}
template <typename... Tp>
void writeToStream(Stream *out, const ::std::tuple<Tp...> &composite)
{
writeToStream_n<0, Tp...>(out, composite);
}
void foo(Stream *out, const asset &a)
{
writeToStream(out, a);
}
writeToStream
明示的な型がないことに注意してくださいasset
。コンパイラは、派生元をアンパックし、::std::tuple
個々のフィールドを書き出すことにより、実行時にそれを書き込みます。
また、裸のポインターがある場合は、貧弱な C++ を書いています。C++ を書くつもりなら、慣用的で優れた C++ を書いてください。実行時リフレクションでやりたいことはすべて、物事を行う方法ではありません。
char *name
これが、 yourをに変換し::std::string
、サイズで区切られBYTE
た配列をsize
およびdata
フィールドでに変換した理由::std::vector
です。これらの型を使用することは、慣用的に正しい C++ の記述方法です。あなたがそうであったように裸のポインターを使用することはできません。data
さらに、強く関連する値 (と)を持つ 2 つのフィールドがありsize
、それらが関連付けられていることを示す振る舞いやその他の兆候を持たないフィールドがあると、実行時にイントロスペクションを行うコンパイラでさえ、正しいことを理解することが難しくなります。BYTE
によって指されている配列の大きさを知ることはdata
できません。また、これを でエンコードするという決定についても知ることができませんsize
。