Boost C++ 库

...世界上最受推崇和设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

描述:一个 C++14 反射库 - Boost C++ 函数库

概述

此库使用户自定义类型(枚举、结构体和类)的作者能够描述其枚举数、基类、数据成员和成员函数。其他代码部分(可能由其他作者编写)可以使用提供的原语 describe_enumeratorsdescribe_basesdescribe_members 来查询这些信息。

要了解如何描述枚举类型,请参阅 描述枚举类型

要了解如何描述类类型(包括结构体),请参阅 描述类类型

有关此功能有用的示例,请参阅 用法示例

该库的目的是建立一种提供这些反射能力的标准化方法。许多现有的库都提供描述枚举或类的方法,但如果没有标准,不同人编写的代码将无法互操作。

最终,人们或许希望这些原语能够被纳入 C++ 标准,由编译器自动提供描述类型所需的元数据,从而不再需要手动宏注解。

修订历史

Boost 1.84.0 中的更改

  • 添加了 enum_from_string 的一个重载,该重载接受一个字符串视图,以避免空终止的需要。

Boost 1.81.0 中的更改

  • 为了在使用了 -pedantic 选项时能够包含 enumerators.hppbases.hppmembers.hppBOOST_DESCRIBE_ENUM 的调用已从 modifiers.hpp 移至单独的头文件 modifier_description.hpp。因此,modifiers.hpp 不再包含 enum.hpp。依赖于此隐式包含的代码可能会失败,需要进行修复以包含 enum.hpp

Boost 1.79.0 中的更改

  • BOOST_DESCRIBE_STRUCT 中启用了联合体,并更新了示例以检查 std::is_union<T>

  • 添加了定义 fmtlib 类格式化器的示例。

  • 添加了定义 fmtlib 枚举格式化器的示例。

  • 添加了打印指向成员的指针的示例。

Boost 1.78.0 中的更改

  • 添加了 has_describe_enumeratorshas_describe_baseshas_describe_members

  • 添加了 enum_to_stringenum_from_string

  • 添加了关系运算符和流插入运算符。

  • 添加了 descriptor_by_namedescriptor_by_pointer

  • 添加了 struct_to_tuple 示例。

描述枚举类型

如果您有一个枚举类型

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

您可以通过 BOOST_DESCRIBE_ENUM 宏为其添加反射元数据

BOOST_DESCRIBE_ENUM(E, v1, v2, v3)

该宏定义在 <boost/describe/enum.hpp> 中,应放置在与 enum 相同的命名空间中。

如果您的枚举值没有初始化器,而不是重复它们

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

您可以使用便捷宏

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

它展开为前面的两行。

要定义 enum class E2,请使用 BOOST_DEFINE_ENUM_CLASS。要添加底层类型,即 enum E3: intenum class E4: unsigned char,请分别使用 BOOST_DEFINE_FIXED_ENUMBOOST_DEFINE_FIXED_ENUM_CLASS

如果您的枚举类型嵌套在类或 struct 中,请在 enum 旁边使用 BOOST_DESCRIBE_NESTED_ENUM 宏,如下所示

class X
{
private:

    enum class E
    {
        v1,
        v2
    };

    BOOST_DESCRIBE_NESTED_ENUM(E, v1, v2)

public:

    // ...
};

一旦枚举类型 E 被注解,就可以使用 describe_enumerators<E> 来获取一个描述符列表。(describe_enumerators 定义在 boost::describe 命名空间中,位于 <boost/describe/enumerators.hpp>。)

描述符列表是一种形式为 L<D1, D2, …​, Dn> 的类型,其中 L 的形式为 template<class…​ T> struct L {};,而 Di 的形式为

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

迭代描述符列表,您可以使用 Mp11 中的 mp_for_each Mp11

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

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

});

描述类类型

具有公共成员的类类型

如果您有一个 struct

struct X
{
    int m1;
    int m2;
};

请使用 BOOST_DESCRIBE_STRUCT 宏来描述它

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

BOOST_DESCRIBE_STRUCT 定义在 <boost/describe/class.hpp> 中,应放置在与 struct 相同的命名空间中。

它接受三个参数:struct 的名称、基类列表(示例中为空)以及按名称列出的(公共)成员列表(包括数据成员和成员函数)。

由于 BOOST_DESCRIBE_STRUCT 放置在类型之外,它是非侵入性的,不需要访问定义,因此可以用来描述第三方类型或系统头文件中定义的类型。

具有保护或私有成员的类类型

要描述类类型,请改用 BOOST_DESCRIBE_CLASS 宏,将其放置在类内部。这使得宏可以访问保护和私有成员,但它是侵入性的,需要访问定义。

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

它接受三个成员列表,分别用于公共、保护和私有成员。

检索类属性

一旦类型 T 被注解,就可以通过 describe_bases<T, M>describe_members<T, M>M 是修饰符的位掩码,如 mod_public | mod_static | mod_function)来检索其属性。

这些原语分别定义在 boost::describe 命名空间中,位于 <boost/describe/bases.hpp><boost/describe/members.hpp> 头文件中。

describe_bases 接受以下可能的修饰符:mod_publicmod_protectedmod_private,或它们的按位或组合。mod_public 的存在会包含结果中的公共基类,其不存在则排除它们。另外两个修饰符也类似工作。

describe_members 接受以下可能的修饰符的按位或组合:mod_publicmod_protectedmod_privatemod_staticmod_functionmod_any_membermod_inheritedmod_hidden

访问修饰符与 describe_bases 中的工作方式相同。

(对于用 BOOST_DESCRIBE_STRUCT 注解的类型,保护和私有成员列表将为空。)

当存在 mod_static 时,返回静态成员,否则返回非静态成员。

当存在 mod_function 时,返回成员函数,否则返回数据成员。

当存在 mod_any_member 时,mod_staticmod_function 被忽略,并且无论种类如何都返回所有成员。

当存在 mod_inherited 时,也返回基类的成员。

当存在 mod_hidden 时,包含隐藏的继承成员。基类的成员被隐藏当派生类具有同名成员时。

对于上面的类 Ydescribe_bases<Y, mod_any_access> 将返回一个类型列表 L<D1>,其中包含一个描述 X 的单个基描述符 D1

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

describe_members<Y, mod_private> 将返回一个类型列表 L<D2>,其中包含数据成员 Y::m5 的描述符。

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

有关如何使用基类和数据成员描述符的示例,请参阅 定义通用打印函数

有关如何使用成员函数描述符的示例,请参阅 自动 JSON RPC

重载的成员函数

要描述重载的成员函数,您需要采用更复杂的语法,因为仅仅列出其名称(例如 f)将使库尝试形成一个成员指针 &X::f,这会失败,因为它不清楚这个表达式指的是哪个 f

为了消除歧义,在函数名称前加上函数类型,放在括号内,如下例所示

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

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

函数类型与声明相同,不包括名称。

请务必保留括号中的函数类型与其名称之间的空格,因为省略它在 GCC 和 Clang 上可以编译通过,但在 MSVC 上由于其非标准预处理器会导致难以理解的错误。

注意括号的正确放置,因为在此处出错也会导致所有编译器上难以理解的编译器错误。

同样的技术也适用于 BOOST_DESCRIBE_CLASS 和静态成员函数。

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), (), ())
};

目前不支持成员函数和静态成员函数同名且函数类型相同的情况。

使用示例

使用编译时循环打印枚举器

一个简单的示例,仅使用 mp11::mp_for_each 迭代枚举器描述符并打印它们。

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

    });
}

示例输出

v1: 11
v2: 12
v3: 5

使用运行时循环打印枚举器

这与前面的示例类似,但它首先构建一个包含值的 std::array,然后使用普通的 for 循环而不是 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 );
    }
}

示例输出

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

enum_to_string

此示例显示了一个函数,给定一个枚举值,它会返回其名称。如果该值不对应于命名值,则函数返回 "(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;
}

示例输出

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

自 1.78.0 版本起,该库提供了 enum_to_string。它与示例中的不同之处在于有一个第二个参数,用于确定当值不对应于命名枚举值时返回什么。

string_to_enum

与上一个示例相反;当给定枚举名时返回枚举值。当传递的字符串不对应于任何枚举名时,抛出异常。

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

示例输出

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

自 1.78.0 版本起,该库提供了 enum_from_string。它与示例中的函数不同之处在于,它通过返回一个 bool 值来指示失败,而不是使用异常。枚举值被赋给输出参数。

定义通用打印函数

此示例定义了一个通用的 operator<<,它可以作用于使用 BOOST_DESCRIBE_STRUCTBOOST_DESCRIBE_CLASS 描述过的任何类或结构体类型。

它首先递归打印基类,然后打印所有成员。

(使用 C 语言的强制转换来访问私有基类。这不像初看起来那么糟糕,因为我们只通过打印其成员来检查基类,这样做不应改变其状态,因此不会违反其不变性。)

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

示例输出

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

自 1.78.0 版本起,此通用 operator<< 由库在 boost::describe::operators 命名空间中提供。它通过在包含所描述应用程序类型的命名空间中进行 using 声明来启用,如下例所示

namespace app
{

struct X
{
    int a = 1;
};

BOOST_DESCRIBE_STRUCT(X, (), (a))

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

}

实现 hash_value

此示例定义了一个通用的 hash_value 重载,它计算已注解的 struct 或类的哈希值。它通过迭代已描述的基类和成员,并对每个成员调用 boost::hash_combine 来实现。

该重载定义在 app 命名空间中,以便也应用于定义在 app 中的所有已注解类。

注意
自 1.81.0 版本起,Boost.ContainerHash 提供了自己的内置支持,用于描述的类,通过一个非常类似于下面的 boost::hash_value 重载。因此,当使用 Boost 1.81.0 或更高版本时,您不需要下面的 hash_value 函数,因为它会导致歧义。
#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;
}

示例输出

12526671134390370097

实现 operator==

此示例定义了一个通用的 operator== 重载,它迭代已描述的基类和成员,并使用 == 对它们进行比较。

该重载定义在 app 命名空间中,以便也应用于定义在 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;
}

示例输出

true false

自 1.78.0 版本起,此通用 operator== 由库在 boost::describe::operators 命名空间中提供。它通过在包含所描述应用程序类型的命名空间中进行 using 声明来启用,如下例所示

namespace app
{

struct X
{
    int a = 1;
};

BOOST_DESCRIBE_STRUCT(X, (), (a))

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

}

其余的关系运算符也已提供,并且可以类似地启用。

struct_to_tuple

此示例定义了一个函数 struct_to_tuple,该函数接受一个已描述的类类型作为参数,并返回一个包含其所有公共成员的元组。

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

示例输出

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

自动转换为 JSON

此示例定义了一个通用的 tag_invoke 重载,它通过迭代已描述的公共成员并将它们添加到返回的 boost::json::object 中,自动将已注解的 struct 转换为 Boost.JSON 值。

该重载定义在 app 命名空间中,以便也应用于定义在 app 中的所有已注解类。

私有成员的存在被视为通用转换不合适的指示,因此使用 std::enable_if_t 在这种情况下禁用了此重载。

注意
自 1.81.0 版本起,Boost.JSON 提供了自己的内置支持,用于描述的类。因此,当使用 Boost 1.81.0 或更高版本时,您不需要下面的 tag_invoke 函数。
#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;
}

示例输出

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

从 JSON 自动转换

与上一个示例类似,但方向相反。定义了一个 tag_invoke 重载,将 boost::json::value 转换为已注解的 struct。

注意
自 1.81.0 版本起,Boost.JSON 提供了自己的内置支持,用于描述的类。因此,当使用 Boost 1.81.0 或更高版本时,您不需要下面的 tag_invoke 函数。
#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;
}

示例输出

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

自动序列化

此示例定义了一个通用的 serialize 函数,该函数自动为已注解的类添加 Boost.Serialization 支持。

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

示例输出

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

自动 JSON RPC

此示例定义了一个通用的 call 函数,该函数可用于按名称调用成员函数,并将参数放入 Boost.JSON 数组中。结果以 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;
}

示例输出

"Hello, world!"
3

交互式变量控制台

此示例实现了一个交互式控制台,允许打印和修改变量。它使用 Boost.JSON 将变量转换为字符串形式以及从字符串形式转换回来。

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

示例输出

> 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 类格式化器

此示例定义了一个通用的 fmtlib 格式化器,它可以作用于使用 BOOST_DESCRIBE_STRUCTBOOST_DESCRIBE_CLASS 描述过的任何类或结构体类型。它类似于 上面显示的通用打印函数

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

示例输出

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

fmtlib 枚举格式化器

此示例定义了一个针对已描述枚举的 fmtlib 格式化器。

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

示例输出

____v1____
____7_____

打印指向成员的指针

此示例为指向成员的指针定义了一个 operator<< 重载。

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

示例输出

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

实现特性

依赖项

支持的编译器

  • GCC 5 或更高版本,使用 -std=c++14 或更高版本

  • Clang 3.9 或更高版本,使用 -std=c++14 或更高版本

  • Visual Studio 2015 或更高版本

Github ActionsAppveyor 上进行了测试。

限制

此实现存在以下限制

  • 在枚举数、基类和成员列表中最多支持 52 个元素。

  • 当被描述的类是 final 的时,无法区分保护基类和私有基类,并将其视为私有。

  • 不支持位域。无法形成指向位域的成员指针。

  • 不支持引用成员。无法形成指向引用的成员指针。

  • 不支持匿名联合体。

参考

<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) 应放置在定义枚举类型 E 的命名空间中,并创建 describe_enumerators<E> 工作所需的元数据。

使用此宏后,describe_enumerators<E> 返回 L<D1, D2, …​, Dn>,其中 L 是一个类模板,形式如下

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

Di 是一个枚举描述符,形式如下

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

其中 vi 是传递给宏的相应标识符。

BOOST_DESCRIBE_NESTED_ENUM

BOOST_DESCRIBE_NESTED_ENUM(E, v1, v2, …​, vN) 类似于 BOOST_DESCRIBE_ENUM,用于注解嵌套在类(或 struct)类型中的枚举类型。它应放置在定义 enum 的类类型中。

BOOST_DEFINE_ENUM

BOOST_DEFINE_ENUM(E, v1, v2, …​, vN) 是一个便捷宏,展开为

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

BOOST_DEFINE_ENUM_CLASS

BOOST_DEFINE_ENUM_CLASS(E, v1, v2, …​, vN) 是一个便捷宏,展开为

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) 是一个便捷宏,展开为

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) 是一个便捷宏,展开为

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> 返回 L<D1, D2, …​, Dn>,其中 L 是一个类模板,形式如下

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

Di 是一个枚举描述符,形式如下

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

其中 vi 是第 i 个枚举数。

如果 E 不是已描述的枚举类型,describe_enumerators<E> 会导致替换失败。

has_describe_enumerators<E>

has_describe_enumerators<E>::valueE 是已描述的枚举类型时为 true,否则为 false

<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 应放置在被描述的 struct 类型相同的命名空间中,并接受三个参数:struct 类型的名称、用括号括起来的基类列表,以及用括号括起来的公共成员列表。

示例

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 应放置在被描述类型的类定义内部,并接受五个参数:类的名称、基类列表、公共成员列表、保护成员列表和私有成员列表。

示例

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

枚举类型 modifiers 是一个位掩码类型,包含以下标志

  • mod_public - 包含描述符列表中的公共基类或成员

  • mod_protected - 包含保护基类或成员

  • mod_private - 包含私有基类或成员

  • mod_virtual - 当基类是虚基类时返回

  • mod_static - 返回静态成员(不提供时,返回非静态成员)

  • mod_function - 返回成员函数(不提供时,返回数据成员)

  • mod_any_member - 覆盖 mod_staticmod_function,并返回所有成员,无论种类如何

  • mod_inherited - 包含基类的成员

  • mod_hidden - 包含隐藏的继承成员

<boost/describe/​modifier_description.hpp>

头文件 modifier_description.hpp 包含 modifiers.hpp 并调用 BOOST_DESCRIBE_ENUM 来描述 modifiers,从而允许 describe_enumerators<modifiers> 工作。

<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 必须是 mod_publicmod_protectedmod_private 的按位或组合,并充当过滤器。

describe_bases<T, M> 返回 L<D1, D2, …​, Dn>,其中 L 是一个类模板,形式如下

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

Di 是一个基描述符,形式如下

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

其中 type 是基类的类型,而 modifiersmod_publicmod_protectedmod_privatemod_virtual 的按位或组合,反映了基类的属性。

如果 T 不是已描述的类类型,describe_bases<T, M> 会导致替换失败。

has_describe_bases<T>

has_describe_bases<T>::valueT 是已描述的类类型时为 true,否则为 false

由于库不提供单独描述基类和成员的方法,has_describe_baseshas_describe_members 在实践中是同义词。它们是分开提供的,以保持一致性。

<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 必须是 mod_publicmod_protectedmod_privatemod_staticmod_functionmod_any_membermod_inheritedmod_hidden 的按位或组合,并充当过滤器。

describe_members<T, M> 返回 L<D1, D2, …​, Dn>,其中 L 是一个类模板,形式如下

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

Di 是一个成员描述符,形式如下

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

其中 pointer 是指向成员的指针(对于非静态成员)或指针(对于静态成员),用于标识类成员;name 是成员的名称;而 modifiersmod_publicmod_protectedmod_privatemod_staticmod_functionmod_inheritedmod_hidden 的按位或组合,反映了成员的属性。

如果 T 不是已描述的类类型,describe_members<T, M> 会导致替换失败。

has_describe_members<T>

has_describe_members<T>::valueT 是已描述的类类型时为 true,否则为 false

由于库不提供单独描述基类和成员的方法,has_describe_baseshas_describe_members 在实践中是同义词。它们是分开提供的,以保持一致性。

<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

函数 enum_to_string 返回枚举值 e 的名称。E 必须是已描述的枚举类型。如果 e 不对应于描述值之一,则函数返回 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

函数 enum_from_string 将对应于 name 的枚举值赋给 e 并返回 trueE 必须是已描述的枚举类型。如果 name 不对应于描述值之一,则函数返回 false

第二个重载仅在 S 是字符串类类型(如 std::stringstd::string_view)时才参与重载解析。字符串类类型通过存在嵌套的 value_typetraits_type typedef 来识别,并期望有一个不抛出异常的 operator==,该运算符接受 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 );

} } }

头文件 <boost/describe/operators.hpp> 定义了已描述类类型的通用运算符。它们通过在包含已描述类型的命名空间中进行 using 声明来使用,如下例所示

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

如果所有基类和成员都相等,则返回 true,否则返回 false

operator!=

返回 operator== 的否定。

operator<

使用 operator< 对基类和成员按顺序进行字典序比较,并返回结果。

operator>

返回反转参数的 operator< 的结果。

operator<=

返回 operator> 的否定结果。

operator>=

返回 operator< 的否定结果。

operator<<

通过递归使用 operator<< 输出所有基类,然后输出所有成员,将 t 的表示形式输出到 os

<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

BOOST_DESCRIBE_MAKE_NAME 创建一个标识给定名称的类型。它应如下使用

using N = BOOST_DESCRIBE_MAKE_NAME(some_member);

descriptor_by_name

descriptor_by_name<L, N> 在描述符列表 L 中搜索名称由 N 标识的成员。N 应为 BOOST_DESCRIBE_MAKE_NAME 创建的类型,如上例所示。L 意图是 describe_members 返回的列表,尽管由于枚举描述符也有 ::name,返回 describe_enumerators 的列表也可以工作。

代码示例 1. 使用 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> 在描述符列表 L 中搜索成员指针 PmL 应为 describe_members 返回的列表。

由于 auto 模板参数是 C++17 的特性,使用 descriptor_by_pointer 需要 C++17。

代码示例 2. 使用 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>

这个便捷头文件包含了前面描述的所有头文件。

此文档的版权为 2020、2021 Peter Dimov,并根据 Boost Software License, Version 1.0 分发。