一、c++23的反射跳票
本来按照预定,c++23中反射就应该比较全的。结果,由于众所周知的原因,线上会议肯定是不如线下会议效率高,那么反射这种对于c++不太急切的功能(当然,也有其它原因)只能向后放一放。只有盼望着下一个版本了,下一个版本就是c++26。不过c++26能否真正上线,还得看最后的结果。
二、c++26的提案
在c++26的提案中,仍然是静态反射为主。主要有下面几个功能:
A、增加反射运算符
一元运算符主要有:
c
1、...
2、^ ::
3、^ namespace-name
4、^ type-id
5、^ cast-expression
需要注意的,提案中只是对一些初级的表达式进行了处理,如果超出范围则反射无法完成。
B、增加拼接器(Splicers)
拼接器是[:...:],其主要的功能如下:
c
1、[: r :] produces an expression evaluating to the entity or constant value represented by r.
2、typename[: r :] produces a simple-type-specifier corresponding to the type represented by r.
3、template[: r :] produces a template-name corresponding to the template represented by r.
4、namespace[: r :] produces a namespace-name corresponding to the namespace represented by r.
5、[:r:]:: produces a nested-name-specifier corresponding to the namespace, enumeration type, or class type represented by r.
Attempting to splice a reflection value that does not meet the requirement of the splice (including "invalid reflections") is ill-formed. For example:
typename[: ^:: :] x = 0; // Error.
拼接器还有另外一种,即Range拼接器,它主要用来处理元组等的反射。
C、增加了元信息空间
在这个功能里提供了一个命名空间std::meta::info,它主要包括以下的内容:
c
1、an error (corresponding to an "invalid reflection")
2、any (C++) type and type-alias
3、any function or member function
4、any variable, static data member, or structured binding
5、any non-static data member
6、any constant value
7、any template
8、any namespace
当然,这里面也有不少的限制,这里就不展开,毕竟不是正式的标准。
D、增加元功能(Metafunctions)
主要包括:
c
1、invalid_reflection, is_invalid, diagnose_error
2、name_of, display_name_of, source_location_of
3、type_of, parent_of, entity_of
4、template_of, template_arguments_of
5、members_of, nonstatic_data_members_of, bases_of, enumerators_of, subobjects_of
6、substitute
7、entity_ref<T>, value_of<T>, ptr_to_member<T>
8、test_type<Pred>
9、 Other Singular Reflection Predicates
10、reflect_value
11、nsdm_description, synth_struct, synth_union
12、Data Layout Reflection
这里面的细节不是太好说清楚,大家可以去看相关的提案:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2996r0.html#ref-P2670R1
省得误导大家。
三、相关的例程
1、类型和值之间的反射转换
c
constexpr auto r = ^int;
typename[:r:] x = 42; // Same as: int x = 42;
typename[:^char:] c = '*'; // Same as: char c = '*';
2、成员反射
c
struct S { unsigned i:2, j:6; };
consteval auto member_number(int n) {
if (n == 0) return ^S::i;
else if (n == 1) return ^S::j;
else return std::meta::invalid_reflection("Only field numbers 0 and 1 permitted");
}
int main() {
S s{0, 0};
s.[:member_number(1):] = 42; // Same as: s.j = 42;
s.[:member_number(5):] = 0; // Error (likely with "Only field numbers 0 and 1 permitted" in text).
}
3、类型和大小的列表反射
c
//sizes将是一个std::array<std::size_t, 3>初始化为{sizeof(int), sizeof(float), sizeof(double)}
constexpr std::array types = {^int, ^float, ^double};
constexpr std::array sizes = []{
std::array<std::size_t, types.size()> r;
std::ranges::transform(types, r.begin(), std::meta::size_of);
return r;
}();
//产生相同的数组sizes
template<class...> struct list {};
using types = list<int, float, double>;
constexpr auto sizes = []<template<class...> class L, class... T>(L<T...>) {
return std::array<std::size_t, sizeof...(T)>{{ sizeof(T)... }};
}(types{});
4、执行make_integer_sequence
c
#include <utility>
#include <vector>
template<typename T>
consteval std::meta::info make_integer_seq_refl(T N) {
std::vector args{^T};
for (T k = 0; k < N; ++k) {
args.push_back(std::meta::reflect_value(k));
}
return substitute(^std::integer_sequence, args);
}
template<typename T, T N>
using make_integer_sequence = [:make_integer_seq_refl<T>(N):];
5、反射枚举
c
//正向
template <typename E>
requires std::is_enum_v<E>
constexpr std::string enum_to_string(E value) {
template for (constexpr auto e : std::meta::members_of(^E)) {
if (value == [:e:]) {
return std::string(std::meta::name_of(e));
}
}
return "<unnamed>";
}
enum Color { red, green, blue };
static_assert(enum_to_string(Color::red) == "red");
static_assert(enum_to_string(Color(42)) == "<unnamed>");
//反向
template <typename E>
requires std::is_enum_v<E>
constexpr std::optional<E> string_to_enum(std::string_view name) {
template for (constexpr auto e : std::meta::members_of(^E)) {
if (name == std::meta::name_of(e)) {
return [:e:];
}
}
return std::nullopt;
}
6、结构体数组的反射
c
struct point {
float x;
float y;
float z;
};
using points = struct_of_arrays<point, 30>;
// equivalent to:
// struct points {
// std::array<float, 30> x;
// std::array<float, 30> y;
// std::array<float, 30> z;
// };
其它还有几种,在这里不再一一列举。
四、总结
看了c++26中的提案,可以发现其实很多现在的反射框架的轮子基本都可以废弃了。不过,能把反射最终支持,估计得2028年左右了,时间还有的是。喜欢造轮子的同学可以继续造,反正在造轮子的过程中,可以更好的学习反射的原理和实践,同样能更好的理解新标准中的反射。这样,在未来的应用中,也会更加轻松的引入标准库内的反射。
在后面反射实现分析讲解中,基本也会沿着大致相同的路线进行。