boost sg msgpack

49
MessagePackAPIバージョニング 近藤 貴俊 2015/5/29 1 Copyright OGIS-RI 2015

Upload: takatoshi-kondo

Post on 21-Jan-2017

2.829 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Boost sg msgpack

MessagePackとAPIバージョニング

近藤 貴俊

2015/5/29 1 Copyright OGIS-RI 2015

Page 2: Boost sg msgpack

• 近藤貴俊

• 株式会社オージス総研 – IoT分野でMessagePackを利用した データの収集、制御を行う プラットフォームを開発している

• msgpack-c コミッタの一人 – 後述するMessagePackフォーマットを扱うための

C/C++ 実装

– C++版は Cのwrapperではない

• C++Nowに ここ6年くらい 連続参加中

2015/5/29 Copyright OGIS-RI 2015 2

自己紹介

Page 3: Boost sg msgpack

2015/5/29 Copyright OGIS-RI 2015 3

MessagePackとは

Page 4: Boost sg msgpack

2015/5/29 Copyright OGIS-RI 2015 4

MessagePackとは

Page 5: Boost sg msgpack

2015/5/29 Copyright OGIS-RI 2015 5

MessagePackとは

Page 6: Boost sg msgpack

2015/5/29 Copyright OGIS-RI 2015 6

MessagePackとは

Page 7: Boost sg msgpack

2015/5/29 Copyright OGIS-RI 2015 7

MessagePackとは

Page 8: Boost sg msgpack

2015/5/29 Copyright OGIS-RI 2015 8

MessagePackとは

Page 9: Boost sg msgpack

2015/5/29 Copyright OGIS-RI 2015 9

MessagePackとは

https://github.com/msgpack/msgpack/blob/master/spec.md

Page 10: Boost sg msgpack

• Website http://msgpack.org/

• JSONと同様

– ポータブル

– 基本的な型情報を内包

– コンポジットなデータ構造(詳細は次ページで)

• JSONとの違い

– サイズが小さい

– バイナリフォーマット

– バイナリデータもそのまま扱える

– 整数と浮動小数点数を区別

– Parseが容易で計算負荷が小さい

2015/5/29 Copyright OGIS-RI 2015 10

MessagePackとは?

Page 11: Boost sg msgpack

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 の名称で記載

Page 12: Boost sg msgpack

MessagePackをサポートする言語

2015/5/29 Copyright OGIS-RI 2015 12

Page 13: Boost sg msgpack

MessagePackをサポートする言語

2015/5/29 Copyright OGIS-RI 2015 13

このリストは github から自動収集して生成されているため どのレベルで実装されているかは要確認

Page 14: Boost sg msgpack

2015/5/29 Copyright OGIS-RI 2015 14

msgpack-c 中でも、C++にフォーカスをあてた内容で、

Cには当てはまらない内容もあるので注意

Page 15: Boost sg msgpack

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

Page 16: Boost sg msgpack

C++/object/byte stream

2015/5/29 16 Copyright OGIS-RI 2015

C++ Types

msgpack::object

Byte stream

pack object

pack unpack

convert

adaptor

Page 17: Boost sg msgpack

Adaptor

2015/5/29 17 Copyright 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

Page 18: Boost sg msgpack

自前のクラスのmsgpack対応

2015/5/29 18 Copyright 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

Page 19: Boost sg msgpack

自前のクラスのmsgpack対応

2015/5/29 19 Copyright 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

Page 20: Boost sg msgpack

ADLを用いたアダプタの実現

2015/5/29 20 Copyright 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 まで

Page 21: Boost sg msgpack

ADLを用いたアダプタの実現

2015/5/29 21 Copyright 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<<オーバーロード

Page 22: Boost sg msgpack

APIのバージョニング

2015/5/29 22 Copyright OGIS-RI 2015

• msgpack-c の C++版はヘッダオンリーライブラリとなった

• ヘッダオンリーライブラリであっても、古いABIのmsgpack-c をincludeして作ったライブラリ(.a , .LIBなど)を、 新しいABIのmsgpack-cと合わせて使うと問題が生じる – std::listのサイズをO(1)で求めることになった際にも 同様の問題が発生した

• これを防ぐために、ABIに変更が生じた場合、 古いバージョンはそのまま使い続けられるようにしたい

• ABIに関わらないバグフィックスなどは 古いバージョンにも反映していきたい

msgpack.hpp

userlib.cpp libuser.a

msgpack.hpp

client.cpp include

生成 include

link

Page 23: Boost sg msgpack

APIのバージョニング

2015/5/29 23 Copyright 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

Page 24: Boost sg msgpack

APIのバージョニング

2015/5/29 24 Copyright 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()

Page 25: Boost sg msgpack

APIのバージョニング

2015/5/29 25 Copyright 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を上手く使い分けることでバージョニングを実現

Page 26: Boost sg msgpack

APIのバージョニング

2015/5/29 26 Copyright OGIS-RI 2015

A

B

C

msgpack::B()

msgpack::C()

V1

D

msgpack::D()

inline namespace

Page 27: Boost sg msgpack

APIのバージョニング

2015/5/29 27 Copyright 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

Page 28: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 28 Copyright 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

Page 29: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 29 Copyright 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

Page 30: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 30 Copyright 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変更対応

Page 31: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 31 Copyright 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

Page 32: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 32 Copyright 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変更対応

Page 33: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 33 Copyright 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変更対応 をここでは行わない

Page 34: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 34 Copyright OGIS-RI 2015

A

B''

C

A

C

ABI変更

msgpack::B()

V1 V2 V3

D

msgpack::D()

B msgpack::B()

inline namespace

msgpack::v1::C()

msgpack::D()

msgpack::C()

using v2::D

CのABI変更対応

ABI変更

Page 35: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 35 Copyright 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変更

Page 36: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 36 Copyright 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変更対応

Page 37: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 37 Copyright 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

Page 38: Boost sg msgpack

using v1::B using V1::C using v1::D

APIのバージョニング

2015/5/29 38 Copyright 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に変更がある場合

Page 39: Boost sg msgpack

ADL問題

2015/5/29 39 Copyright 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は 参照されない

Page 40: Boost sg msgpack

ADL問題

2015/5/29 40 Copyright 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'

Page 41: Boost sg msgpack

ADL問題

2015/5/29 41 Copyright 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

先行宣言を入れてみると。。。

Page 42: Boost sg msgpack

ADL問題

2015/5/29 42 Copyright 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); }

ユーザ定義のオーバーロードが呼ばれない

Page 43: Boost sg msgpack

ADL問題

2015/5/29 43 Copyright 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); }

ユーザ定義のオーバーロードの先行宣言を入れると 期待通りディスパッチされる

Page 44: Boost sg msgpack

ADL問題

2015/5/29 44 Copyright OGIS-RI 2015

msgpackの先行宣言群

ユーザの先行宣言群

msgpackのライブラリ本体

ユーザのコード本体

#include <msgpack_fwd.hpp>

#include <msgpack.hpp>

user.cpp

このアプローチの行き着く先にあるもの

ユーザコードの書き方に大きな制約を課してしまう

Page 45: Boost sg msgpack

なにが問題なのか?

2015/5/29 45 Copyright OGIS-RI 2015

• オーバーロードによるアダプタの実現

– オーバーロードは、呼び出しコードがinstantiateした際、 そこから見えている関数が候補となる

– ADLによって、呼び出しコードのinstantiate を遅延

– ADLを使うためには、名前空間修飾してはならない

• APIバージョニングの実現

– 名前空間修飾しないとAPIバージョニングできない

• この2つの相性が悪い

Page 46: Boost sg msgpack

オーバーロードに変わる手段

2015/5/29 46 Copyright 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& にしなければならない

Page 47: Boost sg msgpack

オーバーロードに変わる手段

2015/5/29 47 Copyright 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

Page 48: Boost sg msgpack

オーバーロードに変わる手段

2015/5/29 48 Copyright 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); }

Page 49: Boost sg msgpack

まとめ

2015/5/29 49 Copyright OGIS-RI 2015

• ユーザ定義の型などに対して 拡張性を持たせたい場合 関数のオーバーロードよりも クラステンプレートの特殊化の方が柔軟に対応できる – 例:名前空間によるAPIバージョニングとの共存

• 将来が予測できない場合、最も柔軟なメカニズムで 実装しておくと後々苦労しない – msgpack-cは、ライブラリが提供している、 既存の全てのオーバーロードベースのアダプタを クラステンプレートに書き換えた

– 書き換えの際、当然ながら既存のユーザコードへの影響が出た

– msgpack-cではこれを メジャーバージョンアップに伴う破壊的変更とし、 マイグレーションガイドなどのドキュメントも用意した