BOOST_DEFINE_ENUM_CLASS 和 json

BOOST_DEFINE_ENUM_CLASS and json

the documentation for boost describe 的标题“自动转换为 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;
}

我需要做什么才能为 c 启用 boost::json::value_from()?

编辑:好的,下面的代码可以解决问题,但我必须让它更模板化:

void tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, E1 const& t) {
    v = boost::describe::enum_to_string(t, "x");
}

文档给出了这个例子(稍作修改以适应我们 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 适用性限制在您自己的枚举中以避免干扰其他库。

现场演示:

Live On Compiler Explorer

#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"}