为第三方类型描述
Describe for third party's type
我使用 boost::json 库生成 json 字符串,但是对于第 3 方类型,BOOST_DESCRIBE_CLASS 无法理解。我可以为使用第 3 方样式的 类 使用友元函数,但它太多了。任何想法只处理 1 次并在任何地方使用?
示例:
class CTest
{
public:
Imath::V3d v;
BOOST_DESCRIBE_CLASS(CTest, (), (v), (), ())
};
我不得不猜测,所以我最初着手研究通用转换函数。
你确实会使用 tag_invoke
:
namespace bd = boost::describe;
template <class T, //
class Bd = bd::describe_bases<T, bd::mod_any_access>,
class Md = bd::describe_members<T, bd::mod_any_access>,
class En = std::enable_if_t<!std::is_union<T>::value>>
void tag_invoke(json::value_from_tag, json::value& v, T const& obj) {
json::object bases, members;
boost::mp11::mp_for_each<Bd>([&, first = true](auto const& D) mutable {
using B = typename std::decay_t<decltype(D)>::type;
bases.emplace(boost::core::demangle(typeid(B).name()),
json::value_from(static_cast<B const&>(obj)));
});
boost::mp11::mp_for_each<Md>([&](auto D) {
members.emplace(D.name, json::value_from(obj.*D.pointer));
});
json::object result;
if (bases.size())
result.emplace("bases", std::move(bases));
if (members.size())
result.emplace("members", std::move(members));
v = std::move(result);
}
这里是演示:
class CTest
{
public:
int v = 42;
BOOST_DESCRIBE_CLASS(CTest, (), (v), (), ())
};
struct X { int m1 = 1; };
struct Y { int m2 = 2; };
BOOST_DESCRIBE_STRUCT(X, (), (m1))
BOOST_DESCRIBE_STRUCT(Y, (), (m2))
class Z : public X , public Y {
int m1 = 3, m2 = 4;
BOOST_DESCRIBE_CLASS(Z, (X, Y), (), (), (m1, m2))
};
int main()
{
std::cout << json::value_from(CTest{}) << "\n";
std::cout << json::value_from(Z{}) << "\n";
}
版画
{"members":{"v":42}}
{"bases":{"X":{"members":{"m1":1}},"Y":{"members":{"m2":2}}},"members":{"m1":3,"m2":4}}
第三方类型
Third-party 类型可以用同样的方式转换。假设 third-party header 包含如下内容:
namespace Imath {
struct V3d {
std::tuple<float, float, float> coords;
std::string meta;
};
};
您无法编辑 header。但是你可以在包含它之后将它添加到你自己的文件中:
namespace boost::json {
void tag_invoke(json::value_from_tag, json::value& v,
Imath::V3d const& v3d) {
v = json::object{
{"x", std::get<0>(v3d.coords)},
{"y", std::get<1>(v3d.coords)},
{"z", std::get<2>(v3d.coords)},
{"meta", v3d.meta},
};
}
} // namespace json
For ADL it's also fine to put the tag_invoke
overload into the Imath
namespace (or rather, the namespace declaring V3d
), but you might prefer the boost::json
namespace as it specifically expects overloads of this customization point.
现在输出变为:
class CTest
{
public:
Imath::V3d v{{11, 22, 33}, "This is very nifty"};
BOOST_DESCRIBE_CLASS(CTest, (), (v), (), ())
};
struct X { int m1 = 1; };
struct Y { int m2 = 2; };
BOOST_DESCRIBE_STRUCT(X, (), (m1))
BOOST_DESCRIBE_STRUCT(Y, (), (m2))
class Z : public X , public Y {
int m1 = 3, m2 = 4;
BOOST_DESCRIBE_CLASS(Z, (X, Y), (), (), (m1, m2))
};
int main()
{
std::cout << json::value_from(CTest{}) << "\n";
std::cout << json::value_from(Z{}) << "\n";
}
正在打印:
{"members":{"v":{"x":1.1E1,"y":2.2E1,"z":3.3E1,"meta":"This is very nifty"}}}
{"bases":{"X":{"members":{"m1":1}},"Y":{"members":{"m2":2}}},"members":{"m1":3,"m2":4}}
我使用 boost::json 库生成 json 字符串,但是对于第 3 方类型,BOOST_DESCRIBE_CLASS 无法理解。我可以为使用第 3 方样式的 类 使用友元函数,但它太多了。任何想法只处理 1 次并在任何地方使用? 示例:
class CTest
{
public:
Imath::V3d v;
BOOST_DESCRIBE_CLASS(CTest, (), (v), (), ())
};
我不得不猜测,所以我最初着手研究通用转换函数。
你确实会使用 tag_invoke
:
namespace bd = boost::describe;
template <class T, //
class Bd = bd::describe_bases<T, bd::mod_any_access>,
class Md = bd::describe_members<T, bd::mod_any_access>,
class En = std::enable_if_t<!std::is_union<T>::value>>
void tag_invoke(json::value_from_tag, json::value& v, T const& obj) {
json::object bases, members;
boost::mp11::mp_for_each<Bd>([&, first = true](auto const& D) mutable {
using B = typename std::decay_t<decltype(D)>::type;
bases.emplace(boost::core::demangle(typeid(B).name()),
json::value_from(static_cast<B const&>(obj)));
});
boost::mp11::mp_for_each<Md>([&](auto D) {
members.emplace(D.name, json::value_from(obj.*D.pointer));
});
json::object result;
if (bases.size())
result.emplace("bases", std::move(bases));
if (members.size())
result.emplace("members", std::move(members));
v = std::move(result);
}
这里是演示:
class CTest
{
public:
int v = 42;
BOOST_DESCRIBE_CLASS(CTest, (), (v), (), ())
};
struct X { int m1 = 1; };
struct Y { int m2 = 2; };
BOOST_DESCRIBE_STRUCT(X, (), (m1))
BOOST_DESCRIBE_STRUCT(Y, (), (m2))
class Z : public X , public Y {
int m1 = 3, m2 = 4;
BOOST_DESCRIBE_CLASS(Z, (X, Y), (), (), (m1, m2))
};
int main()
{
std::cout << json::value_from(CTest{}) << "\n";
std::cout << json::value_from(Z{}) << "\n";
}
版画
{"members":{"v":42}}
{"bases":{"X":{"members":{"m1":1}},"Y":{"members":{"m2":2}}},"members":{"m1":3,"m2":4}}
第三方类型
Third-party 类型可以用同样的方式转换。假设 third-party header 包含如下内容:
namespace Imath {
struct V3d {
std::tuple<float, float, float> coords;
std::string meta;
};
};
您无法编辑 header。但是你可以在包含它之后将它添加到你自己的文件中:
namespace boost::json {
void tag_invoke(json::value_from_tag, json::value& v,
Imath::V3d const& v3d) {
v = json::object{
{"x", std::get<0>(v3d.coords)},
{"y", std::get<1>(v3d.coords)},
{"z", std::get<2>(v3d.coords)},
{"meta", v3d.meta},
};
}
} // namespace json
For ADL it's also fine to put the
tag_invoke
overload into theImath
namespace (or rather, the namespace declaringV3d
), but you might prefer theboost::json
namespace as it specifically expects overloads of this customization point.
现在输出变为:
class CTest
{
public:
Imath::V3d v{{11, 22, 33}, "This is very nifty"};
BOOST_DESCRIBE_CLASS(CTest, (), (v), (), ())
};
struct X { int m1 = 1; };
struct Y { int m2 = 2; };
BOOST_DESCRIBE_STRUCT(X, (), (m1))
BOOST_DESCRIBE_STRUCT(Y, (), (m2))
class Z : public X , public Y {
int m1 = 3, m2 = 4;
BOOST_DESCRIBE_CLASS(Z, (X, Y), (), (), (m1, m2))
};
int main()
{
std::cout << json::value_from(CTest{}) << "\n";
std::cout << json::value_from(Z{}) << "\n";
}
正在打印:
{"members":{"v":{"x":1.1E1,"y":2.2E1,"z":3.3E1,"meta":"This is very nifty"}}}
{"bases":{"X":{"members":{"m1":1}},"Y":{"members":{"m2":2}}},"members":{"m1":3,"m2":4}}