Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Boost sg msgpack

3,417 views

Published on

Published in: Technology

Boost sg msgpack

  1. 1. MessagePackとAPIバージョニング 近藤 貴俊 2015/5/29 1Copyright OGIS-RI 2015
  2. 2. • 近藤貴俊 • 株式会社オージス総研 – IoT分野でMessagePackを利用した データの収集、制御を行う プラットフォームを開発している • msgpack-c コミッタの一人 – 後述するMessagePackフォーマットを扱うための C/C++ 実装 – C++版は Cのwrapperではない • C++Nowに ここ6年くらい 連続参加中 2015/5/29 Copyright OGIS-RI 2015 2 自己紹介
  3. 3. 2015/5/29 Copyright OGIS-RI 2015 3 MessagePackとは
  4. 4. 2015/5/29 Copyright OGIS-RI 2015 4 MessagePackとは
  5. 5. 2015/5/29 Copyright OGIS-RI 2015 5 MessagePackとは
  6. 6. 2015/5/29 Copyright OGIS-RI 2015 6 MessagePackとは
  7. 7. 2015/5/29 Copyright OGIS-RI 2015 7 MessagePackとは
  8. 8. 2015/5/29 Copyright OGIS-RI 2015 8 MessagePackとは
  9. 9. 2015/5/29 Copyright OGIS-RI 2015 9 MessagePackとは https://github.com/msgpack/msgpack/blob/master/spec.md
  10. 10. • Website http://msgpack.org/ • JSONと同様 – ポータブル – 基本的な型情報を内包 – コンポジットなデータ構造(詳細は次ページで) • JSONとの違い – サイズが小さい – バイナリフォーマット – バイナリデータもそのまま扱える – 整数と浮動小数点数を区別 – Parseが容易で計算負荷が小さい 2015/5/29 Copyright OGIS-RI 2015 10 MessagePackとは?
  11. 11. 2015/5/29 Copyright OGIS-RI 2015 11 MessagePackとは? msgpack::object nil boolean u64 i64 f64 str bin ext array map * * double, float object_kv key val ※ クラス名は msgpack-c の名称で記載
  12. 12. MessagePackをサポートする言語 2015/5/29 Copyright OGIS-RI 2015 12
  13. 13. MessagePackをサポートする言語 2015/5/29 Copyright OGIS-RI 2015 13 このリストは github から自動収集して生成されているため どのレベルで実装されているかは要確認
  14. 14. 2015/5/29 Copyright OGIS-RI 2015 14 msgpack-c 中でも、C++にフォーカスをあてた内容で、 Cには当てはまらない内容もあるので注意
  15. 15. Pack/Unpack 2015/5/29 15 #include <tuple> #include <string> #include <sstream> #include <msgpack.hpp> int main() { auto t1 = std::make_tuple("hello", true, 42, 12.3); std::stringstream ss; msgpack::pack(ss, t1); msgpack::unpacked unpacked = msgpack::unpack(ss.str().data(), ss.str().size()); msgpack::object obj = unpacked.get(); auto t2 = obj.as<std::tuple<std::string, bool, int, double>>(); assert(t1 == t2); } write(const char*, std::size_t) メンバ関数を持つ、任意の型 Copyright OGIS-RI 2015 pack unpack convert
  16. 16. C++/object/byte stream 2015/5/29 16Copyright OGIS-RI 2015 C++ Types msgpack::object Byte stream pack object pack unpack convert adaptor
  17. 17. Adaptor 2015/5/29 17Copyright OGIS-RI 2015 https://github.com/msgpack/msgpack-c/wiki/v1_1_cpp_adaptor C++ type msgpack::object type bool bool char* str std::deque array char positive/negative integer signed ints *1 positive/negative integer unsigned ints *2 positive integer std::list array std::map array std::pair array std::set array std::string str std::vector array std::vector<char> bin *1 signed ints signed char, signed short, signed int, signed long, signed long long *2 unsigned ints unsigned char, unsigned short, unsigned int, signed long, signed long long C++11 type msgpack:: object type std::array array std::array<char> bin std::forward_list array std::tuple array std::array array std::unordered_map array std::unordered_set array boost type msgpack:: object type boost::optional<T> T boost::string_ref str
  18. 18. 自前のクラスのmsgpack対応 2015/5/29 18Copyright OGIS-RI 2015 https://github.com/msgpack/msgpack-c/wiki/v1_1_cpp_adaptor #include <msgpack.hpp> struct your_class { int a; std::string b; MSGPACK_DEFINE(a, b); }; intrusive
  19. 19. 自前のクラスのmsgpack対応 2015/5/29 19Copyright OGIS-RI 2015 https://github.com/msgpack/msgpack-c/wiki/v1_1_cpp_adaptor #include <msgpack.hpp> struct my { int a; std::string b; }; namespace msgpack { template <typename Stream> inline packer<Stream>& operator<< (packer<Stream>& o, const my& v) { o.pack_array(2); o << v.a; o << v.b; return o; } } // namespace msgpack non-intrusive
  20. 20. ADLを用いたアダプタの実現 2015/5/29 20Copyright OGIS-RI 2015 namespace msgpack { template <typename Stream> class packer { public: packer(Stream&) {} template <typename T> packer<Stream>& pack(const T& v) { *this << v; return *this; } }; // Default behavior template <typename Stream, typename T> inline packer<Stream>& operator<< (packer<Stream>& o, const T&) { std::cout << "Default serialize" << std::endl; return o; } // API template <typename Stream, typename T> inline void pack(Stream& s, const T& v) { packer<Stream>(s).pack(v); } } // namespace msgpack int main() { std::stringstream ss; msgpack::pack(ss, 1); msgpack::pack(ss, 1.2); } http://melpon.org/wandbox/permlink/L0WpfUz2hSUZ3ItM version 0.5.9 まで
  21. 21. ADLを用いたアダプタの実現 2015/5/29 21Copyright OGIS-RI 2015 namespace msgpack { template <typename Stream> class packer { public: packer(Stream&) {} template <typename T> packer<Stream>& pack(const T& v) { *this << v; return *this; } }; // Default behavior template <typename Stream, typename T> inline packer<Stream>& operator<< (packer<Stream>& o, const T&); // User defined adaptor template <typename Stream> inline packer<Stream>& operator<< (packer<Stream>& o, int) { std::cout << "Int serialize" << std::endl; return o; } } // namespace msgpack int main() { std::stringstream ss; msgpack::pack(ss, 1); msgpack::pack(ss, 1.2); } http://melpon.org/wandbox/permlink/L0WpfUz2hSUZ3ItM version 0.5.9 まで ユーザ定義のoperator<<オーバーロード
  22. 22. APIのバージョニング 2015/5/29 22Copyright OGIS-RI 2015 • msgpack-c の C++版はヘッダオンリーライブラリとなった • ヘッダオンリーライブラリであっても、古いABIのmsgpack-c をincludeして作ったライブラリ(.a , .LIBなど)を、 新しいABIのmsgpack-cと合わせて使うと問題が生じる – std::listのサイズをO(1)で求めることになった際にも 同様の問題が発生した • これを防ぐために、ABIに変更が生じた場合、 古いバージョンはそのまま使い続けられるようにしたい • ABIに関わらないバグフィックスなどは 古いバージョンにも反映していきたい msgpack.hpp userlib.cpplibuser.a msgpack.hpp client.cpp include 生成 include link
  23. 23. APIのバージョニング 2015/5/29 23Copyright OGIS-RI 2015 Inline namespace The inline namespace mechanism is intended to support library evolution by providing a mechanism that support a form of versioning. Consider: inline namespace V99 { void f(int); // does something better than the V98 version void f(double); // new feature // ... } namespace V98 { void f(int); // does something // ... } namespace Mine { #include "V99.h" #include "V98.h" } We here have a namespace Mine with both the latest release (V99) and the previous one (V98). If you want to be specific, you can: #include "Mine.h" using namespace Mine; V98::f(1); // old version V99::f(1); // new version f(1); // default version The point is that the inline specifier makes the declarations from the nested namespace appear exactly as if they had been declared in the enclosing namespace.This is a very ``static'' and implementer-oriented facility in that the inline specifier has to be placed by the designer of the namespaces -- thus making the choice for all users. It is not possible for a user of Mine to say ``I want the default to be V98rather than V99.'' file V99.h file V98.h file Mine.h http://www.stroustrup.com/C++11FAQ.html#inline-namespace
  24. 24. APIのバージョニング 2015/5/29 24Copyright OGIS-RI 2015 #include <iostream> namespace msgpack { // lib code v1 inline namespace v1 { inline void foo() { std::cout << "v1::foo()" << std::endl; } inline void bar() { std::cout << "v1::bar()" << std::endl; foo(); // A msgpack::foo(); // B msgpack::v1::foo(); // C } } } // client code int main() { std::cout << "msgpack::bar();" << std::endl; msgpack::bar(); std::cout << "msgpack::v1::bar();" << std::endl; msgpack::v1::bar(); } http://melpon.org/wandbox/permlink/ncTAeERuXByq80Y3 msgpack::bar(); v1::bar() v1::foo() v1::foo() v1::foo() msgpack::v1::bar(); v1::bar() v1::foo() v1::foo() v1::foo()
  25. 25. APIのバージョニング 2015/5/29 25Copyright OGIS-RI 2015 namespace msgpack { namespace v1 { void bar(); } inline namespace v2 { using v1::bar; void foo() { std::cout << "v2::foo()" << std::endl; } } namespace v1 { void foo() { std::cout << "v1::foo()" << std::endl; } void bar() { std::cout << "v1::bar()" << std::endl; foo(); // A bar()が定義されている名前空間v1のfoo()が呼び出される msgpack::foo(); // B v2のfoo()が呼び出される msgpack::v1::foo(); // C 明示的に指定された名前空間のfoo()が呼び出される } } } http://melpon.org/wandbox/permlink/8HBPTxCmuTiuluaz msgpack::bar(); v1::bar() v1::foo() v2::foo() v1::foo() msgpack::v1::bar(); v1::bar() v1::foo() v2::foo() v1::foo() BとCを上手く使い分けることでバージョニングを実現
  26. 26. APIのバージョニング 2015/5/29 26Copyright OGIS-RI 2015 A B C msgpack::B() msgpack::C() V1 D msgpack::D() inline namespace
  27. 27. APIのバージョニング 2015/5/29 27Copyright OGIS-RI 2015 A B C A ABI変更 msgpack::B() msgpack::C() V1 V2 D msgpack::D() msgpack::B() inline namespace using v1::B using V1::C using v1::D
  28. 28. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 28Copyright OGIS-RI 2015 A B C A C ABI変更 msgpack::B() msgpack::C() V1 V2 V3 D msgpack::D() msgpack::B() inline namespace msgpack::D() using v2::D
  29. 29. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 29Copyright OGIS-RI 2015 A B C A C ABI変更 msgpack::B() msgpack::C() V1 V2 V3 D msgpack::D() msgpack::B() inline namespace msgpack::D() using v2::D
  30. 30. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 30Copyright OGIS-RI 2015 A B' C A C ABI変更 msgpack::B() msgpack::C() V1 V2 V3 D msgpack::D() msgpack::B() inline namespace msgpack::D() using v2::D CのABI変更対応
  31. 31. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 31Copyright OGIS-RI 2015 A B' C A C ABI変更 msgpack::B() msgpack::C() V1 V2 V3 D msgpack::D() msgpack::B() inline namespace msgpack::D() using v2::A using v2::B using v2::D CのABI変更対応 B'のABIに変更が無ければこれでOK
  32. 32. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 32Copyright OGIS-RI 2015 A B' C A C ABI変更 msgpack::B() msgpack::C() V1 V2 V3 D msgpack::D() msgpack::B() inline namespace msgpack::D() using v2::A using v2::B using v2::D B'のABIに変更が生じる場合 CのABI変更対応
  33. 33. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 33Copyright OGIS-RI 2015 A B'' C A C ABI変更 msgpack::B() msgpack::v1::C() V1 V2 V3 D msgpack::D() msgpack::B() inline namespace msgpack::バージョン名前空間:: 修飾することで 明示的に特定バージョンの名前空間を参照 msgpack::名前空間修飾することで inline namespaceを参照 msgpack::D() using v2::D CのABI変更対応 をここでは行わない
  34. 34. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 34Copyright OGIS-RI 2015 A B'' C A C ABI変更 msgpack::B() V1 V2 V3 D msgpack::D() Bmsgpack::B() inline namespace msgpack::v1::C() msgpack::D() msgpack::C() using v2::D CのABI変更対応 ABI変更
  35. 35. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 35Copyright OGIS-RI 2015 A B'' C A C ABI変更 msgpack::B() V1 V2 V3 D msgpack::D() B CのABI変更対応 msgpack::B() inline namespace msgpack::v1::C() msgpack::D() msgpack::C() using v2::D ABI変更
  36. 36. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 36Copyright OGIS-RI 2015 A' B'' C A' C ABI変更 msgpack::B() V1 V2 V3 D msgpack::D() B CのABI変更対応 msgpack::B() inline namespace msgpack::v1::C() msgpack::D() msgpack::C() using v2::D ABI変更 BのABI変更対応BのABI変更対応
  37. 37. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 37Copyright OGIS-RI 2015 A' B'' C A' C ABI変更 msgpack::B() V1 V2 V3 D msgpack::D() B CのABI変更対応 msgpack::B() inline namespace msgpack::v1::C() msgpack::D() msgpack::C() using v2::A using v2::D ABI変更 BのABI変更対応BのABI変更対応 A'のABIに変更が無ければこれでOK
  38. 38. using v1::B using V1::C using v1::D APIのバージョニング 2015/5/29 38Copyright OGIS-RI 2015 A'' B' C A'' C ABI変更 V1 V2 V3 D msgpack::D() B ABI変更対応 A inline namespace msgpack::v1::C() msgpack::v1::B() msgpack::v1::B() msgpack::D() msgpack::C() msgpack::B() using v1::D A'のABIに変更がある場合
  39. 39. ADL問題 2015/5/29 39Copyright OGIS-RI 2015 namespace msgpack { template <typename Stream> class packer { public: packer(Stream&) {} template <typename T> packer<Stream>& pack(const T& v) { *this << v; return *this; } }; // Default behavior template <typename Stream, typename T> inline packer<Stream>& operator<< (packer<Stream>& o, const T&) { std::cout << "Default serialize" << std::endl; return o; } // API template <typename Stream, typename T> inline void pack(Stream& s, const T& v) { packer<Stream>(s).pack(v); } } // namespace msgpack int main() { std::stringstream ss; msgpack::pack(ss, 1); msgpack::pack(ss, 1.2); } http://melpon.org/wandbox/permlink/L0WpfUz2hSUZ3ItM msgpack::operator(*this, v); と書かなければ、inline namespaceは 参照されない
  40. 40. ADL問題 2015/5/29 40Copyright OGIS-RI 2015 namespace msgpack { template <typename Stream> class packer { public: packer(Stream&) {} template <typename T> packer<Stream>& pack(const T& v) { msgpack::operator<<(*this, v); return *this; } }; // Default behavior template <typename Stream, typename T> inline packer<Stream>& operator<< (packer<Stream>& o, const T&) { std::cout << "Default serialize" << std::endl; return o; } // API template <typename Stream, typename T> inline void pack(Stream& s, const T& v) { packer<Stream>(s).pack(v); } } // namespace msgpack int main() { std::stringstream ss; msgpack::pack(ss, 1); msgpack::pack(ss, 1.2); } http://melpon.org/wandbox/permlink/WlEDZo53McLh61AT error: no member named 'operator<<' in namespace 'msgpack'
  41. 41. ADL問題 2015/5/29 41Copyright OGIS-RI 2015 namespace msgpack { template <typename Stream> class packer { public: packer(Stream&) {} template <typename T> packer<Stream>& pack(const T& v) { msgpack::operator<<(*this, v); return *this; } }; // Default behavior template <typename Stream, typename T> inline packer<Stream>& operator<< (packer<Stream>& o, const T&) { std::cout << "Default serialize" << std::endl; return o; } // API template <typename Stream, typename T> inline void pack(Stream& s, const T& v) { packer<Stream>(s).pack(v); } } // namespace msgpack template <typename Stream> class packer; template <typename Stream, typename T> packer<Stream>& operator<< (packer<Stream>& o, const T&); http://melpon.org/wandbox/permlink/Mkp68SjWv9jsEqlD 先行宣言を入れてみると。。。
  42. 42. ADL問題 2015/5/29 42Copyright OGIS-RI 2015 namespace msgpack { template <typename Stream> class packer { public: packer(Stream&) {} template <typename T> packer<Stream>& pack(const T& v) { msgpack::operator<<(*this, v); return *this; } }; // Default behavior template <typename Stream, typename T> inline packer<Stream>& operator<< (packer<Stream>& o, const T&){...} // User defined adaptor template <typename Stream> inline packer<Stream>& operator<< (packer<Stream>& o, int) { std::cout << "Int serialize" << std::endl; return o; } } // namespace msgpack http://melpon.org/wandbox/permlink/Mkp68SjWv9jsEqlD template <typename Stream> class packer; template <typename Stream, typename T> packer<Stream>& operator<< (packer<Stream>& o, const T&); int main() { std::stringstream ss; msgpack::pack(ss, 1); msgpack::pack(ss, 1.2); } ユーザ定義のオーバーロードが呼ばれない
  43. 43. ADL問題 2015/5/29 43Copyright OGIS-RI 2015 namespace msgpack { template <typename Stream> class packer { public: packer(Stream&) {} template <typename T> packer<Stream>& pack(const T& v) { msgpack::operator<<(*this, v); return *this; } }; // Default behavior template <typename Stream, typename T> inline packer<Stream>& operator<< (packer<Stream>& o, const T&){...} // User defined adaptor template <typename Stream> inline packer<Stream>& operator<< (packer<Stream>& o, int) { std::cout << "Int serialize" << std::endl; return o; } } // namespace msgpack http://melpon.org/wandbox/permlink/tE5b4taP6cQPicXb template <typename Stream> class packer; template <typename Stream, typename T> packer<Stream>& operator<< (packer<Stream>& o, const T&); template <typename Stream> packer<Stream>& operator<< (packer<Stream>& o, int); int main() { std::stringstream ss; msgpack::pack(ss, 1); msgpack::pack(ss, 1.2); } ユーザ定義のオーバーロードの先行宣言を入れると 期待通りディスパッチされる
  44. 44. ADL問題 2015/5/29 44Copyright OGIS-RI 2015 msgpackの先行宣言群 ユーザの先行宣言群 msgpackのライブラリ本体 ユーザのコード本体 #include <msgpack_fwd.hpp> #include <msgpack.hpp> user.cpp このアプローチの行き着く先にあるもの ユーザコードの書き方に大きな制約を課してしまう
  45. 45. なにが問題なのか? 2015/5/29 45Copyright OGIS-RI 2015 • オーバーロードによるアダプタの実現 – オーバーロードは、呼び出しコードがinstantiateした際、 そこから見えている関数が候補となる – ADLによって、呼び出しコードのinstantiate を遅延 – ADLを使うためには、名前空間修飾してはならない • APIバージョニングの実現 – 名前空間修飾しないとAPIバージョニングできない • この2つの相性が悪い
  46. 46. オーバーロードに変わる手段 2015/5/29 46Copyright OGIS-RI 2015 template <typename Stream> class packer; template <typename Stream, typename T> packer<Stream>& operator<< (packer<Stream>& o, const T&); class packer; template <typename T> packer& operator<< (packer& o, const T&); template <> packer& operator<< <int> (packer& o, const int&); http://melpon.org/wandbox/permlink/Jf9HNZaV8IFFE32W template <typename Stream> packer<Stream>& operator<< <int> (packer<Stream>& o, const int&); テンプレートの特殊化を使えばinstantiateは遅延される しかし、関数テンプレートの部分特殊化はできない もし、packerがクラステンプレートで無ければ、完全特殊化で対応できるのだが 部分特殊化するためには、関数テンプレートではなくクラステンプレートが必要 T を 特殊化した int に置き換えるため、 引数をconst int& にしなければならない
  47. 47. オーバーロードに変わる手段 2015/5/29 47Copyright OGIS-RI 2015 template <typename Stream, typename T> packer<Stream>& operator<< (packer<Stream>& o, const T&); template <typename T> struct pack { template <typename Stream> msgpack::packer<Stream>& operator()(msgpack::packer<Stream>&, T const&) const; }; template <typename Stream> packer<Stream>& operator<< (packer<Stream>& o, int); template <> struct pack<int> { template <typename Stream> msgpack::packer<Stream>& operator()(msgpack::packer<Stream>&, int) const; }; 今回のケースでは、クラステンプレートの(完全)特殊化と メンバ関数テンプレートという構成で対応できた いままでの、関数テンプレートオーバーロードによるAdaptor クラステンプレートの特殊化によるAdaptor http://melpon.org/wandbox/permlink/pcJUNyGh7zHMYLmS
  48. 48. オーバーロードに変わる手段 2015/5/29 48Copyright OGIS-RI 2015 #include <iostream> #include <sstream> namespace msgpack { template <typename Stream> class packer; namespace adaptor { template <typename T> struct pack { template <typename Stream> packer<Stream>& operator()(packer<Stream>& o, const T&) const { std::cout << "Default serialize" << std::endl; return o; } }; } // namespace adaptor template <typename Stream> class packer { public: packer(Stream&) {} template <typename T> packer<Stream>& pack(const T& v) { msgpack::adaptor::pack<T>()(*this, v); return *this; } }; // API template <typename Stream, typename T> inline void pack(Stream& s, const T& v) { packer<Stream>(s).pack(v); } } // namespace msgpack http://melpon.org/wandbox/permlink/pcJUNyGh7zHMYLmS // User defined adaptor namespace msgpack { namespace adaptor { template <> struct pack<int> { template <typename Stream> packer<Stream>& operator()(packer<Stream>& o, int) const { std::cout << "Int serialize" << std::endl; return o; } }; } // namespace adaptor } // namespace msgpack int main() { std::stringstream ss; msgpack::pack(ss, 1); msgpack::pack(ss, 1.2); }
  49. 49. まとめ 2015/5/29 49Copyright OGIS-RI 2015 • ユーザ定義の型などに対して 拡張性を持たせたい場合 関数のオーバーロードよりも クラステンプレートの特殊化の方が柔軟に対応できる – 例:名前空間によるAPIバージョニングとの共存 • 将来が予測できない場合、最も柔軟なメカニズムで 実装しておくと後々苦労しない – msgpack-cは、ライブラリが提供している、 既存の全てのオーバーロードベースのアダプタを クラステンプレートに書き換えた – 書き換えの際、当然ながら既存のユーザコードへの影響が出た – msgpack-cではこれを メジャーバージョンアップに伴う破壊的変更とし、 マイグレーションガイドなどのドキュメントも用意した

×