Author: Peter Dimov

# Describe: A C++14 Reflection Library

Peter Dimov

## Overview

This library enables authors of user-defined types (enums, structs and classes) to describe their enumerators, base classes, data members and member functions. This information can later be queried by other code portions, possibly written by a different author, using the supplied primitives `describe_enumerators`, `describe_bases`, and `describe_members`.

To learn how to describe enumeration types, see Describing Enumeration Types.

To learn how to describe class types, including structs, see Describing Class Types.

For examples how this functionality is useful, see Usage Examples.

The purpose of the library is to establish a standard way of providing these reflection abilities. Many existing libraries provide their own way of describing enums or classes, but without a standard, code written by different people cannot interoperate.

Eventually, one might hope for the primitives to end up in the C++ standard, with the compiler automatically supplying the metadata necessary to describe the types, making manual macro annotation unnecessary.

## Revision History

### Changes in Boost 1.91.0

- Under C++20, described nested enums now work when the enclosing class is still incomplete. (Thanks to Julien Blanc.)

- Made `enum_to_string` constexpr. (Thanks to Julien Blanc.)

### Changes in Boost 1.84.0

- Added an overload of `enum_from_string` that takes a string view, to avoid the need for null termination.

### Changes in Boost 1.81.0

- To allow the inclusion of `enumerators.hpp`, `bases.hpp`, and `members.hpp` when the option `-pedantic` is used, the invocation of `BOOST_DESCRIBE_ENUM` has been moved from `modifiers.hpp` into a separate header, `modifier_description.hpp`. As a consequence, `modifiers.hpp` no longer includes `enum.hpp`. Code that has been relying on this implicit inclusion may fail, and will need to be fixed to include `enum.hpp`.

### Changes in Boost 1.79.0

- Enabled unions in `BOOST_DESCRIBE_STRUCT` and updated examples to check `std::is_union<T>`.

- Added example of defining a `fmtlib` class formatter.

- Added example of defining a `fmtlib` enum formatter.

- Added example of printing pointers to members.

### Changes in Boost 1.78.0

- Added `has_describe_enumerators`, `has_describe_bases`, `has_describe_members`.

- Added `enum_to_string`, `enum_from_string`.

- Added relational and stream insertion operators.

- Added `descriptor_by_name`, `descriptor_by_pointer`.

- Added `struct_to_tuple` example.

## Describing Enumeration Types

If you have an enumeration type

    enum E
    {
        v1 = 1,
        v2 = 2,
        v3 = 4,
    };

you can add reflection metadata to it via the `BOOST_DESCRIBE_ENUM` macro:

    BOOST_DESCRIBE_ENUM(E, v1, v2, v3)

The macro is defined in `<boost/describe/enum.hpp>` and should be placed in the same namespace as the enum.

If your enumerators don’t have initializers, instead of repeating them

    enum E2 { a, b, c, d };
    BOOST_DESCRIBE_ENUM(E2, a, b, c, d)

you can use the convenience macro

    BOOST_DEFINE_ENUM(E2, a, b, c, d)

which expands to the two previous lines.

For defining `enum class E2` instead, use `BOOST_DEFINE_ENUM_CLASS`. To add an underlying type, i.e. `enum E3: int` or `enum class E4: unsigned char`, use `BOOST_DEFINE_FIXED_ENUM` and `BOOST_DEFINE_FIXED_ENUM_CLASS`, respectively.

If your enumeration type is nested inside a class or a `struct`, use the `BOOST_DESCRIBE_NESTED_ENUM` macro next to the `enum`, as follows:

    class X
    {
    private:

        enum class E
        {
            v1,
            v2
        };

        BOOST_DESCRIBE_NESTED_ENUM(E, v1, v2)

    public:

        // ...
    };

Once an enumeration type `E` is annotated, one can use `describe_enumerators<E>` to obtain a descriptor list. (`describe_enumerators` is defined in the `boost::describe` namespace, in `<boost/describe/enumerators.hpp>`.)

A descriptor list is a type of the form `L<D1, D2, …​, Dn>`, where `L` is of the form `template<class…​ T> struct L {};` and `Di` is of the form

    struct Di
    {
        static constexpr E value;
        static constexpr char const* name;
    };

To iterate over the descriptor list, you can use `mp_for_each` from Mp11:

    boost::mp11::mp_for_each< boost::describe::describe_enumerators<E> >([](auto D){

        std::printf( "%s: %d\n", D.name, D.value );

    });

## Describing Class Types

### Class Types with Public Members

If you have a `struct`

    struct X
    {
        int m1;
        int m2;
    };

use the `BOOST_DESCRIBE_STRUCT` macro to describe it:

    BOOST_DESCRIBE_STRUCT(X, (), (m1, m2))

`BOOST_DESCRIBE_STRUCT` is defined in `<boost/describe/class.hpp>` and should be placed in the same namespace as the `struct`.

It takes three arguments: the `struct` name, a list of base classes (empty in our example), and a list of (public) members by name (this includes both data members and member functions.)

Since `BOOST_DESCRIBE_STRUCT` is placed outside the type, it’s non-intrisive, does not require access to the definition, and can therefore be used to describe third-party types or types defined in system headers.

### Class Types with Protected or Private Members

To describe a class type, use the `BOOST_DESCRIBE_CLASS` macro instead, placing it inside the class. This gives the macro access to the protected and private members, but is intrusive and requires access to the definition.

    class Y: private X
    {
    public:

        int m3;

    protected:

        int m4;

    private:

        int m5;

    public:

        int f() const;

    private:

        BOOST_DESCRIBE_CLASS(Y, (X), (m3, f), (m4), (m5))
    };

It takes three member lists, for the public, protected, and private members.

### Retrieving Class Properties

Once a type `T` is annotated, its properties can be retrieved via `describe_bases<T, M>` and `describe_members<T, M>` (`M` is a bitmask of modifiers such as `mod_public | mod_static | mod_function`).

These primitives are defined in namespace `boost::describe`, in the headers `<boost/describe/bases.hpp>` and `<boost/describe/members.hpp>`, respectively.

`describe_bases` takes the following possible modifiers: `mod_public`, `mod_protected`, `mod_private`, or a bitwise-or combination of them. The presence of `mod_public` includes the public bases in the result, its absence excludes them. The other two modifiers work similarly.

`describe_members` takes a bitwise-or combination of the following possible modifiers: `mod_public`, `mod_protected`, `mod_private`, `mod_static`, `mod_function`, `mod_any_member`, `mod_inherited`, `mod_hidden`.

The access modifiers work the same as with `describe_bases`.

(For types annotated with `BOOST_DESCRIBE_STRUCT`, the protected and private member lists will be empty.)

When `mod_static` is present, the static members are returned, otherwise the nonstatic members are returned.

When `mod_function` is present, the member functions are returned, otherwise the data members are returned.

When `mod_any_member` is present, `mod_static` and `mod_function` are ignored and all members are returned regardless of kind.

When `mod_inherited` is present, members of base classes are also returned.

When `mod_hidden` is present, hidden inherited members are included. A member of a base class is hidden when a derived class has a member of the same name.

For the above class `Y`, `describe_bases<Y, mod_any_access>` will return a type list `L<D1>` containing a single base descriptor `D1` describing `X`:

    struct D1
    {
        using type = X;
        static constexpr unsigned modifiers = mod_private;
    };

`describe_members<Y, mod_private>` will return a type list `L<D2>` containing the descriptor of the data member `Y::m5`:

    struct D2
    {
        static constexpr int Y::* pointer = &Y::m5;
        static constexpr char const * name = "m5";
        static constexpr unsigned modifiers = mod_private;
    };

For an example of how to use the base and data member descriptors, see Defining a Universal Print Function.

For an example of how to use member function descriptors, see Automatic JSON RPC.

### Overloaded Member Functions

To describe an overloaded member function, you will need to resort to a more complicated syntax, as simply listing its name (say, `f`) will make the library attempt to form a member pointer with `&X::f`, which would fail because it’s not clear to which `f` this expression refers.

To disambiguate, precede the function name with the type of the function, in parentheses, as shown in the following example:

    struct X
    {
        int f();
        int f() const;
        void f( int x );
    };

    BOOST_DESCRIBE_STRUCT(X, (), (
        (int ()) f,
        (int () const) f,
        (void (int)) f
    ))

The type of the function is the same as its declaration, without the name.

Be sure to retain the space between the parenthesized function type and its name, because omitting it will compile happily on GCC and Clang but will lead to inscrutable errors on MSVC due to its nonstandard preprocessor.

Pay attention to the proper placement of the parentheses, because a mistake there will also lead to hard to decipher compiler errors, on all compilers.

The same technique also works with `BOOST_DESCRIBE_CLASS`, and with static member functions:

    class Y
    {
    public:

        static void f( int x );
        static void f( int x, int y );

        BOOST_DESCRIBE_CLASS(Y, (), ((void (int)) f, (void (int, int)) f), (), ())
    };

The case where a member function and a static member function have the same name and the same function type is currently not supported.

## Usage Examples

### Printing Enumerators with a Compile Time Loop

A simple example that just iterates over the enumerator descriptors using `mp11::mp_for_each` and prints them.

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <cstdio>

    enum E
    {
        v1 = 11,
        v2,
        v3 = 5
    };

    BOOST_DESCRIBE_ENUM(E, v1, v2, v3)

    int main()
    {
        boost::mp11::mp_for_each< boost::describe::describe_enumerators<E> >([](auto D){

            std::printf( "%s: %d\n", D.name, D.value );

        });
    }

Sample output:

    v1: 11
    v2: 12
    v3: 5

### Printing Enumerators with a Run Time Loop

This is similar to the previous example, but it first builds a `std::array` with the values, and then iterates over it using an ordinary `for` loop, instead of `mp_for_each`.

    #include <boost/describe.hpp>
    #include <cstdio>
    #include <array>

    template<class E> struct enum_descriptor
    {
        E value;
        char const * name;
    };

    template<class E, template<class... T> class L, class... T>
      constexpr std::array<enum_descriptor<E>, sizeof...(T)>
        describe_enumerators_as_array_impl( L<T...> )
    {
        return { { { T::value, T::name }... } };
    }

    template<class E> constexpr auto describe_enumerators_as_array()
    {
        return describe_enumerators_as_array_impl<E>( boost::describe::describe_enumerators<E>() );
    }

    BOOST_DEFINE_ENUM(E, v1, v2, v3, v4, v5, v6)

    int main()
    {
        constexpr auto D = describe_enumerators_as_array<E>();

        for( auto const& x: D )
        {
            std::printf( "%s: %d\n", x.name, x.value );
        }
    }

Sample output:

    v1: 0
    v2: 1
    v3: 2
    v4: 3
    v5: 4
    v6: 5

### enum_to_string

This example shows a function that, given an enumerator value, returns its name. If the value does not correspond to a named value, the function returns `"(unnamed)"`.

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>

    template<class E> char const * enum_to_string( E e )
    {
        char const * r = "(unnamed)";

        boost::mp11::mp_for_each< boost::describe::describe_enumerators<E> >([&](auto D){

            if( e == D.value ) r = D.name;

        });

        return r;
    }

    #include <iostream>

    enum E
    {
        v1 = 3,
        v2,
        v3 = 11
    };

    BOOST_DESCRIBE_ENUM(E, v1, v2, v3)

    int main()
    {
        std::cout << "E(" << v1 << "): " << enum_to_string( v1 ) << std::endl;
        std::cout << "E(" << 0 << "): " << enum_to_string( E(0) ) << std::endl;
    }

Sample output:

    E(3): v1
    E(0): (unnamed)

Since release 1.78.0, the library provides `enum_to_string`. It differs from the one in the example by having a second parameter that determines what should be returned when the value doesn’t correspond to a named enumerator.

### string_to_enum

The opposite of the previous example; returns an enumerator value when given the enumerator name. When the string passed does not correspond to any enumerator name, throws an exception.

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <stdexcept>
    #include <typeinfo>
    #include <string>
    #include <cstring>

    [[noreturn]] void throw_invalid_name( char const * name, char const * type )
    {
        throw std::runtime_error(
            std::string( "Invalid enumerator name '" ) + name
            + "' for enum type '" + type + "'" );
    }

    template<class E> E string_to_enum( char const * name )
    {
        bool found = false;
        E r = {};

        boost::mp11::mp_for_each< boost::describe::describe_enumerators<E> >([&](auto D){

            if( !found && std::strcmp( D.name, name ) == 0 )
            {
                found = true;
                r = D.value;
            }

        });

        if( found )
        {
            return r;
        }
        else
        {
            throw_invalid_name( name, typeid( E ).name() );
        }
    }

    #include <iostream>

    BOOST_DEFINE_ENUM(E, v1, v2, v3)

    int main()
    {
        try
        {
            std::cout << "v1: " << string_to_enum<E>( "v1" ) << std::endl;
            std::cout << "v2: " << string_to_enum<E>( "v2" ) << std::endl;
            std::cout << "v3: " << string_to_enum<E>( "v3" ) << std::endl;
            std::cout << "v4: " << string_to_enum<E>( "v4" ) << std::endl;
        }
        catch( std::exception const & x )
        {
            std::cout << x.what() << std::endl;
        }
    }

Sample output:

    v1: 0
    v2: 1
    v3: 2
    v4: Invalid enumerator name 'v4' for enum type 'enum E'

Since release 1.78.0, the library provides `enum_from_string`. It differs from the function in the example by signaling failure by a `bool` return value instead of using exceptions. The enumerator value is assigned to the output argument.

### 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_bases<T, mod_any_access>,
        class Md = describe_members<T, mod_any_access>,
        class En = std::enable_if_t<!std::is_union<T>::value> >
        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;
    }

Sample output:

    {{.m1 = 1}, {.m2 = 2}, .m1 = 3, .m2 = 4}

Since release 1.78.0, this universal `operator<<` is supplied by the library, in the `boost::describe::operators` namespace. It’s enabled by means of a using declaration in the namespace containing the described application types, like in the example below:

    namespace app
    {

    struct X
    {
        int a = 1;
    };

    BOOST_DESCRIBE_STRUCT(X, (), (a))

    using boost::describe::operators::operator<<;

    }

### Implementing hash_value

This example defines a universal `hash_value` overload that computes the hash value of an annotated struct or class. It does so by iterating over the described bases and members and calling `boost::hash_combine` on each.

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

Note | Since release 1.81.0, Boost.ContainerHash provides its own, built-in, support for described classes, by having an overload of `boost::hash_value` very similar to the one given below. Consequently, when using Boost 1.81.0 or later, you don’t need the below `hash_value` function, as it will cause an ambiguity.

    #include <boost/container_hash/hash.hpp>
    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <boost/variant2/variant.hpp>
    #include <boost/version.hpp>
    #include <vector>

    using namespace boost::describe;

    namespace app
    {

    #if BOOST_VERSION < 108100

    template<class T,
        class Bd = describe_bases<T, mod_any_access>,
        class Md = describe_members<T, mod_any_access>,
        class En = std::enable_if_t<!std::is_union<T>::value> >
        std::size_t hash_value( T const & t )
    {
        std::size_t r = 0;

        boost::mp11::mp_for_each<Bd>([&](auto D){

            using B = typename decltype(D)::type;
            boost::hash_combine( r, (B const&)t );

        });

        boost::mp11::mp_for_each<Md>([&](auto D){

            boost::hash_combine( r, t.*D.pointer );

        });

        return r;
    }

    #endif

    struct A
    {
        int x = 1;
    };

    BOOST_DESCRIBE_STRUCT(A, (), (x))

    struct B
    {
        int y = 2;
    };

    BOOST_DESCRIBE_STRUCT(B, (), (y))

    struct C
    {
        std::vector<boost::variant2::variant<A, B>> v;
    };

    BOOST_DESCRIBE_STRUCT(C, (), (v))

    } // namespace app

    #include <iostream>

    int main()
    {
        app::C c;

        c.v.push_back( app::A{} );
        c.v.push_back( app::B{} );

        std::cout << boost::hash<app::C>()( c ) << std::endl;
    }

Sample output:

    12526671134390370097

### Implementing operator==

This example defines a universal `operator==` overload that iterates over the described bases and members and compares them for equality using `==`.

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

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <boost/variant2/variant.hpp>
    #include <vector>

    using namespace boost::describe;

    namespace app
    {
    template<class T,
        class Bd = describe_bases<T, mod_any_access>,
        class Md = describe_members<T, mod_any_access>,
        class En = std::enable_if_t<!std::is_union<T>::value> >
        bool operator==( T const& t1, T const& t2 )
    {
        bool r = true;

        boost::mp11::mp_for_each<Bd>([&](auto D){

            using B = typename decltype(D)::type;
            r = r && (B const&)t1 == (B const&)t2;

        });

        boost::mp11::mp_for_each<Md>([&](auto D){

            r = r && t1.*D.pointer == t2.*D.pointer;

        });

        return r;
    }

    struct A
    {
        int x = 1;
    };

    BOOST_DESCRIBE_STRUCT(A, (), (x))

    struct B
    {
        int y = 2;
    };

    BOOST_DESCRIBE_STRUCT(B, (), (y))

    struct C
    {
        std::vector<boost::variant2::variant<A, B>> v;
    };

    BOOST_DESCRIBE_STRUCT(C, (), (v))

    } // namespace app

    #include <iostream>

    int main()
    {
        app::C c1, c2, c3;

        c1.v.push_back( app::A{} );
        c2.v.push_back( app::A{} );
        c3.v.push_back( app::B{} );

        std::cout << std::boolalpha
            << ( c1 == c2 ) << ' '
            << ( c1 == c3 ) << std::endl;
    }

Sample output:

    true false

Since release 1.78.0, this universal `operator==` is supplied by the library, in the `boost::describe::operators` namespace. It’s enabled by means of a using declaration in the namespace containing the described application types, like in the example below:

    namespace app
    {

    struct X
    {
        int a = 1;
    };

    BOOST_DESCRIBE_STRUCT(X, (), (a))

    using boost::describe::operators::operator==;

    }

The rest of the relational operators are also provided and can be enabled similarly.

### struct_to_tuple

This example defines a function `struct_to_tuple` that takes a described class type as an argument and returns a tuple of all its public members.

    #include <boost/describe.hpp>
    #include <tuple>

    namespace desc = boost::describe;

    template<class T, template<class...> class L, class... D>
    auto struct_to_tuple_impl( T const& t, L<D...> )
    {
        return std::make_tuple( t.*D::pointer... );
    }

    template<class T,
        class Dm = desc::describe_members<T,
            desc::mod_public | desc::mod_inherited>,
        class En = std::enable_if_t<!std::is_union<T>::value> >
    auto struct_to_tuple( T const& t )
    {
        return struct_to_tuple_impl( t, Dm() );
    }

    #include <boost/core/type_name.hpp>
    #include <iostream>

    struct X
    {
        int a = 1;
    };

    BOOST_DESCRIBE_STRUCT(X, (), (a))

    struct Y
    {
        float b = 3.14f;
    };

    BOOST_DESCRIBE_STRUCT(Y, (), (b))

    struct Z: X, Y
    {
    };

    BOOST_DESCRIBE_STRUCT(Z, (X, Y), ())

    int main()
    {
        Z z;

        auto tp = struct_to_tuple( z );

        std::cout <<
            boost::core::type_name<decltype(tp)>() << ": "
            << std::get<0>(tp) << ", " << std::get<1>(tp);
    }

Sample output:

    std::tuple<int, float>: 1, 3.14

### Automatic Conversion to JSON

This example defines a universal `tag_invoke` overload that automatically converts an annotated struct to a Boost.JSON value by iterating over the described public members and adding them to the return `boost::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`.

Note | Since release 1.81.0, Boost.JSON provides its own, built-in, support for described classes. Consequently, when using Boost 1.81.0 or later, you don’t need the below `tag_invoke` function.

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

    namespace app
    {

    #if BOOST_VERSION < 108100

    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 );

        });
    }

    #endif

    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;
    }

Sample output:

    {"v":[{"x":1,"y":2},{"x":3,"y":4}],"m":{"k1":{"x":5,"y":6},"k2":{"x":7,"y":8}}}

### Automatic Conversion from JSON

Like the previous example, but in the other direction. Defines a `tag_invoke` overload that converts a `boost::json::value` to an annotated struct.

Note | Since release 1.81.0, Boost.JSON provides its own, built-in, support for described classes. Consequently, when using Boost 1.81.0 or later, you don’t need the below `tag_invoke` function.

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <boost/json.hpp>
    #include <boost/version.hpp>
    #include <type_traits>

    namespace app
    {

    #if BOOST_VERSION < 108100

    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_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> >

        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;
    }

    #endif

    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;
    }

Sample output:

    jv: {"x":1,"y":2}
    a: { 1, 2 }

### Automatic Serialization

This example defines a universal `serialize` function that automatically adds Boost.Serialization support to annotated classes.

    #define _CRT_SECURE_NO_WARNINGS

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <boost/serialization/serialization.hpp>
    #include <boost/serialization/base_object.hpp>
    #include <boost/serialization/vector.hpp>
    #include <boost/core/nvp.hpp>
    #include <type_traits>
    #include <cstdio>
    #include <vector>

    namespace app
    {

    template<class Archive, class T,
        class D1 = boost::describe::describe_bases<T, boost::describe::mod_public>,
        class D2 = boost::describe::describe_bases<T,
            boost::describe::mod_protected | boost::describe::mod_private>,
        class D3 = boost::describe::describe_members<T,
            boost::describe::mod_public | boost::describe::mod_protected>,
        class D4 = boost::describe::describe_members<T, boost::describe::mod_private>,
        class En = std::enable_if_t< boost::mp11::mp_empty<D2>::value &&
            boost::mp11::mp_empty<D4>::value && !std::is_union<T>::value> >

        void serialize( Archive & ar, T & t, boost::serialization::version_type )
    {
        int k = 0;

        // public bases: use base_object

        boost::mp11::mp_for_each<D1>([&](auto D){

            using B = typename decltype(D)::type;

            char name[ 32 ];
            std::sprintf( name, "base.%d", ++k );

            ar & boost::make_nvp( name, boost::serialization::base_object<B>( t ) );

        });

        // public (and protected) members

        boost::mp11::mp_for_each<D3>([&](auto D){

            ar & boost::make_nvp( D.name, t.*D.pointer );

        });
    }

    struct A1
    {
        int x;
    };

    BOOST_DESCRIBE_STRUCT(A1, (), (x))

    struct A2
    {
        int y;
    };

    BOOST_DESCRIBE_STRUCT(A2, (), (y))

    struct B: public A1, public A2
    {
        // these constructors aren't needed in C++17
        B(): A1(), A2() {}
        B( int x, int y ): A1{ x }, A2{ y } {}
    };

    BOOST_DESCRIBE_STRUCT(B, (A1, A2), ())

    struct C
    {
        std::vector<B> v;
    };

    BOOST_DESCRIBE_STRUCT(C, (), (v))

    } // namespace app

    #include <boost/archive/xml_oarchive.hpp>
    #include <boost/archive/xml_iarchive.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <string>
    #include <sstream>
    #include <iostream>

    int main()
    {
        app::C c1{ { { 1, 2 }, { 3, 4 }, { 5, 6 } } };

        std::ostringstream os;

        {
            boost::archive::xml_oarchive ar( os );
            ar << boost::make_nvp( "c1", c1 );
        }

        std::string s = os.str();

        std::cout << s << std::endl;

        app::C c2;

        {
            std::istringstream is( s );
            boost::archive::xml_iarchive ar( is );
            ar >> boost::make_nvp( "c2", c2 );
        }

        {
            boost::archive::text_oarchive ar( std::cout );
            ar << c2;
        }
    }

Sample output:

    <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
    <!DOCTYPE boost_serialization>
    <boost_serialization signature="serialization::archive" version="19">
    <c1 class_id="0" tracking_level="0" version="0">
            <v class_id="1" tracking_level="0" version="0">
                    <count>3</count>
                    <item_version>0</item_version>
                    <item class_id="2" tracking_level="0" version="0">
                            <base.1 class_id="3" tracking_level="0" version="0">
                                    <x>1</x>
                            </base.1>
                            <base.2 class_id="4" tracking_level="0" version="0">
                                    <y>2</y>
                            </base.2>
                    </item>
                    <item>
                            <base.1>
                                    <x>3</x>
                            </base.1>
                            <base.2>
                                    <y>4</y>
                            </base.2>
                    </item>
                    <item>
                            <base.1>
                                    <x>5</x>
                            </base.1>
                            <base.2>
                                    <y>6</y>
                            </base.2>
                    </item>
            </v>
    </c1>
    </boost_serialization>

    22 serialization::archive 19 0 0 0 0 3 0 0 0 0 0 1 0 0 2 3 4 5 6

### Automatic JSON RPC

This example defines a generic `call` function that can be used to invoke a member function by name, with the arguments passed in a Boost.JSON array. The result is returned in a `boost::json::value`.

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <boost/json.hpp>
    #include <boost/type_traits.hpp>
    #include <boost/utility/string_view.hpp>
    #include <stdexcept>
    #include <string>

    template<class C1, class C2, class R, class... A, std::size_t... I>
      boost::json::value
        call_impl_( C1 & c1, R (C2::*pmf)(A...), boost::json::array const & args,
          std::index_sequence<I...> )
    {
        return boost::json::value_from(
          (c1.*pmf)(
            boost::json::value_to< boost::remove_cv_ref_t<A> >( args[ I ] )... ) );
    }

    template<class C1, class C2, class R, class... A>
      boost::json::value
        call_impl( C1 & c1, R (C2::*pmf)(A...), boost::json::array const & args )
    {
        if( args.size() != sizeof...(A) )
        {
            throw std::invalid_argument( "Invalid number of arguments" );
        }

        return call_impl_( c1, pmf, args, std::index_sequence_for<A...>() );
    }

    template<class C>
      boost::json::value
        call( C & c, boost::string_view method, boost::json::array const & args )
    {
        using Fd = boost::describe::describe_members<C,
            boost::describe::mod_public | boost::describe::mod_function>;

        bool found = false;
        boost::json::value result;

        boost::mp11::mp_for_each<Fd>([&](auto D){

            if( !found && method == D.name)
            {
                result = call_impl( c, D.pointer, args );
                found = true;
            }

        });

        if( !found )
        {
            throw std::invalid_argument( "Invalid method name" );
        }

        return result;
    }

    struct Object
    {
        std::string greet( std::string const & who )
        {
            return "Hello, " + who + "!";
        }

        int add( int x, int y )
        {
            return x + y;
        }
    };

    BOOST_DESCRIBE_STRUCT(Object, (), (greet, add))

    #include <iostream>

    int main()
    {
        Object obj;
        std::cout << call( obj, "greet", { "world" } ) << std::endl;
        std::cout << call( obj, "add", { 1, 2 } ) << std::endl;
    }

Sample output:

    "Hello, world!"
    3

### Interactive Variable Console

This example implements an interactive console that allows printing and modifying variables. It uses Boost.JSON for converting the variables to and from a string form.

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <boost/json.hpp>
    #include <boost/utility/string_view.hpp>
    #include <string>
    #include <stdexcept>
    #include <vector>
    #include <map>
    #include <iostream>

    // get variable

    template<class Scope> boost::json::value get( Scope& scope, boost::string_view name )
    {
        using Md = boost::describe::describe_members<Scope, boost::describe::mod_public>;

        bool found = false;
        boost::json::value result;

        boost::mp11::mp_for_each<Md>([&](auto D) {

            if( !found && name == D.name )
            {
                result = boost::json::value_from( scope.*D.pointer );
                found = true;
            }

        });

        if( !found )
        {
            throw std::invalid_argument(
                std::string( "'" ) + std::string( name ) + "': no such variable" );
        }

        return result;
    }

    // set variable

    template<class T> void set_impl( T & t, boost::string_view /*name*/, boost::json::value const& value )
    {
        t = boost::json::value_to<T>( value );
    }

    template<class T> void set_impl( T const & /*t*/, boost::string_view name, boost::json::value const& /*value*/ )
    {
        throw std::invalid_argument(
            std::string( "'" ) + std::string( name ) + "': variable cannot be modified" );
    }

    template<class Scope> void set( Scope& scope, boost::string_view name, boost::json::value const& value )
    {
        using Md = boost::describe::describe_members<Scope, boost::describe::mod_public>;

        bool found = false;

        boost::mp11::mp_for_each<Md>([&](auto D) {

            if( !found && name == D.name )
            {
                set_impl( scope.*D.pointer, name, value );
                found = true;
            }

        });

        if( !found )
        {
            throw std::invalid_argument(
                std::string( "'" ) + std::string( name ) + "': no such variable" );
        }
    }

    //

    struct globals
    {
        std::string const help = "Enter a variable name ('x', 'y', 'v', or 'm') to print its value; enter variable=value to assign a new value to a variable. Values are in JSON format.";

        int x = 1;

        double y = 3.14;

        std::vector<int> v{ 1, 2, 3 };

        std::map<std::string, double> m{ { "BTC", 44898.68 }, { "ETH", 1386.57 } };
    };

    BOOST_DESCRIBE_STRUCT( globals, (), (help, x, y, v, m) )

    int main()
    {
        globals g_;

        for( ;; )
        {
            std::cout << "\n> ";

            std::string line;
            std::getline( std::cin, line );

            try
            {
                std::size_t i = line.find( '=' );

                if( i != std::string::npos )
                {
                    set( g_, line.substr( 0, i ), boost::json::parse( line.substr( i + 1 ) ) );
                }
                else
                {
                    std::cout << get( g_, line ) << std::endl;
                }
            }
            catch( std::exception const& x )
            {
                std::cout << "Error: " << x.what() << std::endl;
            }
        }
    }

Sample output:

    > help
    "Enter a variable name ('x', 'y', 'v', or 'm') to print its value; enter variable=value to assign a new value to a variable. Values are in JSON format."

    > x
    1

    > y
    3.14E0

    > v
    [1,2,3]

    > m
    {"BTC":4.489868E4,"ETH":1.38657E3}

    > x="hello"
    Error: not a number [boost.json:15]

    > x=3.14
    Error: not exact [boost.json:16]

    > x=4

    > x
    4

    > y=6.28

    > y
    6.28E0

    > v=["hello", "world"]
    Error: not a number [boost.json:15]

    > v=[1.2, 3.4]
    Error: not exact [boost.json:16]

    > v=[11, 27]

    > c
    Error: 'c': no such variable

    > v
    [11,27]

    > m={"BTC": 42139.07, "ETH": 2912.00}

    > m
    {"BTC":4.213907E4,"ETH":2.912E3}

### `fmtlib` Class Formatter

This example defines a universal `fmtlib` formatter that works on any class or struct type that has been described with `BOOST_DESCRIBE_STRUCT` or `BOOST_DESCRIBE_CLASS`. It’s similar to the universal print function shown above.

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <fmt/format.h>
    #include <type_traits>

    template<class T> struct fmt::formatter<T, char, std::enable_if_t<
        boost::describe::has_describe_bases<T>::value &&
        boost::describe::has_describe_members<T>::value &&
        !std::is_union<T>::value>>
    {
        constexpr auto parse( format_parse_context& ctx )
        {
            auto it = ctx.begin(), end = ctx.end();

            if( it != end && *it != '}' )
            {
                throw_format_error( "invalid format" );
            }

            return it;
        }

        auto format( T const& t, format_context& ctx ) const
        {
            using namespace boost::describe;

            using Bd = describe_bases<T, mod_any_access>;
            using Md = describe_members<T, mod_any_access>;

            auto out = ctx.out();

            *out++ = '{';

            bool first = true;

            boost::mp11::mp_for_each<Bd>([&](auto D){

                if( !first )
                {
                    *out++ = ',';
                }

                first = false;

                out = fmt::format_to( out, " {}",
                    (typename decltype(D)::type const&)t );
            });

            boost::mp11::mp_for_each<Md>([&](auto D){

                if( !first )
                {
                    *out++ = ',';
                }

                first = false;

                out = fmt::format_to( out, " .{}={}",
                    D.name, t.*D.pointer );
            });

            if( !first )
            {
                *out++ = ' ';
            }

            *out++ = '}';

            return out;
        }
    };

    struct point
    {
        int x, y;
    };

    BOOST_DESCRIBE_STRUCT( point, (), (x, y) )

    struct color
    {
        unsigned char r, g, b;
    };

    BOOST_DESCRIBE_STRUCT( color, (), (r, g, b) )

    struct line: color
    {
        point first, last;
    };

    BOOST_DESCRIBE_STRUCT( line, (color), (first, last) )

    int main()
    {
        fmt::print( "{}\n", line{ { 255, 192, 16 }, { 1, 2 }, { 3, 4 } } );
    }

Sample output:

    { { .r=255, .g=192, .b=16 }, .first={ .x=1, .y=2 }, .last={ .x=3, .y=4 } }

### `fmtlib` Enum Formatter

This example defines a `fmtlib` formatter for described enums.

    #include <boost/describe.hpp>
    #include <fmt/format.h>
    #include <type_traits>

    template<class T> struct fmt::formatter<T, char, std::enable_if_t<
        boost::describe::has_describe_enumerators<T>::value>>
    {
    private:

        using U = std::underlying_type_t<T>;

        fmt::formatter<fmt::string_view, char> sf_;
        fmt::formatter<U, char> nf_;

    public:

        constexpr auto parse( format_parse_context& ctx )
        {
            auto i1 = sf_.parse( ctx );
            auto i2 = nf_.parse( ctx );

            if( i1 != i2 )
            {
                throw_format_error( "invalid format" );
            }

            return i1;
        }

        auto format( T const& t, format_context& ctx ) const
        {
            char const * s = boost::describe::enum_to_string( t, 0 );

            if( s )
            {
                return sf_.format( s, ctx );
            }
            else
            {
                return nf_.format( static_cast<U>( t ), ctx );
            }
        }
    };

    enum E1
    {
        v1, v2, v3 = 11
    };

    BOOST_DESCRIBE_ENUM( E1, v1, v2, v3 )

    int main()
    {
        fmt::print( "{:_^10}\n", E1::v1 );
        fmt::print( "{:_^10}\n", (E1)7 );
    }

Sample output:

    ____v1____
    ____7_____

### Printing Pointers to Members

This example defines an `operator<<` overload for pointers to members.

    #include <boost/describe.hpp>
    #include <boost/mp11.hpp>
    #include <boost/core/type_name.hpp>
    #include <string>
    #include <ostream>

    namespace detail
    {

    template<class T1, class T2>
    constexpr auto equals( T1 t1, T2 t2, int ) -> decltype( t1 == t2 )
    {
        return t1 == t2;
    }

    template<class T1, class T2>
    constexpr bool equals( T1 /*t1*/, T2 /*t2*/, long )
    {
        return false;
    }

    using namespace boost::describe;

    template<class T, class C,
        class L = describe_members<C, mod_any_access | mod_any_member>>
    char const* get_member_name( T C::* pm, int )
    {
        char const * name = nullptr;

        boost::mp11::mp_for_each<L>([&](auto D){

            if( equals( D.pointer, pm, 0 ) ) name = D.name;

        });

        return name;
    }

    template<class T, class C>
    char const* get_member_name( T C::* /*pm*/, long )
    {
        return nullptr;
    }

    } // namespace detail

    template<class T, class C>
    std::string pm_to_string( T C::* pm )
    {
        char const * name = ::detail::get_member_name( pm, 0 );

        if( name == nullptr ) name = "(unknown)";

        return "&" + boost::core::type_name<C>() + "::" + name
            + " [" + boost::core::type_name<T>() + "]";
    }

    template<class T, class C>
    std::ostream& operator<<( std::ostream& os, T C::* pm )
    {
        os << pm_to_string( pm );
        return os;
    }

    struct X
    {
        int m;
        int f() const { return m; }
    };

    BOOST_DESCRIBE_STRUCT(X, (), (m, f))

    struct Y: public X
    {
        int m;
        int g() const { return -m; }
    };

    BOOST_DESCRIBE_STRUCT(Y, (X), (m, g))

    struct Z
    {
        void h() {}
    };

    #if !defined(_MSC_VER) || defined(__clang__)

    // MSVC doesn't support BOOST_DESCRIBE_CLASS inside
    // templates until 2022 in C++20 mode

    template<class T1, class T2> struct pair
    {
        T1 first;
        T2 second;

        BOOST_DESCRIBE_CLASS(pair, (), (first, second), (), ())
    };

    #endif

    #include <iostream>

    int main()
    {
        std::cout << &X::m << std::endl;
        std::cout << &X::f << std::endl;

        std::cout << &Y::m << std::endl;
        std::cout << &Y::f << std::endl;
        std::cout << &Y::g << std::endl;

        std::cout << &Z::h << std::endl;

    #if !defined(_MSC_VER) || defined(__clang__)
        std::cout << &pair<int, float>::second << std::endl;
    #endif
    }

Sample output:

    &X::m [int]
    &X::f [int() const]
    &Y::m [int]
    &X::f [int() const]
    &Y::g [int() const]
    &Z::(unknown) [void()]

## Implementation Features

### Dependencies

Boost.Mp11.

### Supported Compilers

- GCC 5 or later with `-std=c++14` or above

- Clang 3.9 or later with `-std=c++14` or above

- Visual Studio 2015 or later

Tested on Github Actions and Appveyor.

### Limitations

This implementation has the following limitations:

- Up to 52 elements are supported in the lists of enumerators, bases, and members.

- Protected base classes cannot be distinguished from private base classes when the described class is final, and are considered private.

- Bitfields are not supported. It’s not possible to form a pointer to member to a bitfield.

- Reference members are not supported. It’s not possible to form a pointer to member to a reference.

- Anonymous unions are not supported.

## Reference

### <boost/describe/enum.hpp>

    #define BOOST_DESCRIBE_ENUM(E, ...) /*...*/

    #define BOOST_DESCRIBE_NESTED_ENUM(E, ...) /*...*/

    #define BOOST_DEFINE_ENUM(E, ...) \
        enum E { __VA_ARGS__ }; BOOST_DESCRIBE_ENUM(E, __VA_ARGS__)

    #define BOOST_DEFINE_ENUM_CLASS(E, ...) \
        enum class E { __VA_ARGS__ }; BOOST_DESCRIBE_ENUM_CLASS(E, __VA_ARGS__)

    #define BOOST_DEFINE_FIXED_ENUM(E, Base, ...) \
        enum E: Base { __VA_ARGS__ }; BOOST_DESCRIBE_ENUM(E, __VA_ARGS__)

    #define BOOST_DEFINE_FIXED_ENUM_CLASS(E, Base, ...) \
        enum class E: Base { __VA_ARGS__ }; BOOST_DESCRIBE_ENUM_CLASS(E, __VA_ARGS__)

#### BOOST_DESCRIBE_ENUM

`BOOST_DESCRIBE_ENUM(E, v1, v2, …​, vN)` should be placed in the namespace where the enumeration type `E` is defined, and creates the necessary metadata for `describe_enumerators<E>` to work.

After this macro is used, `describe_enumerators<E>` returns `L<D1, D2, …​, Dn>`, where `L` is a class template of the form

    template<class...> struct L {};

and `Di` is an enumerator descriptor of the form

    struct Di
    {
        static constexpr E value = vi;
        static constexpr char const * name = "vi";
    };

where `vi` is the corresponding identifier passed to the macro.

#### BOOST_DESCRIBE_NESTED_ENUM

`BOOST_DESCRIBE_NESTED_ENUM(E, v1, v2, …​, vN)` is similar to `BOOST_DESCRIBE_ENUM` and is used to annotate enumeration types nested inside class (or `struct`) types. It should be placed in the class type where the `enum` is defined.

#### BOOST_DEFINE_ENUM

`BOOST_DEFINE_ENUM(E, v1, v2, …​, vN)` is a convenience macro expanding to

    enum E { v1, v2, ..., vN };
    BOOST_DESCRIBE_ENUM(E, v1, v2, ..., vN)

#### BOOST_DEFINE_ENUM_CLASS

`BOOST_DEFINE_ENUM_CLASS(E, v1, v2, …​, vN)` is a convenience macro expanding to

    enum class E { v1, v2, ..., vN };
    BOOST_DESCRIBE_ENUM(E, v1, v2, ..., vN)

#### BOOST_DEFINE_FIXED_ENUM

`BOOST_DEFINE_FIXED_ENUM(E, Base, v1, v2, …​, vN)` is a convenience macro expanding to

    enum E: Base { v1, v2, ..., vN };
    BOOST_DESCRIBE_ENUM(E, v1, v2, ..., vN)

#### BOOST_DEFINE_FIXED_ENUM_CLASS

`BOOST_DEFINE_FIXED_ENUM_CLASS(E, Base, v1, v2, …​, vN)` is a convenience macro expanding to

    enum class E: Base { v1, v2, ..., vN };
    BOOST_DESCRIBE_ENUM(E, v1, v2, ..., vN)

### <boost/describe/​enumerators​.hpp>

    namespace boost {
    namespace describe {

    template<class E> using describe_enumerators = /*...*/;

    template<class E> using has_describe_enumerators = /*...*/;

    } }

#### describe_enumerators<E>

`describe_enumerators<E>` returns `L<D1, D2, …​, Dn>`, where `L` is a class template of the form

    template<class...> struct L {};

and `Di` is an enumerator descriptor of the form

    struct Di
    {
        static constexpr E value = vi;
        static constexpr char const * name = "vi";
    };

where `vi` is the i-th enumerator.

If `E` is not a described enumeration type, `describe_enumerators<E>` causes a substitution failure.

#### has_describe_enumerators<E>

`has_describe_enumerators<E>::value` is `true` when `E` is a described enumeration type, `false` otherwise.

### <boost/describe/class.hpp>

    #define BOOST_DESCRIBE_STRUCT(Name, Bases, Members) /*...*/
    #define BOOST_DESCRIBE_CLASS(Name, Bases, Public, Protected, Private) /*...*/

#### BOOST_DESCRIBE_STRUCT

`BOOST_DESCRIBE_STRUCT` should be placed in the same namespace as the struct type being described, and takes three arguments: name of the struct type, a parentheses-enclosed list of base classes, and a parentheses-enclosed list of public members.

Example:

    struct X
    {
    };

    BOOST_DESCRIBE_STRUCT(X, (), ())

    struct Y: public X
    {
        int m;
        static void f();
    };

    BOOST_DESCRIBE_STRUCT(Y, (X), (m, f))

#### BOOST_DESCRIBE_CLASS

`BOOST_DESCRIBE_CLASS` should be placed inside the class definition of the described type, and takes five arguments: the name of the class, a list of base classes, a list of public members, a list of protected members, and a list of private members.

Example:

    class X
    {
        int m1;

        BOOST_DESCRIBE_CLASS(X, (), (), (), (m1))
    };

    class Y: private X
    {
    public:

        int m1;
        void f() const {}

    protected:

        int m2;

    private:

        int m3;

        BOOST_DESCRIBE_CLASS(Y, (X), (m1, f), (m2), (m3))
    };

### <boost/describe/modifiers.hpp>

    namespace boost
    {
    namespace describe
    {

    enum modifiers
    {
        mod_public = 1,
        mod_protected = 2,
        mod_private = 4,
        mod_virtual = 8,
        mod_static = 16,
        mod_function = 32,
        mod_any_member = 64,
        mod_inherited = 128,
        mod_hidden = 256
    };

    constexpr modifiers mod_any_access = static_cast<modifiers>( mod_public | mod_protected | mod_private );

    } // namespace describe
    } // namespace boost

#### modifiers

The enumeration type `modifiers` is a bitmask type that contains the following flags:

- `mod_public` - includes public bases or members in the descriptor list

- `mod_protected` - includes protected bases or members

- `mod_private` - includes private bases or members

- `mod_virtual` - returned when a base class is a virtual base

- `mod_static` - returns static members (when not given, returns nonstatic members)

- `mod_function` - returns member functions (when not given, returns data members)

- `mod_any_member` - overrides `mod_static` and `mod_function` and returns all members regardless of kind

- `mod_inherited` - includes members of base classes

- `mod_hidden` - includes hidden inherited members

### <boost/describe/​modifier_description.hpp>

The header `modifier_description.hpp` includes `modifiers.hpp` and invokes `BOOST_DESCRIBE_ENUM` on `modifiers`, allowing `describe_enumerators<modifiers>` to work.

### <boost/describe/bases.hpp>

    namespace boost {
    namespace describe {

    template<class T, unsigned M> using describe_bases = /*...*/;

    template<class T> using has_describe_bases = /*...*/;

    } }

#### describe_bases<T, M>

`M` must be a bitwise-or combination of `mod_public`, `mod_protected`, and `mod_private`, and acts as a filter.

`describe_bases<T, M>` returns `L<D1, D2, …​, Dn>`, where `L` is a class template of the form

    template<class...> struct L {};

and `Di` is a base descriptor of the form

    struct Di
    {
        using type = /*...*/;
        static constexpr unsigned modifiers = /*...*/;
    };

where `type` is the type of the base class, and `modifiers` are a bitwise-or combination of `mod_public`, `mod_protected`, `mod_private`, and `mod_virtual` that reflects the properties of the base class.

If `T` is not a described class type, `describe_bases<T, M>` causes a substitution failure.

#### has_describe_bases<T>

`has_describe_bases<T>::value` is `true` when `T` is a described class type, `false` otherwise.

Since the library does not provide a way to describe bases and members separately, `has_describe_bases` and `has_describe_members` are, in practice, synonyms. They are provided separately for consistency.

### <boost/describe/members.hpp>

    namespace boost {
    namespace describe {

    template<class T, unsigned M> using describe_members = /*...*/;

    template<class T> using has_describe_members = /*...*/;

    } }

#### describe_members<T, M>

`M` must be a bitwise-or combination of `mod_public`, `mod_protected`, `mod_private`, `mod_static`, `mod_function`, `mod_any_member`, `mod_inherited`, and `mod_hidden`, and acts as a filter.

`describe_members<T, M>` returns `L<D1, D2, …​, Dn>`, where `L` is a class template of the form

    template<class...> struct L {};

and `Di` is a member descriptor of the form

    struct Di
    {
        static constexpr auto pointer = &T::m;
        static constexpr char const * name = "m";
        static constexpr unsigned modifiers = /*...*/;
    };

where `pointer` is a pointer to member (for nonstatic members) or a pointer (for static members) identifying the class member, `name` is the name of the member, and `modifiers` are a bitwise-or combination of `mod_public`, `mod_protected`, `mod_private`, `mod_static`, `mod_function`, `mod_inherited`, and `mod_hidden` that reflects the properties of the member.

If `T` is not a described class type, `describe_members<T, M>` causes a substitution failure.

#### has_describe_members<T>

`has_describe_members<T>::value` is `true` when `T` is a described class type, `false` otherwise.

Since the library does not provide a way to describe bases and members separately, `has_describe_bases` and `has_describe_members` are, in practice, synonyms. They are provided separately for consistency.

### <boost/describe/​enum_to_string​.hpp>

    namespace boost {
    namespace describe {

    template<class E> char const * enum_to_string( E e, char const * def ) noexcept;

    } }

#### enum_to_string

The function `enum_to_string` returns the name of the enumerator `e`. `E` must be a described enumeration type. If `e` does not correspond to one of the described values, the function returns `def`.

### <boost/describe/​enum_from_string​.hpp>

    namespace boost {
    namespace describe {

    template<class E> bool enum_from_string( char const * name, E & e ) noexcept;
    template<class S, class E> bool enum_from_string( S const & name, E & e ) noexcept;

    } }

#### enum_from_string

The function `enum_from_string` assigns to `e` the enumerator value corresponding to `name` and returns `true`. `E` must be a described enumeration type. If `name` does not correspond to one of the described values, the function returns `false`.

The second overload only participates in overload resolution when `S` is a string-like type such as `std::string` or `std::string_view`. String-like types are recognized by the presence of nested `value_type` and `traits_type` typedefs and are expected to have a non-throwing `operator==` taking `char const*`.

### <boost/describe/operators.hpp>

    namespace boost {
    namespace describe {
    namespace operators {

    template<class T> bool operator==( T const& t1, T const& t2 );
    template<class T> bool operator!=( T const& t1, T const& t2 );
    template<class T> bool operator<( T const& t1, T const& t2 );
    template<class T> bool operator>( T const& t1, T const& t2 );
    template<class T> bool operator<=( T const& t1, T const& t2 );
    template<class T> bool operator>=( T const& t1, T const& t2 );

    template<class T, class Ch, class Tr>
      std::basic_ostream<Ch, Tr>&
        operator<<( std::basic_ostream<Ch, Tr>& os, T const& t );

    } } }

The header `<boost/describe/operators.hpp>` defines generic operators for described class types. They are used by bringing them into the namespace containing the described types via a using declaration, as in the example below:

    namespace app
    {

    struct X
    {
        int a = 1;
    };

    BOOST_DESCRIBE_STRUCT(X, (), (a))

    using boost::describe::operators::operator==;
    using boost::describe::operators::operator!=;
    using boost::describe::operators::operator<<;

    }

#### operator==

If all bases and members compare equal, returns `true`, otherwise `false`.

#### operator!=

Returns the negation of `operator==`.

#### operator<

Performs a lexicographical comparison over the bases and members in sequence using `operator<` and returns the result.

#### operator>

Returns the result of `operator<` with the arguments reversed.

#### operator<=

Returns the negated result of `operator>`.

#### operator>=

Returns the negated result of `operator<`.

#### operator<<

Outputs a representation of `t` to `os` by recursively using `operator<<` to output all bases and then all members.

### <boost/describe/​descriptor_by_name​.hpp>

    namespace boost {
    namespace describe {

    #define BOOST_DESCRIBE_MAKE_NAME(s) /*...*/

    template<class L, class N> using descriptor_by_name = /*...*/;

    } }

#### BOOST_DESCRIBE_MAKE_NAME

The macro `BOOST_DESCRIBE_MAKE_NAME` creates a type that identifies the name given as an argument. It should be used as follows:

    using N = BOOST_DESCRIBE_MAKE_NAME(some_member);

#### descriptor_by_name

`descriptor_by_name<L, N>` searches the descriptor list `L` for the member with the name identified by `N`. `N` should be a type created by `BOOST_DESCRIBE_MAKE_NAME` as in the above example. `L` is intended to be a list returned by `describe_members`, although since enumerator descriptors also have `::name`, a list returned by `describe_enumerators` will work as well.

Code Example 1. Using descriptor_by_name

    using L = describe_members<SomeType, mod_any_access>;
    using N = BOOST_DESCRIBE_MAKE_NAME(some_member);
    using D = descriptor_by_name<L, N>; // descriptor for SomeType::some_member

### <boost/describe/​descriptor_by_pointer​.hpp>

    namespace boost {
    namespace describe {

    template<class L, auto Pm> using descriptor_by_pointer = /*...*/;

    } }

#### descriptor_by_pointer

`descriptor_by_pointer<L, Pm>` searches the descriptor list `L` for the member pointer `Pm`. `L` should be a list returned by `describe_members`.

Since `auto` template parameters are a C++17 feature, using `descriptor_by_pointer` requires C++17.

Code Example 2. Using descriptor_by_pointer

    using L = describe_members<X, mod_any_access>;
    using D = descriptor_by_pointer<L, &X::a>; // descriptor for X::a

### <boost/describe.hpp>

This convenience header includes all the headers previously described.

## Copyright and License

This documentation is copyright 2020, 2021 Peter Dimov and is distributed under the Boost Software License, Version 1.0.