概述
此库使自定义类型(枚举、结构体和类)的作者能够描述其枚举器、基类、数据成员和成员函数。此信息随后可以被其他代码部分(可能是由不同的作者编写的)查询,使用提供的原语 describe_enumerators
、describe_bases
和 describe_members
。
要了解如何描述枚举类型,请参阅 描述枚举类型。
要了解如何描述类类型(包括结构体),请参阅 描述类类型。
有关此功能如何有用的示例,请参阅 用法示例。
该库的目的是建立一种提供这些反射能力的标准方法。许多现有库提供了自己描述枚举或类的方法,但如果没有标准,不同人编写的代码就无法互操作。
最终,人们可能希望这些原语最终出现在 C++ 标准中,编译器自动提供描述类型所需的元数据,从而使手动宏注释变得不必要。
修订历史
Boost 1.84.0 版本的变更
-
添加了
enum_from_string
的重载,该重载接受字符串视图,以避免需要空终止。
Boost 1.81.0 版本的变更
-
为了允许在使用
-pedantic
选项时包含enumerators.hpp
、bases.hpp
和members.hpp
,BOOST_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_enumerators
、has_describe_bases
、has_describe_members
。 -
添加了
enum_to_string
、enum_from_string
。 -
添加了关系运算符和流插入运算符。
-
添加了
descriptor_by_name
、descriptor_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: int
或 enum class E4: unsigned char
,请分别使用 BOOST_DEFINE_FIXED_ENUM
和 BOOST_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;
};
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_public
、mod_protected
、mod_private
或它们的按位或组合。 mod_public
的存在会将公共基类包含在结果中,其缺失会排除它们。 其他两个修饰符的工作方式类似。
describe_members
采用以下可能的修饰符的按位或组合:mod_public
、mod_protected
、mod_private
、mod_static
、mod_function
、mod_any_member
、mod_inherited
、mod_hidden
。
访问修饰符的工作方式与 describe_bases
相同。
(对于使用 BOOST_DESCRIBE_STRUCT
注释的类型,受保护和私有成员列表将为空。)
当 mod_static
存在时,返回静态成员,否则返回非静态成员。
当 mod_function
存在时,返回成员函数,否则返回数据成员。
当 mod_any_member
存在时,mod_static
和 mod_function
将被忽略,并且返回所有成员,无论类型如何。
当 mod_inherited
存在时,也会返回基类的成员。
当 mod_hidden
存在时,会包含隐藏的继承成员。 当派生类具有同名成员时,基类的成员将被隐藏。
对于上面的类 Y
,describe_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_STRUCT
或 BOOST_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_STRUCT
或 BOOST_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 Actions 和 Appveyor 上测试。
局限性
此实现具有以下局限性
-
在枚举器、基类和成员列表中最多支持 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>::value
为 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 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_static
和mod_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_public
、mod_protected
和 mod_private
的按位或组合,并充当过滤器。
describe_bases<T, M>
返回 L<D1, D2, …, Dn>
,其中 L
是以下形式的类模板
template<class...> struct L {};
而 Di
是以下形式的基类描述符
struct Di
{
using type = /*...*/;
static constexpr unsigned modifiers = /*...*/;
};
其中 type
是基类的类型,modifiers
是 mod_public
、mod_protected
、mod_private
和 mod_virtual
的按位或组合,它反映了基类的属性。
如果 T
不是描述的类类型,则 describe_bases<T, M>
会导致替换失败。
has_describe_bases<T>
当 T
是描述的类类型时,has_describe_bases<T>::value
为 true
,否则为 false
。
由于该库未提供单独描述基类和成员的方法,因此实际上 has_describe_bases
和 has_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_public
、mod_protected
、mod_private
、mod_static
、mod_function
、mod_any_member
、mod_inherited
和 mod_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
是成员的名称,modifiers
是 mod_public
、mod_protected
、mod_private
、mod_static
、mod_function
、mod_inherited
和 mod_hidden
的按位或组合,它反映了成员的属性。
如果 T
不是描述的类类型,则 describe_members<T, M>
会导致替换失败。
has_describe_members<T>
当 T
是描述的类类型时,has_describe_members<T>::value
为 true
,否则为 false
。
由于该库未提供单独描述基类和成员的方法,因此实际上 has_describe_bases
和 has_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
并返回 true
。 E
必须是描述的枚举类型。 如果 name
与描述的值之一不对应,则函数返回 false
。
仅当 S
是类似字符串的类型(例如 std::string
或 std::string_view
)时,第二个重载才会参与重载解析。 类似字符串的类型通过嵌套的 value_type
和 traits_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
返回的列表也将起作用。
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
中搜索成员指针 Pm
。 L
应是由 describe_members
返回的列表。
由于 auto
模板参数是 C++17 功能,因此使用 descriptor_by_pointer
需要 C++17。
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 版 分发。