Boost C++ 库

...世界上最受推崇和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《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 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

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 重载,用于计算带注释的结构体或类的哈希值。 它通过迭代描述的基类和成员,并在每个成员上调用 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 中,自动将带注释的结构体转换为 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 转换为带注释的结构体。

注意
自 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>

E 是描述的枚举类型时,has_describe_enumerators<E>::valuetrue,否则为 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 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 并在 modifiers 上调用 BOOST_DESCRIBE_ENUM,从而允许 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>

T 是描述的类类型时,has_describe_bases<T>::valuetrue,否则为 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>

T 是描述的类类型时,has_describe_members<T>::valuetrue,否则为 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 typedefs 来识别,并且应具有不抛出异常的 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 软件许可,1.0 版 分发。