通过C++26的反射,将C++ Lambda的捕获映射到POD结构体(P2996)
我正在研究使用C++26的反射(P2996)将带状态的lambda的捕获列表反射到一个可命名的POD结构体中。我的目标是“滥用”编译器通常将lambda实现为无名类这一点。相关代码已放在下方,并且也可在Compiler Explorer上查看:这里。
我在两大实现的反射实现之间看到的结果差异很大:
- Clang(p2996分支):正确镜像了捕获。输出是
Success!。看起来Clang将捕获视为标准可反射的数据成员。 - GCC(trunk):输出是
Reflection failed: size mismatch (1 vs 16)。似乎nonstatic_data_members_of为闭包类型返回了一个空的范围,导致得到一个空的1 字节结构体。
GCC是否以一种根本性的方式实现lambda捕获,使得反射引擎根本无法将其视为数据成员?P2996的初衷是最终支持对lambda捕获列表的反射,还是目前的这一行为属于实现定义/不可接受的?我是不是完全误解了什么?
#include <bit>
#include <iostream>
#include <meta>
#include <vector>
template<typename M, typename Lambda>
void try_mirror(Lambda&& l) {
if constexpr (sizeof(M) == sizeof(Lambda)) {
// This is taken in Clang (expected)
auto m = std::bit_cast<M>(l);
std::cout << "Success!\n";
} else {
// This is taken on GCC (why?)
std::cout << "Reflection failed: size mismatch ("
<< sizeof(M) << " vs " << sizeof(Lambda) << ")\n";
}
}
int main() {
int id = 101;
double val = 99.9;
auto lambda = [id, val](int x) { return id + x + (int)val; };
using L = decltype(lambda);
struct Mirror; // Want to send captured members here
consteval {
using namespace std::meta;
constexpr auto ctx = access_context::unchecked();
std::vector<info> specs;
// Was expecting to see all captured members here
template for (constexpr auto c : std::define_static_array(
nonstatic_data_members_of(^^L, ctx)))
{
specs.push_back(data_member_spec(type_of(c), {}));
}
define_aggregate(^^Mirror, specs);
}
try_mirror<Mirror>(lambda);
return 0;
}
为什么我要这样做
我们正在致力于在HPX中实现对lambda序列化的自动化。我们最近为使用反射序列化的一般类添加了支持。如果一个lambda的捕获列表能够镜像成一个POD结构体,我们现有的序列化基础设施就能够处理它。
解决方案
此行为当前是实现定义的。
根据 [meta.reflection.member.queries],nonstatic_data_members_of(r, ctx)(其中 r 是对类或命名空间Q 的反射)返回对那些属于 Q-members-of-representable 的成员M 的反射。为了让一个成员成为 Q-members-of-representable,该成员必须具备一个 Q-members-of-eligible 声明。
Q-members-of-eligible 的要求包括(其中之一):
如果Q 是闭包类型,则M 必须是函数调用运算符或函数调用运算符模板。
对一个闭包类型Q 的其他成员是否为Q-members-of-eligible,属于实现定义。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。