如何静态断言指针转换是微不足道的?

发布于 2024-10-29 17:59:25 字数 907 浏览 6 评论 0原文

假设我有以下类型:

struct A {
    int a;
};

struct B {
    int b;
};

struct C : public A, public B {
    int c;
};

C* 指针可以转换为 A* 指针,而根本不需要调整实际地址。但是,当C* 转换为B* 时,该值必须更改。我想确保我拥有的两个相关类型可以在不更改地址的情况下相互转换(即没有多重继承,或者基类是派生类的第一个基类)。这可以在运行时检查,例如,这样

assert(size_t(static_cast<A*>((C*)0xF000) == 0xF000);
assert(size_t(static_cast<B*>((C*)0xF000) != 0xF000);

就可以了。但这些信息在编译时是已知的,所以我正在寻找一种方法来对其进行编译时断言。将上述内容转换为静态断言的明显方法(例如,将 assert 替换为 BOOST_STATIC_ASSERT 会给出错误“对除整型或枚举类型之外的类型的强制转换不能出现在一个常量表达式”与 g++ 4.2。

可移植性并不是太重要。使用 gcc 扩展或 hacky 模板技巧都可以。

更新:发现之前已经问过几乎相同的问题:< a href="https://stackoverflow.com/questions/1131643/c-statically-detect-base-classes-with-differing-addresses">C++,静态检测具有不同地址的基类?。 code>offsetof() 也是唯一有用的建议。

Let's say I have these types:

struct A {
    int a;
};

struct B {
    int b;
};

struct C : public A, public B {
    int c;
};

A C* pointer can be cast to A* pointer without adjusting the actual address at all. But when C* is cast to B*, the value must change. I'd like to ensure that two related types I have can be cast to each other without a change in address (i.e. that there is no multiple inheritance, or that the base class is the first base of the derived class). This could be checked at run-time, e.g. like so

assert(size_t(static_cast<A*>((C*)0xF000) == 0xF000);
assert(size_t(static_cast<B*>((C*)0xF000) != 0xF000);

That works. But this information is known at compile time, so I'm looking for a way to do a compile-time assert on it. The obvious ways of converting the above to a static assert (e.g. replace assert with BOOST_STATIC_ASSERT give the error "a cast to a type other than an integral or enumeration type cannot appear in a constant-expression" with g++ 4.2.

Portability isn't too important. Using gcc extensions, or hacky template tricks would all be fine.

Update: Found that almost the same question has been asked before: C++, statically detect base classes with differing addresses?. Using offsetof() is the only useful suggestion there too.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

燃情 2024-11-05 17:59:26

“我想确保两个相关类型可以在不更改地址的情况下相互转换(即没有多重继承,或者基类是派生类的第一个基类)。”

你的“ie”不正确。例如,即使 Base 不是多态的,Derived 的前 4 个字节也完全有可能是 vtable 指针。是的,如果 (1) 第一个基子对象位于偏移量 0 处,并且 (2) vtable 指针位于偏移量 0 处,C++ 编译器会更容易。但这两个目标本质上是不一致的,并且没有明显更好的选择。

现在,第一部分可以在理论上进行测试。对于标准布局类型,offset_of(Base,first_member)offset_of(Derived,first_member) 没有区别。但实际上,offset_of 不适用于有趣的类型;是UB。此检查的重点是检查类型,因此对于非标准布局类型,它应该可靠地失败。

"I'd like to ensure that two related types can be cast to each other without a change in address (i.e. that there is no multiple inheritance, or that the base class is the first base of the derived class)."

Your "i.e." isn't correct. For instance, it's entirely possible that the first 4 bytes of Derived are a vtable pointer, even when Base isn't polymorphic. Yes, C++ compilers are easier if (1) the first base subobject is at offset 0, and (2) the vtable pointer is at offset 0. But those two goals are inherently at odds, and there is no clear better choice.

Now, the first part could be tested in theory. With standard-layout types, there would be no difference in offset_of(Base, first_member) and offset_of(Derived, first_member). But in practice, offset_of doesn't work for interesting types; it's UB. And the whole point of this check is to check a type, so it should fail reliably for nonstandard-layout types.

巴黎盛开的樱花 2024-11-05 17:59:26

基于 MSalters 的建议和 C++ 的答案,静态检测基数具有不同地址的类?,这是我能想到的最接近答案的内容。它可能是特定于 gcc 的,并且需要了解基类的一些成员:

#pragma GCC diagnostic ignored "-Winvalid-offsetof"     // To suppress warning.
BOOST_STATIC_ASSERT(offsetof(C, a) == offsetof(A, a));
BOOST_STATIC_ASSERT(offsetof(C, b) != offsetof(B, b));
#pragma GCC diagnostic warn "-Winvalid-offsetof"

显然这既不方便又可怕(需要了解成员并关闭警告)。

Based on a suggestion from MSalters, and an answer from C++, statically detect base classes with differing addresses?, here is the closest thing to an answer I can come up with. It's probably gcc-specific, and requires knowing some member of the base class:

#pragma GCC diagnostic ignored "-Winvalid-offsetof"     // To suppress warning.
BOOST_STATIC_ASSERT(offsetof(C, a) == offsetof(A, a));
BOOST_STATIC_ASSERT(offsetof(C, b) != offsetof(B, b));
#pragma GCC diagnostic warn "-Winvalid-offsetof"

Obviously this is both inconvenient and scary (requires to know a member and to turn off a warning).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文