在某些情况下,我们使用标签来区分函数。标签通常是一个空结构:
struct Tag { };
Run Code Online (Sandbox Code Playgroud)
假设我有一个使用此标签的函数:
void func(Tag, int a);
Run Code Online (Sandbox Code Playgroud)
现在,我们调用这个函数:
func(Tag(), 42);
Run Code Online (Sandbox Code Playgroud)
并检查生成的 x86-64 反汇编,godbolt:
mov edi, 42
jmp func(Tag, int) # TAILCALL
Run Code Online (Sandbox Code Playgroud)
没关系,标签得到了完全优化:没有为其分配寄存器/堆栈空间。
但是,如果我查看其他平台,就会发现该标签存在一些。
在 ARM 上,r0用作标签,并且它被归零(似乎没有必要):
mov r1, #42
mov r0, #0
b func(Tag, int)
Run Code Online (Sandbox Code Playgroud)
对于 MSVC,ecx被用作标记,并且它是从堆栈中“初始化”的(同样,似乎没有必要):
movzx ecx, BYTE PTR $T1[rsp]
mov edx, 42 ; 0000002aH
jmp void func(Tag,int) ; func
Run Code Online (Sandbox Code Playgroud)
我的问题是:是否有一种标签技术在所有这些平台上都得到了同样的优化?
注意:我没有找到 SysV ABI 指定可以在参数传递时优化空类的地方...(甚至,Itanium C++ ABI说:“空类的传递与普通类没有什么不同”。)
Jason Turner在他的演讲中提议打破 C++ ABI 以保持该语言的发展。他还提到,如果出于兼容性原因需要,可以通过将 C++ 库包装到 C 库中来隔离 C++ ABI 更改。
27:30相关截图:
这里“BinaryLibrary”和“Old C++ stdlib”使用旧的 ABI,“NewExecutable”使用假设的更新 ABI。
据我了解,这是有效的,因为“BinaryLibrary”的旧 C++ ABI 被烘焙成具有更稳定接口的单独二进制文件。
但是什么让 C 成为一个好的替代方案呢?难道它的ABI也不能改变吗?
我将 GUI 代码中的问题缩小到SetWindowTextW(HWND, wchar_t *)如果新窗口标题未与两个字节对齐则默默失败。在本例中,SetWindowText()返回1( success ) 但不设置新文本。
MSVC 上的自然对齐wchar_t是 2 个字节,所以这绝对是我的错误。但为了确保我尝试找到 Win32 字符串的对齐规则。
我没有找到官方文档,只是一个旧的新闻组帖子提到了 Open Watcom 编译器\xc2\xa0\xe2\x80\x93的错误报告,该报告声称Windows NT 上的 Win32 和 COM 实际上需要4 字节对齐!虽然这对我来说似乎很奇怪,但我注意到 MSVC 确实将每个wchar_t文字对齐到四个字节,而不是两个。实际上,您可以wchar_t通过 使 MSVC 更密集地打包常量字符串alignas(2)。Win32 中的堆粒度也 >=8 字节。
如果 Win32 需要对宽字符字符串进行四字节对齐(如源声明),并且 API 调用在错误的数据对齐上默默失败(如SetWindowText()1 字节对齐),我感觉遇到了很大的麻烦。
有没有官方文档说明 Win32/COM 中宽字符串的明确对齐要求?是两个字节还是四个字节?
\n重现问题的代码:
\n#include <Windows.h>\n#include <CommCtrl.h>\n#include <cassert>\n\nint main() {\n\n auto …Run Code Online (Sandbox Code Playgroud) 我在cygwin下使用Android NDK r6b(系统正确更新).我正在修改hello-jni示例以学习使用NDK.因为我有一个用C++编写的库,我希望在hello-jni中使用(实际上,我创建了一个名为helloworld的prj,其中包含一个名为ndkfoo.cpp的.cpp文件),我在Eclipse中创建了一个新的Android项目(更新为Android),添加了一个jni目录,添加了一个Android.mk和Application.mk文件,编辑它们以编译.cpp.在编译结束时,我获得了一个.so文件.
现在,在helloworld Android.mk中,我需要进行一些编辑,以告诉链接器包含该库.假设库文件是libmylib.so,我有以下android.mk脚本:
LOCAL_PATH:= $(调用my-dir)
包括$(CLEAR_VARS)
LOCAL_MODULE:= ndkfoo
LOCAL_SRC_FILES:= ndkfoo.cpp
LOCAL_C_INCLUDES + = $(LOCAL_PATH)/ mylib
LOCAL_LDLIBS + = -L $(LOCAL_PATH)/../../ mylib/libs/armeabi/-lmylib
包含$(BUILD_SHARED_LIBRARY)
我的目录按以下方式组织:
d:/android/android-ndk-r6b => android ndk root
d:/android/workspace/helloworld => helloworld project
d:/android/workspace/mylib => mylib project library
Run Code Online (Sandbox Code Playgroud)
(因此,libmylib.so的路径是:d:/ android/workspace/mylib/libs/armeabi).
不幸的是,这似乎不起作用.如果我从ndkfoo.cpp 删除对mylib的每个引用,它甚至在我的手机上编译并运行.如果我不删除对mylib的引用,它会编译但不会链接:我得到以下结果:
D:/android/android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windo ws/bin /../ lib/gcc/arm-linux-androideabi/4.4.3 /../ ../../../arm-linux-androideabi/ bin/ld.exe:找不到-lmylib
PS.我忘了提到我在helloworld项目的jni目录下运行ndk-buld.PSS的.我在互联网上发现了很多类似的问题.我一直使用Visual C/C++ IDE,所以我对GCC,makefile等都很新...
有关背景,请参阅此问题.
基本上,我有一个类的以下定义
class MyClass {
virtual int foo4(double, int);
};
Run Code Online (Sandbox Code Playgroud)
有没有办法指示编译器生成两个可以解析的符号foo4?也就是说,如果可执行文件要求动态链接器解析_ZN7MyClass4foo4Edi(符号for MyClass::foo4(double, int))和其他一些符号(比方说_ZN7MyClass9reserved1Ev,符号MyClass::reserved1()),我希望动态链接器将两者解析为&MyClass::foo4(double, int).我在Linux上使用相当现代的GCC.
我是编程Linux内核模块的新手,许多关于该主题的入门指南包含很少有关如何构建内核模块的信息,该内核模块将在Linux的许多版本和CPU平台上运行.我见过的大多数指南都只是说"Linux不能确保版本之间的任何 ABI/API兼容性".但是,其他操作系统确实为主要版本提供了这些保证,并且指南主要针对2.7(现在有点旧).
我想知道现在是否有任何类型的ABI/API兼容性,或者是否有任何标准方法来处理版本控制,而不是将我的代码的内核相关位隔离到具有大量预处理器指令的文件中.(另外,在第二种情况下我应该使用任何标准的预处理器符号吗?)
在C中,我习惯于编写一个可以从任何客户端代码调用的共享库,只需链接库并包含相关的头文件即可使用它.但是,我已经读过C++的ABI过于易变,非标准,无法从其他来源可靠地调用函数.
这将使我相信在C++中创建像C一样通用的真正共享库是不可能的,但现实世界的实现似乎表明不是这样.例如,Node.js公开了一个非常简单的模块系统,它允许使用该函数动态导出普通的C++函数(不extern "C").NODE_SET_METHOD
C++ API的哪些元素可以安全公开(如果有的话),以及允许C++代码与其他C++代码交互的常用方法是什么?是否可以创建可以公开C++类的共享库?或者由于ABI不一致,是否必须为每个程序单独重新编译这些类?
使用我目前无法分解为MCVE的大量代码,所以我会尽我所能.
我正在使用一个编译为静态库的大型项目libfoo.a.一个单独的项目,bar链接到该库."冒犯"片段libfoo如下:
class Base {
public:
void foo(){}
void bar(){}
};
class Derived : private Base {
public:
using Base::foo;
};
Run Code Online (Sandbox Code Playgroud)
双方libfoo并bar广泛使用的提升.bar必须编译,-std=c++11因为它使用了C++ 11的功能,但libfoo可以用最少的选项编译(即GCC v4.8使用的默认编译器选项,似乎是).-std=c++0x-std=gnu++03
当我尝试bar使用编译的GCC默认值进行链接时,它会因为冗长的名称错误警告而失败,该警告会减少为:-std=c++0xlibfoo.a
Undefined reference to Base::Derived::foo()
Run Code Online (Sandbox Code Playgroud)
当我重新构建libfoo.a时-std=c++11,不再出现此问题.
我比较了libfoo.avia nm的输出,在这两种情况下,都存在适当的符号.我还浏览了Cxx11Abi兼容性文档,看来这个编译器设置不应该"破坏"兼容性.
这个链接器问题的原因是什么?
我读过的,因为它是为"业绩原因"做不同的地方,但我仍然不知道什么是在性能得到这个16字节对齐提高了特殊情况.或者,无论如何,选择这个的原因是什么.
编辑:我想我以误导的方式写了这个问题.我没有询问为什么处理器使用16字节对齐的内存更快地处理事情,这在文档中随处可见.我想要知道的是,强制执行16字节对齐比仅让程序员在需要时自己对齐堆栈更好.我问这个是因为根据我的汇编经验,堆栈实施有两个问题:只有少于1%的执行代码才有用(所以其他99%实际上是开销); 它也是一个非常常见的错误来源.所以我想知道它最终是如何得到回报的.虽然我对此仍有疑问,但我接受了彼得的回答,因为它包含了我原来问题的最详细答案.