1

さまざまな構造体で特定の変数の「タイプ」を見つけて、それらを読み取れるようにしようとしています。(これは疑似コードであることに注意してください)

例えば:

#include "stream.h" //a custom stream reader class I made

typedef unsigned char BYTE;

/***** SERIES OF DIFFERENT STRUCTS ******/

struct asset
{
    char *name;
    int size;
    BYTE *data;
};

struct asset2
{
    char *lang;
    char *entry;
};

/*****************************************/


    void readAsset( Enumerable<struct> &istruct)
    {
        foreach( object o in istruct )
        {
            switch( o )
            {
                case int:
                    &o = _stream->ReadInt32();
                    break;
                case char:
                    &o = _stream->ReadChar();
                    break;
                case *:
                    &o = _stream->ReadInt32();
                    break;
                default: break;
            }
        }
    }

次のことができるようにしたいと思います。

asset a1;
asset2 a2;

readAsset( a1 );
readAsset( a2 );

そして、ファイルからのすべての情報をa1とa2に渡します。

C / C ++で、構造体の任意のオブジェクトからデータの型を取得し、それに基づいて読み取る方法があるかどうか疑問に思いましたか?複雑な列挙型で可能ですか?悪いコードで申し訳ありませんが、私がやろうとしていることをより簡単に理解できるようにしたかったのです。

追加情報:

_streamは、.NetのStream Readerと同様に作成したStreamクラスへのポインタです。ファイルからデータを読み取り、読み取られたデータの大きさに基づいてその位置を進めます。

私が何を求めているのかわからない場合は、言い換えさせていただきます。

4

3 に答える 3

2

すべてを一覧表示せずに、構造体のメンバーを反復処理する方法はありません。

::std::tupleC++ 11を使用して、コンパイル時に構造体のようなものを反復処理できます。

また、その方法でタイプを実際に切り替えることもできません。それを行うことはできますが、その方法は、それぞれが異なるパラメーター型を取る同じ名前の関数をいくつか持つことです。何かのようなもの:

 void doRead(StreamType &stream, int &data)
 {
     data = stream.readInt32();
 }
 void doRead(StreamType &stream, char &data)
 {
     data = stream.readChar();
 }
 // etc...

次にdoRead、構造体メンバーで呼び出すだけで、コンパイラーは型に基づいて適切なものを魔法のように選択します。

C++ では、ここで解決している問題を解決する方法がシリアライゼーション ライブラリです。書き込みフォーマットと読み取りフォーマットの両方を制御できる場合は、protobufboost:: 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

于 2013-03-09T01:48:59.417 に答える
2

あなたが求めているのはリフレクションと呼ばれるものです- それは次のとおりです:

実行時にオブジェクトの構造と動作 (具体的には、値、メタデータ、プロパティ、および関数) を調べて変更するコンピューター プログラムの機能。

C++ にはその「ネイティブ」がありません。

つまり、いくつかの側面を導入しようとする試みがいくつかありましたが、さまざまな成功の度合いで、リフレクションのいくつかの側面を生み出しましたが、Ruby などの言語で得られる「完全な」リフレクション自体ではありません。

ただし、冒険したい場合は、Reflectabitと呼ばれる Reflection ライブラリを試すことができます。

それが価値があるかどうかを確認するために (コードを検討している可能性があります)、ここで参照されます-API の使用方法に関するかなりの例があります:

http://www.altdevblogaday.com/2011/09/25/reflection-in-c-part-1-introduction/

幸運を!

于 2013-03-09T02:01:22.140 に答える
0

C++ での通常のパターンは、型のメンバーが何であるかを理解しようとするのではなく、型の実装者によって実装され、ディスクにシリアル化/逆シリアル化できる演算子を提供することです。

たとえば、boost::serialize ライブラリを見ることができます。使用法はそれほど複雑ではありません。メンバーをある順序でリストする関数を提供する必要があります。ライブラリはそこからそれを取得し、さまざまな形式へのシリアル化を実装します。

于 2013-03-09T02:17:15.023 に答える