今までCLI/SV間のデータのやり取りにはJSONを利用してきましたが、調べてみるとMessagePackの方が利点が多そうなので動作の確認をしました
データ構造の定義について
あらかじめstructを全て定義しておく必要があるようです
structの入れ子構造にも対応しています
MSGPACK_DEFINEというマクロでシリアライズしたいフィールドを定義します
struct sFoo
{
std::string name;
int secret;
MSGPACK_DEFINE( name );
};
変換も簡単
serialize
auto origin = std::make_unique<sFoo>();
sTest->name = "foo";
msgpack::sbuffer sbuf;
msgpack::pack( sbuf, *( origin.get() ) );
deserialize
std::string msg = // 受け取ったconst char*
msgpack::object_handle oh = msgpack::unpack( msg.c_str(), msg.size() );
msgpack::object deserialized = oh.get();
auto clone = std::make_unique<sFoo>();
deserialized.convert( *( clone.get() ) );
unique_ptrもしっかりコピーしてくれるし、nullptr(nullableなフィールドも定義可能)にも対応してます
データの中身を参照する
std::stringstream ss;
ss << deserialized;
// 出力は [["child1"],[4,[2,4,6,8]],null] のようになる
公式ではstd::coutに渡していましたが、Stream write(const char*, size_t s)を持つ型ならなんでもいいそう
JSONのpritty formatに比べるとインデント/改行やキーがないのでデータが大きくなると結構見づらい
変換の成否判定
try
{
auto failed = std::make_unique<sBar>();
deserialized.convert( *( failed.get() ) );
}
catch ( const std::bad_cast& e )
{
// FAILED!!
}
変換できなかったときstd::bad_castエラーを吐きます
JSONと違ってメンバーチェック等は必要ないし、Import/Export関数も書かなくてよい
確認したコード
msgpack-cのUsageに沿って確認
msgpacktest.cpp
#include <msgpack.hpp>
struct sChild1
{
std::string name;
MSGPACK_DEFINE( name );
};
struct sChild2
{
int num;
std::vector<int> nums;
MSGPACK_DEFINE( num, nums );
};
struct sParent
{
std::unique_ptr<sChild1> child1;
std::unique_ptr<sChild2> child2;
std::unique_ptr<sChild2> nullable;
MSGPACK_DEFINE( child1, child2, nullable );
};
int main()
{
auto origin = std::make_unique<sParent>();
origin->child1 = std::make_unique<sChild1>();
origin->child2 = std::make_unique<sChild2>();
origin->nullable = nullptr;
origin->child1->name = "child1";
origin->child2->num = 4;
origin->child2->nums = { 2, 4, 6, 8 };
// serialize
msgpack::sbuffer sbuf;
msgpack::pack( sbuf, *( origin.get() ) );
// 元のEntityを削除してみる
origin = nullptr;
// deserialize
msgpack::object_handle oh = msgpack::unpack( sbuf.data(), sbuf.size() );
msgpack::object deserialized = oh.get();
auto clone = std::make_unique<sParent>();
deserialized.convert( *( clone.get() ) );
if ( clone->nullable == nullptr )
{
Log::Console( "nullable is null." );
}
Log::Console( "child1 %s, child2 num %u", clone->child1->name.c_str(), clone->child2->num );
std::stringstream ss;
ss << deserialized;
Log::Console( ss.str().c_str() );
try
{
auto failed = std::make_unique<sChild1>();
deserialized.convert( *( failed.get() ) );
}
catch ( const std::bad_cast& e )
{
Log::Console( e.what() );
}
return 0;
}
nullable is null.
child1 child1, child2 num 4
[["child1"],[4,[2,4,6,8]],null]
std::bad_cast