diff --git a/doc/describe/examples.adoc b/doc/describe/examples.adoc index 379475b..b24aa1d 100644 --- a/doc/describe/examples.adoc +++ b/doc/describe/examples.adoc @@ -16,7 +16,7 @@ descriptors using `mp11::mp_for_each` and prints them. [source] ---- -include::../../example/printing_enums_ct.cpp[] +include::../../example/printing_enums_ct.cpp[lines=5..-1] ---- ## Printing Enumerators with a Run Time Loop @@ -27,7 +27,7 @@ an ordinary `for` loop, instead of `mp_for_each`. [source] ---- -include::../../example/printing_enums_rt.cpp[] +include::../../example/printing_enums_rt.cpp[lines=5..-1] ---- ## enum_to_string @@ -47,7 +47,7 @@ the enum value doesn't have a name. [source] ---- -include::../../example/enum_to_string.cpp[] +include::../../example/enum_to_string.cpp[lines=5..-1] ---- ## string_to_enum @@ -60,6 +60,53 @@ throws an exception. [source] ---- -include::../../example/string_to_enum.cpp[] +include::../../example/string_to_enum.cpp[lines=5..-1] +---- + +## Defining a Universal Print Function + +This example defines a universal `operator<<` that works on +any class or struct type that has been described with +`BOOST_DESCRIBE_STRUCT` or `BOOST_DESCRIBE_CLASS`. + +It first prints the base classes, recursively, then prints +all the members. + +(A C cast is used to access private base classes. This is +not as bad as it first appears, because we're only inspecting +the base class by printing its members, and doing so should not +change its state and hence cannot violate its invariant.) + +[source] +---- +include::../../example/print_function.cpp[lines=5..-1] +---- + +## Defining a Universal Conversion to JSON + +This example defines a universal `tag_invoke` overload that +automatically converts an annotated struct to JSON by iterating +over the described public members and adding them to the return +`json::object`. + +The overload is defined in namespace `app` in order to apply +to all annotated classes also defined in `app`. + +The presence of private members is taken as an indication that +a universal conversion is not suitable, so the overload is +disabled in this case using `std::enable_if_t`. + +[source] +---- +include::../../example/to_json.cpp[lines=5..-1] +---- + +## Defining a Universal Conversion from JSON + +Like the previous example, but in the other direction. + +[source] +---- +include::../../example/from_json.cpp[lines=5..-1] ---- diff --git a/doc/html/describe.html b/doc/html/describe.html index 841bdaa..b95b67f 100644 --- a/doc/html/describe.html +++ b/doc/html/describe.html @@ -461,6 +461,9 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
  • Printing Enumerators with a Run Time Loop
  • enum_to_string
  • string_to_enum
  • +
  • Defining a Universal Print Function
  • +
  • Defining a Universal Conversion to JSON
  • +
  • Defining a Universal Conversion from JSON
  • Reference @@ -932,6 +935,231 @@ int main() +
    +

    Defining a Universal Print Function

    +
    +

    This example defines a universal operator<< that works on +any class or struct type that has been described with +BOOST_DESCRIBE_STRUCT or BOOST_DESCRIBE_CLASS.

    +
    +
    +

    It first prints the base classes, recursively, then prints +all the members.

    +
    +
    +

    (A C cast is used to access private base classes. This is +not as bad as it first appears, because we’re only inspecting +the base class by printing its members, and doing so should not +change its state and hence cannot violate its invariant.)

    +
    +
    +
    +
    #include <boost/describe.hpp>
    +#include <boost/mp11.hpp>
    +#include <ostream>
    +
    +using namespace boost::describe;
    +
    +template<class T,
    +    class Bd = describe_base_classes<T, mod_any_access>,
    +    class Md = describe_data_members<T, mod_any_access>>
    +    std::ostream& operator<<( std::ostream & os, T const & t )
    +{
    +    os << "{";
    +
    +    bool first = true;
    +
    +    boost::mp11::mp_for_each< Bd >([&](auto D){
    +
    +        if( !first ) { os << ", "; } first = false;
    +
    +        using B = typename decltype(D)::type;
    +        os << (B const&)t;
    +
    +    });
    +
    +    boost::mp11::mp_for_each< Md >([&](auto D){
    +
    +        if( !first ) { os << ", "; } first = false;
    +
    +        os << "." << D.name << " = " << t.*D.pointer;
    +
    +    });
    +
    +    os << "}";
    +    return os;
    +}
    +
    +struct X
    +{
    +    int m1 = 1;
    +};
    +
    +BOOST_DESCRIBE_STRUCT(X, (), (m1), ())
    +
    +struct Y
    +{
    +    int m2 = 2;
    +};
    +
    +BOOST_DESCRIBE_STRUCT(Y, (), (m2), ())
    +
    +class Z: public X, private Y
    +{
    +    int m1 = 3;
    +    int m2 = 4;
    +
    +    BOOST_DESCRIBE_CLASS(Z, (X, Y), (), (m1, m2), ())
    +};
    +
    +#include <iostream>
    +
    +int main()
    +{
    +    std::cout << Z() << std::endl;
    +}
    +
    +
    +
    +
    +

    Defining a Universal Conversion to JSON

    +
    +

    This example defines a universal tag_invoke overload that +automatically converts an annotated struct to JSON by iterating +over the described public members and adding them to the return +json::object.

    +
    +
    +

    The overload is defined in namespace app in order to apply +to all annotated classes also defined in app.

    +
    +
    +

    The presence of private members is taken as an indication that +a universal conversion is not suitable, so the overload is +disabled in this case using std::enable_if_t.

    +
    +
    +
    +
    #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_data_members<T, boost::describe::mod_public>,
    +    class D2 = boost::describe::describe_data_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), ())
    +
    +} // namespace app
    +
    +#include <iostream>
    +
    +int main()
    +{
    +    app::B b{ { { 1, 2 }, { 3, 4 } }, { { "k1", { 5, 6 } }, { "k2", { 7, 8 } } } };
    +
    +    std::cout << boost::json::value_from( b ) << std::endl;
    +}
    +
    +
    +
    +
    +

    Defining a Universal Conversion from JSON

    +
    +

    Like the previous example, but in the other direction.

    +
    +
    +
    +
    #include <boost/describe.hpp>
    +#include <boost/mp11.hpp>
    +#include <boost/json.hpp>
    +#include <type_traits>
    +
    +namespace app
    +{
    +
    +template<class T> void extract( boost::json::object const & obj, char const * name, T & value )
    +{
    +    value = boost::json::value_to<T>( obj.at( name ) );
    +}
    +
    +template<class T,
    +    class D1 = boost::describe::describe_data_members<T, boost::describe::mod_public>,
    +    class D2 = boost::describe::describe_data_members<T, boost::describe::mod_private>,
    +    class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value> >
    +
    +    T tag_invoke( boost::json::value_to_tag<T> const&, boost::json::value const& v )
    +{
    +    auto const& obj = v.as_object();
    +
    +    T t = {};
    +
    +    boost::mp11::mp_for_each<D1>([&](auto D){
    +
    +        extract( obj, D.name, t.*D.pointer );
    +
    +    });
    +
    +    return t;
    +}
    +
    +struct A
    +{
    +    int x;
    +    int y;
    +};
    +
    +BOOST_DESCRIBE_STRUCT(A, (), (x, y), ())
    +
    +} // namespace app
    +
    +#include <iostream>
    +
    +int main()
    +{
    +    boost::json::value jv{ { "x", 1 }, { "y", 2 } };
    +
    +    std::cout << "jv: " << jv << std::endl;
    +
    +    auto a = boost::json::value_to<app::A>( jv );
    +
    +    std::cout << "a: { " << a.x << ", " << a.y << " }" << std::endl;
    +}
    +
    +
    +
    diff --git a/example/enum_to_string.cpp b/example/enum_to_string.cpp index 64de74a..7c52911 100644 --- a/example/enum_to_string.cpp +++ b/example/enum_to_string.cpp @@ -1,3 +1,7 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + #include #include diff --git a/example/from_json.cpp b/example/from_json.cpp new file mode 100644 index 0000000..d416079 --- /dev/null +++ b/example/from_json.cpp @@ -0,0 +1,59 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +namespace app +{ + +template void extract( boost::json::object const & obj, char const * name, T & value ) +{ + value = boost::json::value_to( obj.at( name ) ); +} + +template, + class D2 = boost::describe::describe_data_members, + class En = std::enable_if_t::value> > + + T tag_invoke( boost::json::value_to_tag const&, boost::json::value const& v ) +{ + auto const& obj = v.as_object(); + + T t = {}; + + boost::mp11::mp_for_each([&](auto D){ + + extract( obj, D.name, t.*D.pointer ); + + }); + + return t; +} + +struct A +{ + int x; + int y; +}; + +BOOST_DESCRIBE_STRUCT(A, (), (x, y), ()) + +} // namespace app + +#include + +int main() +{ + boost::json::value jv{ { "x", 1 }, { "y", 2 } }; + + std::cout << "jv: " << jv << std::endl; + + auto a = boost::json::value_to( jv ); + + std::cout << "a: { " << a.x << ", " << a.y << " }" << std::endl; +} diff --git a/example/print_function.cpp b/example/print_function.cpp new file mode 100644 index 0000000..8b3c487 --- /dev/null +++ b/example/print_function.cpp @@ -0,0 +1,68 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +using namespace boost::describe; + +template, + class Md = describe_data_members> + std::ostream& operator<<( std::ostream & os, T const & t ) +{ + os << "{"; + + bool first = true; + + boost::mp11::mp_for_each< Bd >([&](auto D){ + + if( !first ) { os << ", "; } first = false; + + using B = typename decltype(D)::type; + os << (B const&)t; + + }); + + boost::mp11::mp_for_each< Md >([&](auto D){ + + if( !first ) { os << ", "; } first = false; + + os << "." << D.name << " = " << t.*D.pointer; + + }); + + os << "}"; + return os; +} + +struct X +{ + int m1 = 1; +}; + +BOOST_DESCRIBE_STRUCT(X, (), (m1), ()) + +struct Y +{ + int m2 = 2; +}; + +BOOST_DESCRIBE_STRUCT(Y, (), (m2), ()) + +class Z: public X, private Y +{ + int m1 = 3; + int m2 = 4; + + BOOST_DESCRIBE_CLASS(Z, (X, Y), (), (m1, m2), ()) +}; + +#include + +int main() +{ + std::cout << Z() << std::endl; +} diff --git a/example/printing_enums_ct.cpp b/example/printing_enums_ct.cpp index b504fe8..e2e0b51 100644 --- a/example/printing_enums_ct.cpp +++ b/example/printing_enums_ct.cpp @@ -1,3 +1,7 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + #include #include #include diff --git a/example/printing_enums_rt.cpp b/example/printing_enums_rt.cpp index 632423b..70780d4 100644 --- a/example/printing_enums_rt.cpp +++ b/example/printing_enums_rt.cpp @@ -1,3 +1,7 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + #include #include #include diff --git a/example/string_to_enum.cpp b/example/string_to_enum.cpp index b989dcf..3a6b49b 100644 --- a/example/string_to_enum.cpp +++ b/example/string_to_enum.cpp @@ -1,3 +1,7 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + #include #include #include diff --git a/example/to_json.cpp b/example/to_json.cpp new file mode 100644 index 0000000..b09824e --- /dev/null +++ b/example/to_json.cpp @@ -0,0 +1,56 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +namespace app +{ + +template, + class D2 = boost::describe::describe_data_members, + class En = std::enable_if_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([&](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 v; + std::map m; +}; + +BOOST_DESCRIBE_STRUCT(B, (), (v, m), ()) + +} // namespace app + +#include + +int main() +{ + app::B b{ { { 1, 2 }, { 3, 4 } }, { { "k1", { 5, 6 } }, { "k2", { 7, 8 } } } }; + + std::cout << boost::json::value_from( b ) << std::endl; +} diff --git a/test/Jamfile b/test/Jamfile index b507bcb..2636194 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -20,7 +20,12 @@ run data_members_test.cpp ; run data_members_test2.cpp ; run member_functions_test.cpp ; -run ../example/printing_enums_ct.cpp : : : msvc:"-wd4100" ; +local NW = msvc-14.0:"-wd4100" ; + +run ../example/printing_enums_ct.cpp : : : $(NW) ; run ../example/printing_enums_rt.cpp ; -run ../example/enum_to_string.cpp : : : msvc:"-wd4100" ; -run ../example/string_to_enum.cpp : : : msvc:"-wd4100" ; +run ../example/enum_to_string.cpp : : : $(NW) ; +run ../example/string_to_enum.cpp : : : $(NW) ; +run ../example/print_function.cpp : : : $(NW) ; +run ../example/to_json.cpp /boost//json /boost//container : : : $(NW) ; +run ../example/from_json.cpp /boost//json : : : msvc-14.0:no ;