adaptors
当您想从各种类型的对象打包到 msgpack::object 时,您需要一个适配器。从各种类型的对象转换为 msgpack::object ,反之亦然,它也需要一个适配器。
见转换。
predefined adaptors(预定义适配器)
msgpack-c 为 C++ 基本类型和标准库提供预定义的适配器。
C++ type | msgpack::object type | note |
---|---|---|
bool | bool | |
char* | str | |
std::deque | array | |
char | positive/negative integer | |
signed ints *1 | positive/negative integer | |
unsigned ints *2 | positive integer | |
float | float32 | since 2.1.0 |
double | float64 | |
T[] | array | since 2.0.0 |
char[] | str | since 2.0.0 |
unsigned char[] | bin | since 2.0.0 |
std::list | array | |
std::map | array | |
std::pair | array | |
std::set | array | |
std::string | str | |
std::wstring | array of positive integer | |
std::vector | array | |
std::vector |
bin | |
std::vector |
bin | since 1.2.0 |
msgpack type | msgpack::object type | note |
---|---|---|
nil_t | nil | |
tuple | array | |
array_ref *3 | array | since 1.2.0 |
raw_ref *3 | bin | |
v4raw_ref *3 | str | since 1.2.0 |
ext | ext | |
ext_ref *3 | ext |
C++11 type | msgpack::object type | note |
---|---|---|
std::array | array | |
std::array |
bin | |
std::array |
bin | since 1.2.0 |
std::forward_list | array | |
std::tuple | array | |
std::unordered_map | array | |
std::unordered_set | array | |
std::unique_ptr | depends on template type | since 1.2.0 |
std::shared_ptr | depends on template type | since 1.2.0 |
boost type | msgpack::object type | note |
---|---|---|
string_ref | str | since 1.2.0 |
string_view | str | since 2.1.0 |
optional | depends on optional type | since 1.2.0 |
fusion sequence | array | since 1.2.0 |
variant | all types | since 1.2.0 |
当您将适配器用于 boost 容器时,您需要定义 MSGPACK_USE_BOOST。
*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
*3 xxx_ref types enforce packing types without copies.
msgpack::object 类型定义如下: https://github.com/msgpack/msgpack/blob/master/spec.md
这些适配器在以下目录中定义:https://github.com/msgpack/msgpack-c/tree/master/include/msgpack/adaptor
defining custom adaptors(定义自定义适配器)
classes
intrusive approach
当你想让你的类适应 msgpack 时,使用 MSGPACK_DEFINE 宏。
macro | msgpack::object type | note |
---|---|---|
MSGPACK_DEFINE | array or map *3 | |
MSGPACK_DEFINE_ARRAY | array | since 1.2.0 |
MSGPACK_DEFINE_MAP | map | since 1.2.0 |
*3 定义 MSGPACK_USE_DEFINE_MAP 时,MSGPACK_DEFINE 适配数组,否则适配映射。
#include <msgpack.hpp>
struct your_class {
int a;
std::string b;
MSGPACK_DEFINE(a, b);
};
// ...
假设a == 42, b == “ABC”,your_class的对象序列化如下:
// array
[42,"ABC"]
// map (MSGPACK_USE_DEFINE_MAP is defined)
{"a":42,"b":"ABC"}
我使用 JSON 来帮助理解。但实际情况是 msgpack 格式。
宏 MSGPACK_DEFINE 提供打包、转换为带区域的 msgpack 对象以及从 msgpack::object 功能转换为 your_class 的功能。 your_class 被打包/转换为 msgpack 数组。
https://github.com/msgpack/msgpack-c/blob/cpp_master/example/cpp03/class_intrusive.cpp
当你想用它的基类调整你的类时,使用 MSGPACK_BASE 宏。
#include <msgpack.hpp>
struct base1 {
int a;
MSGPACK_DEFINE(a);
};
struct base2 {
int a;
MSGPACK_DEFINE(a);
};
struct your_class : base1, base2 {
int a;
std::string b;
// You can choose any order. It is represented to the msgpack array elements order.
MSGPACK_DEFINE(b, MSGPACK_BASE(base2), a, MSGPACK_BASE(base1));
};
// ...
macro | msgpack::object | type |
---|---|---|
MSGPACK_BASE | array or map *4 | |
MSGPACK_BASE_ARRAY | array | |
MSGPACK_BASE_MAP | map |
*4 定义 MSGPACK_USE_DEFINE_MAP 时,MSGPACK_BASE 适配数组,否则适配映射。
您必须将 MSGPACK_BASE 与 MSGPACK_DEFINE一起使用、MSGPACK_BASE_ARRAY 与 MSGPACK_DEFINE_ARRAY一起使用 或 MSGPACK_BASE_MAP 与 MSGPACK_DEFINE_MAP 一起使用。当您使用 MSGPACK_BASE_MAP 时,键是基类名称的 STR。
假设base1::a == 1, base2::a == 2, your_class::a == 42, and your_class::b == “ABC”,your_class的对象序列化如下:
// array
["ABC",[2],42,[1]]
// map (MSGPACK_USE_DEFINE_MAP is defined)
{"b":"ABC","base2":{"a":2},"a":42,"base1":{"a":1}}
我使用 JSON 来帮助理解。但实际情况是 msgpack 格式。
当您使用基于数组的适配器时(比如:MSGPACK_DEFINE_ARRAY),成员变量和基类的顺序很重要。 msgpack::object 数组的索引对应于成员变量的顺序。当您使用基于映射的适配器时(MSGPACK_DEFINE_MAP),成员变量和基类的名称很重要。 msgpack::object 映射的键对应于成员变量名称和基类名称。
since 2.1.0
您还可以使用 MSGPACK_NVP 自定义映射键的名称。例如,值 b 的键是#b。您可以使用任何字符串,即使它不能用作 C++ 变量名。它有助于跨编程语言数据传输。
#include <msgpack.hpp>
struct your_class {
int a;
std::string b;
MSGPACK_DEFINE_MAP(a, MSGPACK_NVP("#b", b));
};
{"a":42,"#b":"ABC"}
non-intrusive approach(非侵入式方法)
当你不想修改你的类时,你可以使用非侵入式的方法。 msgpack-c 提供以下四个仿函数类模板。这些模板位于命名空间 msgpack::adaptor 中。
convert
从 msgpack::object 转换为 T。
template <typename T>
struct convert {
msgpack::object const& operator()(msgpack::object const&, T&) const;
};
pack
从 T 打包到 msgpack::packer。
template <typename T>
struct pack {
template <typename Stream>
msgpack::packer<Stream>& operator()(msgpack::packer<Stream>&, T const&) const;
};
object
将 T 设置为 msgpack::object。
template <typename T>
struct object {
void operator()(msgpack::object&, T const&) const;
};
object_with_zone
将 T 设置为 msgpack::object::with_zone。
template <typename T>
struct object_with_zone {
void operator()(msgpack::object::with_zone&, T const&) const;
};
为了为您的自定义类型添加打包/转换支持,您需要特例化模板。
以下是专门针对“your_type”的模板。
template <>
struct convert<your_type> {
msgpack::object const& operator()(msgpack::object const&, your_type&) const {
// your implementation
}
};
template <>
struct pack<your_type> {
template <typename Stream>
msgpack::packer<Stream>& operator()(msgpack::packer<Stream>&, your_type const&) const {
// your implementation
}
};
template <>
struct object<your_type> {
void operator()(msgpack::object&, your_type const&) const {
// your implementation
}
};
template <>
struct object_with_zone<your_type> {
void operator()(msgpack::object::with_zone&, your_type const&) const {
// your implementation
}
};
您不需要专门化所有模板。当您只想支持包时,您可以只专门化包模板。
Example:
#include <msgpack.hpp>
class my_class {
public:
my_class() {} // When you want to convert from msgpack::object to my_class
// using msgpack::object::as fucntion template,
// my_class should be default constructible.
my_class(std::string const& name, int age):name_(name), age_(age) {}
// my_class should provide getters for the data members you want to pack.
std::string const& get_name() const { return name_; }
int get_age() const { return age_; }
private:
std::string name_;
int age_;
};
namespace msgpack {
MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
namespace adaptor {
// Place class template specialization here
template<>
struct convert<my_class> {
msgpack::object const& operator()(msgpack::object const& o, my_class& v) const {
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
if (o.via.array.size != 2) throw msgpack::type_error();
v = my_class(
o.via.array.ptr[0].as<std::string>(),
o.via.array.ptr[1].as<int>());
return o;
}
};
template<>
struct pack<my_class> {
template <typename Stream>
packer<Stream>& operator()(msgpack::packer<Stream>& o, my_class const& v) const {
// packing member variables as an array.
o.pack_array(2);
o.pack(v.get_name());
o.pack(v.get_age());
return o;
}
};
template <>
struct object_with_zone<my_class> {
void operator()(msgpack::object::with_zone& o, my_class const& v) const {
o.type = type::ARRAY;
o.via.array.size = 2;
o.via.array.ptr = static_cast<msgpack::object*>(
o.zone.allocate_align(sizeof(msgpack::object) * o.via.array.size));
o.via.array.ptr[0] = msgpack::object(v.get_name(), o.zone);
o.via.array.ptr[1] = msgpack::object(v.get_age(), o.zone);
}
};
} // namespace adaptor
} // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
} // namespace msgpack
https://github.com/msgpack/msgpack-c/blob/master/example/cpp03/class_non_intrusive.cpp
当你在convert类的特例化模板中使用msgpack::object::as 成员函数模板时,会创建临时对象。
template<>
struct convert<my_class> {
msgpack::object const& operator()(msgpack::object const& o, my_class& v) const {
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
if (o.via.array.size != 2) throw msgpack::type_error();
v = my_class(
o.via.array.ptr[0].as<std::string>(), // 临时对象在这里创建
o.via.array.ptr[1].as<int>());
return o;
}
};
如果可以在转换目标类时获得成员变量的引用,就可以去掉临时对象的创建。例如,如果 my_class 的成员变量是公共的,您可以对它们应用 operator>>,如下所示:
class my_class {
public:
/* ... */
std::string name_;
int age_;
};
template<>
struct convert<my_class> {
msgpack::object const& operator()(msgpack::object const& o, my_class& v) const {
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
if (o.via.array.size != 2) throw msgpack::type_error();
o.via.array.ptr[0] >> v.name_; // no temporary object creation
o.via.array.ptr[1] >> v.age_;
return o;
}
};
accessing private members(访问私有成员)
由于非侵入性,非侵入式方法无法访问目标类的私有成员。为了从适配器类模板特化中访问私有成员,您需要在目标类中定义友元类模板声明(侵入式),如下所示:
class my_class {
// ...
template <typename T, typename Enabler>
friend struct msgpack::MSGPACK_DEFAULT_API_NS::adaptor::pack;
template <typename T, typename Enabler>
friend struct msgpack::MSGPACK_DEFAULT_API_NS::adaptor::convert;
template <typename T, typename Enabler>
friend struct msgpack::MSGPACK_DEFAULT_API_NS::adaptor::object;
template <typename T, typename Enabler>
friend struct msgpack::MSGPACK_DEFAULT_API_NS::adaptor::object_with_zone;
template <typename T, typename Enabler>
friend struct msgpack::MSGPACK_DEFAULT_API_NS::adaptor::as;
// ...
};
4.0.3版本之前,需要为对象适配器声明如下:
template <typename T, typename Enabler, typename Enabler2>
friend struct msgpack::MSGPACK_DEFAULT_API_NS::adaptor::object;
您只需要声明您定义的类模板特化。
non default constructible class support (C++11 only, since 1.2.0)(非默认可构造类支持)
您可能希望从 msgpack::object 转换为没有默认构造函数的类。为此,您可以使用“as”类模板特化。
这是一个非默认的可构造类:
struct no_def_con {
no_def_con() = delete;
no_def_con(int i):i(i) {}
int i;
MSGPACK_DEFINE(i);
};
Define 'as' class template specialization as follows:
namespace msgpack {
MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
namespace adaptor {
template <>
struct as<no_def_con> {
no_def_con operator()(msgpack::object const& o) const {
if (o.type != msgpack::type::ARRAY) throw msgpack::type_error();
if (o.via.array.size != 1) throw msgpack::type_error();
return no_def_con(o.via.array.ptr[0].as<int>());
}
};
} // adaptor
} // MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS)
} // msgpack
然后,您可以转换为您的类形式 msgpack::object 如下:
msgpack::object o = /*...*/
no_def_con ndc = o.as<no_def_con>();
注意:MSVC2015 不完全支持 C++11 功能。由于缺少表达式 SFINAE,MSVC2015 不支持此功能。请参阅 https://github.com/msgpack/msgpack-c/issues/343#issuecomment-131654386。
enums
当你想使 enum 或 enum 类适应 msgpack 时,使用 MSGPACK_ADD_ENUM 宏。
#include <msgpack.hpp>
enum your_enum {
elem1,
elem2
};
MSGPACK_ADD_ENUM(your_enum);
enum class your_enum_class {
elem1,
elem2
};
MSGPACK_ADD_ENUM(your_enum_class);
// ...
您需要在全局命名空间中使用 MSGPACK_ADD_ENUM。
宏 MSGPACK_DEFINE 提供打包、转换为带或不带区域的 msgpack 对象以及从 msgpack::object 功能转换为 your_enum 的功能。 your_enum 被打包/转换为 msgpack 正/负整数。
See an example.
Q.E.D.