C ++でクラスをシリアル化および逆シリアル化することは可能ですか?
私は3年間Javaを使用してきましたが、その言語ではシリアライゼーション/デシリアライゼーションはかなり簡単です。C ++には同様の機能がありますか?シリアル化を処理するネイティブライブラリはありますか?
例が参考になります。
C ++でクラスをシリアル化および逆シリアル化することは可能ですか?
私は3年間Javaを使用してきましたが、その言語ではシリアライゼーション/デシリアライゼーションはかなり簡単です。C ++には同様の機能がありますか?シリアル化を処理するネイティブライブラリはありますか?
例が参考になります。
回答:
Boost::serialization
ライブラリは、むしろエレガントこれを処理します。いくつかのプロジェクトで使用しました。ここには、使用方法を示すプログラム例があります。
それを行う唯一のネイティブな方法は、ストリームを使用することです。これは基本的にBoost::serialization
ライブラリが行うすべてのことであり、オブジェクトをテキストのような形式で書き込み、同じ形式から読み取るようにフレームワークを設定することにより、streamメソッドを拡張します。
タイプ、またはを使用して独自のタイプの内蔵用operator<<
及びoperator>>
適切に定義され、それはかなり簡単です。詳細については、C ++ FAQを参照してください。
これは古い投稿ですが、を検索したときに最初に出てくるものの1つですc++ serialization
。
C ++ 11にアクセスできる人は誰でも、シリアル、シリアル化用のC ++ 11ヘッダーのみのライブラリ、シリアル、JSON、XMLをそのまま使用できるcerealをご覧になることをお勧めします。cerealは拡張と使用が簡単になるように設計されており、Boostと同様の構文を持っています。
ブーストは良い提案です。しかし、自分でロールしたい場合は、それほど難しくありません。
基本的には、オブジェクトのグラフを作成し、それらを何らかの構造化ストレージ形式(JSON、XML、YAMLなど)に出力する方法が必要です。グラフの作成は、再帰的な適切なオブジェクトアルゴリズムをマークして、マークされたすべてのオブジェクトを出力するのと同じくらい簡単です。
私は初歩的な(しかしまだ強力な)シリアル化システムについて説明した記事を書きました。おもしろいかもしれません:SQLiteをディスク上のファイル形式として使用する、パート2。
「組み込み」ライブラリに関する限り、<<
および>>
はシリアル化専用に予約されています。
<<
オブジェクトをシリアル化コンテキスト(通常はiostream
)に>>
出力し、そのコンテキストからデータを読み取るようにオーバーライドする必要があります。各オブジェクトは、その集約された子オブジェクトを出力する責任があります。
この方法は、オブジェクトグラフにサイクルが含まれていない限り、正常に機能します。
その場合は、ライブラリを使用してこれらのサイクルに対処する必要があります。
<<
演算子は、人間が読める形式のオブジェクトのテキスト表現を出力するために使用されます。
<<
ジェネリックで定義する代わりにostream
、ファイルストリームで定義してみてください。
<<
オブジェクトをシリアル化コンテキストに出力するには、オーバーライドする必要があります…各オブジェクトがその出力を担当します...」 —問題は、各オブジェクトに対してそれを面倒に書き出す必要を回避する方法に関するものです。言語またはライブラリが役立ちますか?
Google プロトコルバッファをお勧めします。新しいプロジェクトでライブラリを試運転する機会があり、それは非常に使いやすいです。ライブラリは、パフォーマンスのために大幅に最適化されています。
Protobufは、オブジェクトをシリアル化するのではなく、仕様に従ってシリアル化するオブジェクトのコードを生成するという意味で、ここで説明する他のシリアル化ソリューションとは異なります。
ブースト::シリアル化は素晴らしい選択肢ですが、私は、新しいプロジェクトに遭遇しました:穀物私ははるかにエレガント見つけます!私はそれを調査することを強く勧めます。
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の両方のコーデックがあり、興味深いのは、名前と値のペアの形式でオブジェクトクラスの表現を保持できることです。偶然このプロトコルに偶然出くわしたときに、最後のプロジェクトで同様のプロトコルが必要でした。私の要件に従ってベースライブラリを変更しました。これがお役に立てば幸いです。
他のポスターで説明されているように、ブーストシリアライゼーションを使用することをお勧めします。これは、ブーストチュートリアルをうまく補完する、使用方法に関する詳細なチュートリアルです。http://www.ocoudert.com/blog/2011/07/09/a-practical-guide-to-c-serialization/
Sweet Persistは別の問題です。
XML、JSON、Lua、バイナリ形式のストリームとの間でシリアル化を行うことができます。
シンプルで最高のパフォーマンスが必要で、データの下位互換性を気にしない場合は、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);
これが、私がノックアップした単純なシリアライザライブラリです。ヘッダーのみで、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
}
次のテンプレートを使用してシリアル化を実装しています。
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());
抽象イテレータロジックにより、イテレータ(ストリームイテレータなど)、ポインタなどと連携する必要があります。