C ++でクラスをシリアル化および逆シリアル化することは可能ですか?


138

C ++でクラスをシリアル化および逆シリアル化することは可能ですか?

私は3年間Javaを使用してきましたが、その言語ではシリアライゼーション/デシリアライゼーションはかなり簡単です。C ++には同様の機能がありますか?シリアル化を処理するネイティブライブラリはありますか?

例が参考になります。


2
「ネイティブ」の意味がわからない場合、ネイティブC ++(Boost.Serializationなど)を意味しますか?C ++標準ライブラリのみを使用することを意味しますか?他の意味ですか?
jwfearn 2008年

1
「外部のソフトウェアライブラリではない」という意味です。申し訳ありませんが、私の英語はあまり上手ではありません。私はアルゼンチンから来たんだ
アグスティ-N

3
オブジェクトをシリアル化するネイティブの方法はありません(PODからバイナリデータをダンプすることはできますが、必要なものが得られません)。それでも、Boostは「内部ライブラリ」ではありませんが、コンパイラに追加することを検討する必要がある最初の外部ライブラリです。ブーストはSTL品質(つまり、トップガンC ++)です
パート

回答:


95

Boost::serializationライブラリは、むしろエレガントこれを処理します。いくつかのプロジェクトで使用しました。ここには、使用方法を示すプログラム例があります

それを行う唯一のネイティブな方法は、ストリームを使用することです。これは基本的にBoost::serializationライブラリが行うすべてのことであり、オブジェクトをテキストのような形式で書き込み、同じ形式から読み取るようにフレームワークを設定することにより、streamメソッドを拡張します。

タイプ、またはを使用して独自のタイプの内蔵用operator<<及びoperator>>適切に定義され、それはかなり簡単です。詳細については、C ++ FAQ参照してください。


boost :: serializationでは、呼び出し元がオブジェクトの書き込みと読み取りの順序を追跡する必要があるように思えます。あれは正しいですか?したがって、プログラムのバージョン間で2つのフィールドが書き込まれる順序に変更がある場合、互換性がありません。これは正解?
Agnel Kurian 2013

1
それはおそらく、Boost :: serializationコード自体ではなく、シリアル化関数が原因です。
ギークヘッド、2013

1
@ 0xDEADBEEF:これは、binary_(i | o)アーカイブを使用したときにおそらく発生しました。これにより、エンディアンのような他の「問題」が発生します。text_(i | o)archiveを試してください。プラットフォームに依存しません。
Ela782 14

2
特定のフレームワーク/ライブラリソリューションは、受け入れられる答えではありません。
Andrea

3
@Andrea:Boostライブラリは特別なケースです。C ++ 11が完成するまでは、それなしで最新のC ++コードを書くことはほぼ不可能でした。そのため、独立したライブラリよりもセカンダリSTLに近いものでした。
ギーク

52

これは古い投稿ですが、を検索したときに最初に出てくるものの1つですc++ serialization

C ++ 11にアクセスできる人は誰でも、シリアルシリアル化用のC ++ 11ヘッダーのみのライブラリ、シリアル、JSON、XMLをそのまま使用できるcerealをご覧になることをお勧めします。cerealは拡張と使用が簡単になるように設計されており、Boostと同様の構文を持っています。


4
シリアルの良い点は、ブーストとは異なり、メタデータが最小限(ほとんどない)です。boost :: serializationは、アーカイブを開くたびにlibバージョンをストリームに書き込むときに本当に煩わしくなり、ファイルへの追加が不可能になります。
Cyber​​Snoopy 2014

@Cyber​​Snoopy-アーカイブの作成時にこの機能を抑制するためのフラグがあります。もちろん、アーカイブを読み取るときにもこの機能を覚えておく必要があります。
Robert Ramey

16

ブーストは良い提案です。しかし、自分でロールしたい場合は、それほど難しくありません。

基本的には、オブジェクトのグラフを作成し、それらを何らかの構造化ストレージ形式(JSON、XML、YAMLなど)に出力する方法が必要です。グラフの作成は、再帰的な適切なオブジェクトアルゴリズムをマークして、マークされたすべてのオブジェクトを出力するのと同じくらい簡単です。

私は初歩的な(しかしまだ強力な)シリアル化システムについて説明した記事を書きました。おもしろいかもしれません:SQLiteをディスク上のファイル形式として使用する、パート2


14

「組み込み」ライブラリに関する限り、<<および>>はシリアル化専用に予約されています。

<<オブジェクトをシリアル化コンテキスト(通常はiostream)に>>出力し、そのコンテキストからデータを読み取るようにオーバーライドする必要があります。各オブジェクトは、その集約された子オブジェクトを出力する責任があります。

この方法は、オブジェクトグラフにサイクルが含まれていない限り、正常に機能します。

その場合は、ライブラリを使用してこれらのサイクルに対処する必要があります。


3
確かに、それは正しくありません...実装された<<演算子は、人間が読める形式のオブジェクトのテキスト表現を出力するために使用されます。
einpoklum 2014年

1
@einpoklum <<ジェネリックで定義する代わりにostream、ファイルストリームで定義してみてください。
Carcigenicate

1
@Carcigenicate:人間が読み取れるテキストを取得するログファイルはファイルストリームです。
einpoklum、2015年

1
@einpoklumどういう意味かよくわかりません。Frankの言う通りですが、これらの演算子はシリアル化に使用できます。ベクトルをシリアル化/非シリアル化するように定義しました。
カルシジェネート、

2
問題はここにあると思います。<<オブジェクトをシリアル化コンテキストに出力するには、オーバーライドする必要があります…各オブジェクトがその出力を担当します...」 —問題は、各オブジェクトに対してそれを面倒に書き出す必要を回避する方法に関するものです。言語またはライブラリが役立ちますか?
ShreevatsaR 2018年

14

Google プロトコルバッファをお勧めします。新しいプロジェクトでライブラリを試運転する機会があり、それは非常に使いやすいです。ライブラリは、パフォーマンスのために大幅に最適化されています。

Protobufは、オブジェクトをシリアル化するのではなく、仕様に従ってシリアル化するオブジェクトのコードを生成するという意味で、ここで説明する他のシリアル化ソリューションとは異なります。


2
これを使用してサイズが約10-50MBのオブジェクトをシリアル化した経験がありますか?ドキュメントには、プロトコルバッファはサイズが約MBのオブジェクトに最適であると記載されているようです。
Agnel Kurian 2013

私はそれが小さなもののために本当にですので、(まだ)のストリームを使用していない、自分のlibが巻か: gist.github.com/earonesty/5ba3a93f391ea03ef90884840f068767
エリックAronesty

13

ブースト::シリアル化は素晴らしい選択肢ですが、私は、新しいプロジェクトに遭遇しました:穀物私ははるかにエレガント見つけます!私はそれを調査することを強く勧めます。


4

amefプロトコルを確認できます。amefでのC ++エンコードの例は次のようになります。

    //Create a new AMEF object
    AMEFObject *object = new AMEFObject();

    //Add a child string object
    object->addPacket("This is the Automated Message Exchange Format Object property!!","adasd");   

    //Add a child integer object
    object->addPacket(21213);

    //Add a child boolean object
    object->addPacket(true);

    AMEFObject *object2 = new AMEFObject();
    string j = "This is the property of a nested Automated Message Exchange Format Object";
    object2->addPacket(j);
    object2->addPacket(134123);
    object2->addPacket(false);

    //Add a child character object
    object2->addPacket('d');

    //Add a child AMEF Object
    object->addPacket(object2);

    //Encode the AMEF obejct
    string str = new AMEFEncoder()->encode(object,false);

Javaでのデコードは次のようになります、

    string arr = amef encoded byte array value;
    AMEFDecoder decoder = new AMEFDecoder()
    AMEFObject object1 = AMEFDecoder.decode(arr,true);

プロトコルの実装にはC ++とJavaの両方のコーデックがあり、興味深いのは、名前と値のペアの形式でオブジェクトクラスの表現を保持できることです。偶然このプロトコルに偶然出くわしたときに、最後のプロジェクトで同様のプロトコルが必要でした。私の要件に従ってベースライブラリを変更しました。これがお役に立てば幸いです。



3

Sweet Persistは別の問題です。

XML、JSON、Lua、バイナリ形式のストリームとの間でシリアル化を行うことができます。


そのサイトはダウンしているようです、私が見つけることができたすべてはこの古いレポでした
Janusz Syf

2

シリアル化の基礎としてよく使用されるAbstractファクトリーを調べることをお勧めします

C ++ファクトリーに関する別のSOの質問で答えました。フレキシブルな工場に興味がある方はそちらをご覧ください。私はET ++からマクロを使用するための古い方法を説明しようとしていますが、これは私にとってはうまくいきました。

ET ++は、古いMacAppをC ++およびX11に移植するプロジェクトでした。その取り組みの中で、エリックガンマなどはデザインパターンについて考え始めました。ET ++には、実行時のシリアル化とイントロスペクションの自動方法が含まれていました。


0

シンプルで最高のパフォーマンスが必要で、データの下位互換性を気にしない場合は、HPSを試してください。HPSは軽量で、Boostなどよりもはるかに高速で、Protobufなどよりもはるかに使いやすいです。

例:

std::vector<int> data({22, 333, -4444});
std::string serialized = hps::serialize_to_string(data);
auto parsed = hps::parse_from_string<std::vector<int>>(serialized);

0

これが、私がノックアップした単純なシリアライザライブラリです。ヘッダーのみで、c11であり、基本的な型をシリアル化するための例があります。これは、クラスへのマップの1つです。

https://github.com/goblinhack/simple-c-plus-plus-serializer

#include "c_plus_plus_serializer.h"

class Custom {
public:
    int a;
    std::string b;
    std::vector c;

    friend std::ostream& operator<<(std::ostream &out, 
                                    Bits my)
    {
        out << bits(my.t.a) << bits(my.t.b) << bits(my.t.c);
        return (out);
    }

    friend std::istream& operator>>(std::istream &in, 
                                    Bits my)
    {
        in >> bits(my.t.a) >> bits(my.t.b) >> bits(my.t.c);
        return (in);
    }

    friend std::ostream& operator<<(std::ostream &out, 
                                    class Custom &my)
    {
        out << "a:" << my.a << " b:" << my.b;

        out << " c:[" << my.c.size() << " elems]:";
        for (auto v : my.c) {
            out << v << " ";
        }
        out << std::endl;

        return (out);
    }
};

static void save_map_key_string_value_custom (const std::string filename)
{
    std::cout << "save to " << filename << std::endl;
    std::ofstream out(filename, std::ios::binary );

    std::map< std::string, class Custom > m;

    auto c1 = Custom();
    c1.a = 1;
    c1.b = "hello";
    std::initializer_list L1 = {"vec-elem1", "vec-elem2"};
    std::vector l1(L1);
    c1.c = l1;

    auto c2 = Custom();
    c2.a = 2;
    c2.b = "there";
    std::initializer_list L2 = {"vec-elem3", "vec-elem4"};
    std::vector l2(L2);
    c2.c = l2;

    m.insert(std::make_pair(std::string("key1"), c1));
    m.insert(std::make_pair(std::string("key2"), c2));

    out << bits(m);
}

static void load_map_key_string_value_custom (const std::string filename)
{
    std::cout << "read from " << filename << std::endl;
    std::ifstream in(filename);

    std::map< std::string, class Custom > m;

    in >> bits(m);
    std::cout << std::endl;

    std::cout << "m = " << m.size() << " list-elems { " << std::endl;
    for (auto i : m) {
        std::cout << "    [" << i.first << "] = " << i.second;
    }
    std::cout << "}" << std::endl;
}

void map_custom_class_example (void)
{
    std::cout << "map key string, value class" << std::endl;
    std::cout << "============================" << std::endl;
    save_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
    load_map_key_string_value_custom(std::string("map_of_custom_class.bin"));
    std::cout << std::endl;
}

出力:

map key string, value class
============================
save to map_of_custom_class.bin
read from map_of_custom_class.bin

m = 2 list-elems {
    [key1] = a:1 b:hello c:[2 elems]:vec-elem1 vec-elem2
    [key2] = a:2 b:there c:[2 elems]:vec-elem3 vec-elem4
}

0

次のテンプレートを使用してシリアル化を実装しています。

template <class T, class Mode = void> struct Serializer
{
    template <class OutputCharIterator>
    static void serializeImpl(const T &object, OutputCharIterator &&it)
    {
        object.template serializeThis<Mode>(it);
    }

    template <class InputCharIterator>
    static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
    {
        return T::template deserializeFrom<Mode>(it, end);
    }
};

template <class Mode = void, class T, class OutputCharIterator>
void serialize(const T &object, OutputCharIterator &&it)
{
    Serializer<T, Mode>::serializeImpl(object, it);
}

template <class T, class Mode = void, class InputCharIterator>
T deserialize(InputCharIterator &&it, InputCharIterator &&end)
{
    return Serializer<T, Mode>::deserializeImpl(it, end);
}

template <class Mode = void, class T, class InputCharIterator>
void deserialize(T &result, InputCharIterator &&it, InputCharIterator &&end)
{
    result = Serializer<T, Mode>::deserializeImpl(it, end);
}

ここではTあなたがシリアル化するためにしたいタイプですModeシリアライズ、例えば、異なる種類を区別するためのダミータイプです。同じ整数をリトルエンディアン、ビッグエンディアン、varintなどとしてシリアル化できます。

デフォルトでは、Serializerシリアル化されるオブジェクトにタスクを委任します。組み込み型の場合、テンプレートを特殊化する必要がありSerializerます。

便利な関数テンプレートも用意されています。

たとえば、符号なし整数のリトルエンディアンシリアル化:

struct LittleEndianMode
{
};

template <class T>
struct Serializer<
    T, std::enable_if_t<std::is_unsigned<T>::value, LittleEndianMode>>
{
    template <class InputCharIterator>
    static T deserializeImpl(InputCharIterator &&it, InputCharIterator &&end)
    {
        T res = 0;

        for (size_t i = 0; i < sizeof(T); i++)
        {
            if (it == end) break;
            res |= static_cast<T>(*it) << (CHAR_BIT * i);
            it++;
        }

        return res;
    }

    template <class OutputCharIterator>
    static void serializeImpl(T number, OutputCharIterator &&it)
    {
        for (size_t i = 0; i < sizeof(T); i++)
        {
            *it = (number >> (CHAR_BIT * i)) & 0xFF;
            it++;
        }
    }
};

次にシリアライズするには:

std::vector<char> serialized;
uint32_t val = 42;
serialize<LittleEndianMode>(val, std::back_inserter(serialized));

逆シリアル化するには:

uint32_t val;
deserialize(val, serialized.begin(), serialized.end());

抽象イテレータロジックにより、イテレータ(ストリームイテレータなど)、ポインタなどと連携する必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.