9

我知道添加static成员函数很好,但是enum定义呢?没有新的数据成员,只是它的定义。


一点背景:

我需要添加一个static成员函数(在类中),它将IP通过字符串表示来识别(函数)地址的版本。我想到的第一件事是声明一个enumfor IPv4IPv6并为我的函数Unknown制作这个enum返回码。

但我不想破坏二进制向后兼容性。

还有一个非常糟糕的问题(对于 SO) - 这里有任何来源或问题,我可以阅读更多相关信息吗?我的意思是 - 什么破坏了二进制兼容性,什么 - 没有。或者它取决于很多东西(如架构、操作系统、编译器..)?


编辑:关于@PeteKirkham 的评论:好吧,至少 - 有没有办法测试/检查更改的ABI,或者最好发布关于此的新问题?

EDIT2:我刚刚发现了一个SO 问题:静态分析工具来检测 C++ 中的 ABI 中断 。我认为它在这里有某种关联,并回答了有关检查二进制兼容性的工具的部分。这就是为什么我在这里把它联系起来。

4

2 回答 2

6

这里真正的问题显然是为什么要让它成为类(静态)成员?

从定义中可以明显看出,这完全可以是它自己的命名空间(也可能是头文件)中的自由函数,或者如果使用被隔离定义在源文件中的匿名命名空间中。

尽管这仍然可能破坏 ABI,但确实需要一个有趣的编译器才能做到这一点。

至于 ABI 破损:

  • 修改类的大小:添加数据成员,除非您设法将它们存储到以前未使用的填充中(当然,编译器特定)
  • 修改类的对齐方式:更改数据成员,有一些技巧可以人为地增加对齐方式(联合),但缩小它需要编译器特定的编译指示或属性以及兼容的硬件
  • 修改 vtable 的布局:添加虚拟方法可能会更改 vtable 中先前虚拟方法的偏移量。对于 gcc,vtable 是按照声明的顺序布局的,因此在最后添加虚拟方法是可行的……但是它在基类中不起作用,因为 vtable 布局可能与派生类共享。最好考虑冷冻
  • 修改函数的签名:符号的名称通常取决于函数本身的名称及其参数的类型(加上方法的类名称和方法的限定符)。您可以在参数上添加顶级const,无论如何它都会被忽略,并且您通常可以更改返回类型(但这可能会带来其他问题)。请注意,添加具有默认值的参数确实会破坏 ABI,就签名而言,默认值将被忽略。最好考虑冷冻
  • 删除以前导出符号的任何函数或类(即,具有直接或继承虚方法的类)

我可能已经忘记了一两点,但这应该让你已经有一段时间了。

ABI 的示例:Itanium ABI

于 2011-11-18T15:04:35.847 回答
5

形式上...如果您链接针对您的类的两个不同版本编译的文件,则您违反了一个定义规则,这是未定义的行为。实际上......唯一破坏二进制兼容性的事情是添加数据成员或虚拟函数(非虚拟函数很好),或更改函数的名称或签名,或任何涉及基类的东西。这似乎是通用的——我不知道规则不同的编译器。

于 2011-11-18T15:12:58.810 回答