В документация для boost описывает под заголовком «Автоматическое преобразование в JSON» показано, как реализовать «универсальную перегрузку tag_invoke, которая автоматически преобразует аннотированную структуру в значение Boost.JSON». Пример поддерживает BOOST_DESCRIBE_STRUCT, как реализовать нечто подобное для BOOST_DEFINE_ENUM_CLASS?
Вот моя наивная попытка адаптировать пример для поддержки BOOST_DEFINE_ENUM_CLASS наряду с BOOST_DESCRIBE_STRUCT:
#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <boost/json.hpp>
#include <type_traits>
#include <vector>
#include <map>
namespace app {
template<class T,
class D1 = boost::describe::describe_members<T, boost::describe::mod_public | boost::describe::mod_protected>,
class D2 = boost::describe::describe_members<T, boost::describe::mod_private>,
class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value> >
void tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, T const& t) {
auto& obj = v.emplace_object();
boost::mp11::mp_for_each<D1>([&](auto D) {
obj[D.name] = boost::json::value_from(t.*D.pointer);
});
}
struct A {
int x;
int y;
};
BOOST_DESCRIBE_STRUCT(A, (), (x, y))
struct B {
std::vector<A> v;
std::map<std::string, A> m;
};
BOOST_DESCRIBE_STRUCT(B, (), (v, m))
BOOST_DEFINE_ENUM_CLASS(E1, v1, v2, v3)
struct C {
int x;
E1 e1;
};
BOOST_DESCRIBE_STRUCT(C, (), (x, e1))
} // namespace app
#include <iostream>
void main() {
app::A a{ 1, 2 };
std::cout << boost::json::value_from(a) << std::endl;
app::B b{ { { 1, 2 }, { 3, 4 } }, { { "k1", { 5, 6 } }, { "k2", { 7, 8 } } } };
std::cout << boost::json::value_from(b) << std::endl;
app::C c{ 1, app::E1::v1 };
//std::cout << boost::json::value_from(c) << std::endl;
}
Что мне нужно сделать, чтобы включить boost::json::value_from() для c?
Редактировать: Окей, код ниже делает свое дело, но я должен сделать его более шаблонным:
void tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, E1 const& t) {
v = boost::describe::enum_to_string(t, "x");
}
🤔 А знаете ли вы, что...
Язык C++ обеспечивает совместимость с языком C, что позволяет использовать код на обоих языках в одном проекте.
В документации приведен этот пример (слегка измененный в соответствии с нашими потребностями JSON позже):
template <class E> char const* enum_to_string(E e) {
char const* r = nullptr;
boost::mp11::mp_for_each<boost::describe::describe_enumerators<E>>(
[&](auto D) {
if (e == D.value)
r = D.name;
});
return r;
}
Вы можете использовать это для реализации точки настройки value_from
:
static inline void tag_invoke(json::value_from_tag, json::value& jv, E1 e) {
auto name = enum_to_string(e);
jv = name
? name
: std::to_string(static_cast<std::underlying_type_t<E1>>(e));
}
При наличии подходящего SFINAE вы можете создать шаблон для любого типа перечисления, если вы помните об ограничении применимости ADL к вашим собственным перечислениям, чтобы не мешать другим библиотекам.
Живая демонстрация:
#include <boost/describe.hpp>
#include <boost/json.hpp>
#include <boost/json/src.hpp> // for online demo
#include <boost/mp11.hpp>
#include <map>
#include <type_traits>
#include <vector>
namespace app {
namespace json = boost::json;
template<class T,
class D1 = boost::describe::describe_members<T, boost::describe::mod_public | boost::describe::mod_protected>,
class D2 = boost::describe::describe_members<T, boost::describe::mod_private>,
class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value> >
void tag_invoke(json::value_from_tag const&, json::value& v, T const& t) {
auto& obj = v.emplace_object();
boost::mp11::mp_for_each<D1>([&](auto D) {
obj[D.name] = json::value_from(t.*D.pointer);
});
}
struct A {
int x;
int y;
};
BOOST_DESCRIBE_STRUCT(A, (), (x, y))
struct B {
std::vector<A> v;
std::map<std::string, A> m;
};
BOOST_DESCRIBE_STRUCT(B, (), (v, m))
BOOST_DEFINE_ENUM_CLASS(E1, v1, v2, v3)
template <class E> char const* enum_to_string(E e) {
char const* r = nullptr;
boost::mp11::mp_for_each<boost::describe::describe_enumerators<E>>(
[&](auto D) {
if (e == D.value)
r = D.name;
});
return r;
}
static inline void tag_invoke(json::value_from_tag, json::value& jv, E1 e) {
auto name = enum_to_string(e);
jv = name
? name
: std::to_string(static_cast<std::underlying_type_t<E1>>(e));
}
struct C {
int x;
E1 e1;
};
BOOST_DESCRIBE_STRUCT(C, (), (x, e1))
} // namespace app
#include <iostream>
int main() {
app::A a{ 1, 2 };
std::cout << boost::json::value_from(a) << std::endl;
app::B b{ { { 1, 2 }, { 3, 4 } }, { { "k1", { 5, 6 } }, { "k2", { 7, 8 } } } };
std::cout << boost::json::value_from(b) << std::endl;
app::C c{ 1, app::E1::v1 };
std::cout << boost::json::value_from(c) << std::endl;
}
Отпечатки
{"x":1,"y":2}
{"v":[{"x":1,"y":2},{"x":3,"y":4}],"m":{"k1":{"x":5,"y":6},"k2":{"x":7,"y":8}}}
{"x":1,"e1":"v1"}