C++
library
serializer
cereal

cereal を使いはじめるにあたって確認した事、はまった事

More than 1 year has passed since last update.

概要

先日投稿されていた 「C++のcerealのシリアライズが快適すぎるやばい」 を読んでこれは良いと思い、早速今日からお仕事でも使おうとはじめに確認した事や少々まはった事をまとめておきます。

確認した事

1. std::uint64_t を JSON でシリアライズするとどうなるか?

答え: C++er の期待通りにシリアライズされる。心配ない。

#include <cereal/cereal.hpp>
#include <cereal/archives/json.hpp>
#include <limits>
#include <cstint>
#include <iostream>

auto main() -> int
{
  cereal::JSONOutputArchive( std::cout )( std::numeric_limits< std::uint64_t >::max() );
}
{
    "value0": 18446744073709551615
}

だそく: 以前JSONを扱いたい状況で picojson を使おうとサンプルも書いて提案したら「 64-bit 値を数値で扱えないみたいなんですがー」と言われ採用できない事がありました。picojson だと PICOJSON_USE_INT64 しても std::uint64_t はサポートされなかった悲しみの思い出。

2. enum class を手間なくシリアライズできるか?

答え: 全自動で std::underlying_type を見てシリアライズしてくれる。楽々。

#include <cereal/cereal.hpp>
#include <cereal/archives/json.hpp>
#include <cstint>
#include <iostream>

enum class pon_type
  : std::uint8_t
{ none
, ajipon
, yuzupon
, sudachipon
};

auto main() -> int
{
  cereal::JSONOutputArchive( std::cout )( pon_type::yuzupon );
}
{
    "value0": 2
}{

3. Binary シリアライズでエンディアンはどうなるか?

答え: Binary 系 API を使うとエンディアンでアプリが爆発するかもしれないけれど、そんなユースケースには PortableBinary 系 API があるから大丈夫だよ。

Binary 系 API の場合

#include <cereal/cereal.hpp>
#include <cereal/archives/json.hpp>
#include <cstint>
#include <fstream>

auto main() -> int
{
  std::ofstream f( "a.bin", std::ios::binary );
  if ( f.is_open() )
    ( cereal::BinaryOutputArchive( f ) )( static_cast< std::uint16_t >( 0x1234 ) );
}

intel Core i7 でビルドし実行した結果を od -t x1 a.bin で眺めると:

0000000 34 12
0000002

PortableBinary 系 API の場合

#include <cereal/cereal.hpp>
#include <cereal/archives/json.hpp>
#include <cstint>
#include <fstream>

auto main() -> int
{
  std::ofstream f( "a.bin", std::ios::binary );
  if ( f.is_open() )
    ( cereal::BinaryOutputArchive( f ) )( static_cast< std::uint16_t >( 0x1234 ) );
}

intel Core i7 でビルドし実行した結果を od -t x1 a.bin で眺めると:

0000000 01 34 12
0000003

エンディアンのフラグが先頭に 1 byte くっつく。

はまった事

1. std::map std::unordered_map 試そ -> error

状況: とりあえず基本的なシリアライザーの動作を確認しよう、 std::uint64_t 、 enum class 、 std::vector 、 std::unordered_map ... compile error !!

#include <cereal/cereal.hpp>
#include <cereal/unordered_map.hpp>
#include <cereal/archives/json.hpp>
#include <unordered_map>
#include <string>
#include <iostream>
#include <cstint>

auto main() -> int
{
  std::unordered_map< std::string, std::uint16_t > data
  { { "alice"  , 0x1122 }
  , { "bob"    , 0x3344 }
  , { "charlie", 0x5566 }
  };
  cereal::JSONOutputArchive( std::cout )( data );
}

翻訳時エラー:

cereal/include/cereal/cereal.hpp:448:9: error: static assertion failed: cereal could not find any output serialization functions for the provided type and archive combination.

 Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these).
 Serialize functions generally have the following signature:

 template<class Archive>
   void serialize(Archive & ar)
   {
     ar( member1, member2, member3 );
   }


         static_assert(traits::detail::count_output_serializers<T, ArchiveType>::value != 0,
         ^

答え: #include <cereal/types/string.hpp> が抜けてた (ノω・)テヘ

しばらく cereal が内蔵で対応してるはずだし types/unordered_map も読んでるしなんでやろ?w? などと間抜けな状況を経験した。

ちなみに、このエラー、本来はユーザー定義のシリアライズを行おうとした時にユーザーへ親切丁寧に問題を知らせてくれるもの。C++にしては気配りのよく効いたいい子ですね・w・b

所感

とても扱い易く楽に使えて嬉しい C++er に愛されそうなシリアライザーライブラリーですね。STL対応もお馴染みのコンテナーやコンテナーアダプター群はもちろん、 std::valarray std::complex std::tuple std::bitset などにまで対応が広がっていて多くの状況で手間なく安心して利用できます。さっそく、今日の午後のお仕事から私も実用して参りましょう( ・`ω・´)

Reference