2

ifstreamとofstreamを使用してPlainOldDatastructureをシリアル化しようとしていますが、動作させることができませんでした。次に、問題をcharとintだけの超基本的なシリアル化に減らしようとしましたが、それでも機能しませんでした。明らかに、私はコアの基本的なレベルで何かが欠けています。

基本構造の場合:

struct SerializeTestStruct
{
    char mCharVal;
    unsigned int mIntVal;

    void Serialize(std::ofstream& ofs);
};

シリアル化機能付き:

void SerializeTestStruct::Serialize(std::ofstream& ofs)
{
    bool isError = (false == ofs.good());
    if (false == isError)
    {
        ofs.write((char*)&mCharVal, sizeof(mCharVal));
        ofs.write((char*)&mIntVal, sizeof(mIntVal));
    }
}

次の短いプログラムでこれが失敗するのはなぜですか?

//ultra basic serialization test.
    SerializeTestStruct* testStruct = new SerializeTestStruct();
    testStruct->mCharVal = 'y';
    testStruct->mIntVal = 9;

    //write
    std::string testFileName = "test.bin";
    std::ofstream fileOut(testFileName.data());
    fileOut.open(testFileName.data(), std::ofstream::binary|std::ofstream::out);
    fileOut.clear();
    testStruct->Serialize(fileOut);

    fileOut.flush();
    fileOut.close();

    delete testStruct;

    //read
    char * memblock;
    std::ifstream fileIn (testFileName.data(), std::ifstream::in|std::ifstream::binary);
    if (fileIn.is_open())
    {
        // get length of file:
        fileIn.seekg (0, std::ifstream::end);
        int length = fileIn.tellg();
        fileIn.seekg (0, std::ifstream::beg);

        // allocate memory:
        memblock = new char [length];
        fileIn.read(memblock, length);
        fileIn.close();

        // read data as a block:
        SerializeTestStruct* testStruct2 = new(memblock) SerializeTestStruct();

        delete[] testStruct2;
    }

コードを実行するとmemblock、上部に「y」が付いているので、機能している可能性があり、placement new最後の部分に問題があるだけですか?その新しい配置の後、私SerializeTestStructは値が0、0になります。

4

5 に答える 5

2

これがあなたのものがどのように読まれるべきかです:

#include <fstream>
#include <string>
#include <stdexcept>

struct SerializeTestStruct
{
    char mCharVal;
    unsigned int mIntVal;

    void Serialize(::std::ostream &os);
    static SerializeTestStruct Deserialize(::std::istream &is);
};

void SerializeTestStruct::Serialize(std::ostream &os)
{
    if (os.good())
    {
        os.write((char*)&mCharVal, sizeof(mCharVal));
        os.write((char*)&mIntVal, sizeof(mIntVal));
    }
}

SerializeTestStruct SerializeTestStruct::Deserialize(std::istream &is)
{
        SerializeTestStruct retval;

    if (is.good())
    {
        is.read((char*)&retval.mCharVal, sizeof(retval.mCharVal));
        is.read((char*)&retval.mIntVal, sizeof(retval.mIntVal));
    }
    if (is.fail()) {
        throw ::std::runtime_error("failed to read full struct");
    }
    return retval;
}

int main(int argc, const char *argv[])
{
//ultra basic serialization test.

    // setup
    const ::std::string testFileName = "test.bin";

    // write
    {
        SerializeTestStruct testStruct;
        testStruct.mCharVal = 'y';
        testStruct.mIntVal = 9;

        ::std::ofstream fileOut(testFileName.c_str());
        fileOut.open(testFileName.c_str(),
                     std::ofstream::binary|std::ofstream::out);
        fileOut.clear();
        testStruct.Serialize(fileOut);
    }

    // read
    {
        ::std::ifstream fileIn (testFileName.c_str(),
                                std::ifstream::in|std::ifstream::binary);
        if (fileIn.is_open())
        {
            SerializeTestStruct testStruct =            \
                SerializeTestStruct::Deserialize(fileIn);

            ::std::cout << "testStruct.mCharVal == '" << testStruct.mCharVal
                        << "' && testStruct.mIntVal == " << testStruct.mIntVal
                        << '\n';
        }
    }
    return 0;
}

スタイルの問題:

  • newあなたがそれを助けることができるならば、物を作るために使用しないでください。スタックに割り当てられたオブジェクトは通常、必要なものであり、ヒープから割り当てる任意のライフタイムオブジェクトよりも管理が非常に簡単です。を使用する場合はnew、ライフタイムの管理に役立つ何らかのスマートポインタタイプの使用を検討してください。
  • シリアル化と逆シリアル化のコードは、一緒に調べて変更できるように一致させる必要があります。これにより、このようなコードの保守がはるかに簡単になります。
  • C ++を使用して、デストラクタを使用して処理をクリーンアップします。これが、デストラクタの目的です。これは、使用される変数のスコープが比較的限定されている場合に、コードの一部を含む基本ブロックを作成することを意味します。
  • 不必要にフラグを使用しないでください。

間違い...

  • dataのメンバー関数は使用しないでください::std::string
  • 配置newとそのメモリブロックを使用することは、途方もなく複雑なので、本当に悪い考えです。また、使用した場合は、使用した方法で配列削除を使用しません。そして最後に、後で説明する理由により、とにかく機能しません。
  • ofstream関数が使用する型では使用しないでくださいSerialize。これは、不要な機能を持つ派生クラスであるためです。特別な理由がない限り、必要な機能を備えた階層内で最も基本的なクラスを常に使用する必要があります。基本クラスSerializeの機能で正常に動作するため、代わりにそのタイプを使用してください。ostream
  • 構造のディスク上のレイアウトとメモリ内のレイアウトが一致しないため、配置の新しい手法は失敗する運命にあります。原則として、serialize関数がある場合は、一致する関数が必要ですdeserialize

これがあなたのメモリレイアウトの問題のさらなる説明です。x86_64ベースのLinuxボックスのメモリ内の構造は、次のようになります。

+------------+-----------+
|Byte number | contents  |
+============+===========+
|          0 |     0x79  |
|            | (aka 'y') |
+------------+-----------+
|          1 |   padding |
+------------+-----------+
|          3 |   padding |
+------------+-----------+
|          4 |   padding |
+------------+-----------+
|          5 |         9 |
+------------+-----------+
|          6 |         0 |
+------------+-----------+
|          7 |         0 |
+------------+-----------+
|          8 |         0 |
+------------+-----------+

セクションの内容paddingは未定義ですが、一般的には0intただし、そのスペースは使用されず、単に存在するため、以下へのアクセスは効率的な4バイト境界上にあるため問題ありません。

ディスク上の構造のサイズは5バイトであり、パディングセクションが完全に欠落しています。つまり、メモリに読み込んだときに、メモリ内の構造とまったく一致せず、アクセスすると、ある種の恐ろしい問題が発生する可能性があります。

最初のルールは、serialize関数が必要な場合は関数が必要deserializeです。2番目のルールは、自分が何をしているかを本当に正確に理解していない限り、生のメモリをファイルにダンプしないでください。これは多くの場合問題なく機能しますが、機能しない重要な場合もあります。そして、何が機能するのか、何が機能しないのか、そして機能するのか機能しないのかを知らない限り、特定のテスト状況では問題なく機能しているように見えますが、実際のシステム。

私のコードはまだメモリをファイルにダンプします。そして、それを書いたときと同じバージョンのコンパイラでコンパイルされたコードを使用して、まったく同じアーキテクチャとプラットフォームで結果を読み戻す限り、それは機能するはずです。これらの変数の1つが変更されるとすぐに、すべての賭けはオフになります。

于 2011-04-18T20:33:23.670 に答える
1
bool isError = (false == ofs.good());
if (false == isError)
{
    ofs.write((char*)&mCharVal, sizeof(mCharVal));
    ofs.write((char*)&mIntVal, sizeof(mIntVal));
}

への変更

if ( ofs.good() )
{
    ofs.write((char*)&mCharVal, sizeof(mCharVal));
    ofs.write((char*)&mIntVal, sizeof(mIntVal));
}

私はします:

ostream & operator << ( ostream &os, const SerializeTestStruct &mystruct )
{
  if ( ofs.good() )
  {
    os.write((char*)&mystruct.mCharVal, sizeof(mCharVal));
    os.write((char*)&mystruct.mIntVal, sizeof(mIntVal));
  }
  return os;
}
于 2011-04-18T19:34:34.920 に答える
1

問題はここにあります:

SerializeTestStruct* testStruct2 = new(memblock) SerializeTestStruct();

SerializeTestStructこれにより、以前に割り当てられたメモリに、値で初期化されたタイプのオブジェクトが作成されます。値の初期化はPODタイプのゼロmemblock初期化であるため、ゼロで埋められます(詳細)。

コードの迅速な修正は次のとおりです。

SerializeTestStruct* testStruct2 = new SerializeTestStruct;
fileIn.read( (char*)&testStruct2->mCharVal, sizeof(testStruct2->mCharVal) );
fileIn.read( (char*)&testStruct2->mIntVal, sizeof(testStruct2->mIntVal) );
fileIn.close();
// do some with testStruct2
// ...
delete testStruct2;
于 2011-04-18T19:47:20.293 に答える
0

私の意見では、ストリームに直接ではなく、バッファへのシリアル化を許可する必要があります。バッファへの書き込みにより、ネストされたクラスまたは継承されたクラスをメモリに書き込むことができ、バッファ全体をストリームに書き込むことができます。ストリームへの断片の書き込みは効率的ではありません。

ストリームへのバイナリデータの書き込みを停止する前に、私が作成したものは次のとおりです。

struct Serialization_Interface
{
    //!  Returns size occupied on a stream.
    /*! Note:  size on the platform may be different.
     *  This method is used to allocate memory.
     */
    virtual size_t  size_on_stream(void) const = 0;

    //!  Stores the fields of the object to the given pointer.
    /*!  Pointer is incremented by the size on the stream.
     */
    virtual void    store_to_buffer(unsigned char *& p_buffer) const = 0;

    //!  Loads the object's fields from the buffer, advancing the pointer.
    virtual void    load_from_buffer(const unsigned char *& p_buffer) = 0;
};

struct Serialize_Test_Structure
  : Serialization_Interface
{
    char mCharVal;
    int  mIntVal;

    size_t  size_on_stream(void) const
    {
        return sizeof(mCharVal) + sizeof(mIntVal);
    }

    void  store_to_buffer(unsigned char *& p_buffer) const
    {
        *p_buffer++ = mCharVal;
        ((int&)(*p_buffer)) = mIntVal;
        p_buffer += sizeof(mIntVal);
        return;
    }

    void  load_from_buffer(const unsigned char *& p_buffer)
    {
         mCharVal = *p_buffer++;
         mIntVal = (const int&)(*p_buffer);
         p_buffer += sizeof(mIntVal);
         return;
    }
};


int main(void)
{
   struct Serialize_Test_Struct myStruct;
   myStruct.mCharVal = 'G';
   myStruct.mIntVal = 42;

   // Allocate a buffer:
   unsigned char * buffer = new unsigned char[](myStruct.size_on_stream());

   // Create output file.
   std::ofstream outfile("data.bin");

   // Does your design support this concept?
   unsigned char * p_buffer = buffer;
   myStruct.store_to_buffer(p_buffer);
   outfile.write((char *) buffer, myStruct.size_on_stream());

   outfile.close();
   return 0;
}

テキストデータはエンディアンや受信プラットフォームで受け入れられるIEEE浮動小数点形式について心配する必要がないため、テキストデータを優先してバイナリデータをストリームに書き込むのをやめました。

于 2011-04-18T20:54:01.710 に答える
0

これを完全に不透明だと思うのは私だけですか?

bool isError = (false == ofs.good());
if (false == isError) {
    // stuff
}

なぜだめですか:

if ( ofs ) {
    // stuff
}
于 2011-04-18T19:43:18.180 に答える