Home > Mobile >  How to convert a struct that includes an enum member to JSON with Boost Describe?
How to convert a struct that includes an enum member to JSON with Boost Describe?

Time:12-12

In the documentation of Boost Describe, there is an example for automatic conversion of a struct to JSON. However, when adding an enum (not a nested one) as a member of the struct, the code example does not work. I guess that the tag_invoke function needs to be modified in order to handle the enum.

I tried the following, based on the example:

#include <boost/describe.hpp>
#include <boost/json.hpp>
#include <boost/mp11.hpp>
#include <iostream>
#include <map>
#include <type_traits>
#include <vector>

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 &&
                                      !std::is_union<T>::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); }); 
}

enum E { e1, e2 };

BOOST_DESCRIBE_ENUM(E, e1, e2);

struct A { 
  int x;
  int y;
  E e;
};

BOOST_DESCRIBE_STRUCT(A, (), (x, y, e)) 

} // namespace app

int main() {
  app::A a{1, 2, app::e1};

  std::cout << boost::json::value_from(a) << std::endl;
}

CodePudding user response:

You only have the tag_invoke defined for class types.

You need to add one for enumeration types:

template <
    class E,
    class = std::enable_if_t<boost::describe::has_describe_enumerators<E>::value>>
void tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, E const& e) {
    auto s = boost::describe::enum_to_string(e, 0);
    v      = s ? s : std::to_string(static_cast<std::underlying_type_t<E>>(e));
}

Now your sample works:

Live On Coliru

#include <boost/core/demangle.hpp>
#include <boost/describe.hpp>
#include <boost/json/src.hpp>
#include <boost/mp11.hpp>
#include <iostream>

namespace app {

    template <
        class E,
        class = std::enable_if_t<boost::describe::has_describe_enumerators<E>::value>>
    void tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, E const& e) {
        auto s = boost::describe::enum_to_string(e, 0);
        v      = s ? s : std::to_string(static_cast<std::underlying_type_t<E>>(e));
    }

    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 &&
                                    !std::is_union<T>::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); });
    }

    enum E { e1, e2 };

    struct A {
        int x;
        int y;
        E   e;
    };

    BOOST_DESCRIBE_ENUM(E, e1, e2)
    BOOST_DESCRIBE_STRUCT(A, (), (x, y, e))

} // namespace app

int main() {
    app::A a{1, 2, app::e1};

    std::cout << boost::json::value_from(a) << std::endl;
}

Prints

{"x":1,"y":2,"e":"e1"}
  • Related