最近用g++编译和链接共享库时,发现一个命名空间中操作符函数导出的问题。
过程是这样的:程序中有些C++操作符函数,放在命名空间中,这些函数需要作为共享库供其他程序调用。
例如,函数声明如下:
#mylib.h namespace myns { //.... ACE_CDR::Boolean operator<< (ACE_OutputCDR& os, PDCommand& x); //.... }
函数实现如下:
#mylib.cpp #include "mylib.h" using namespace myns; ACE_CDR::Boolean operator<< (ACE_OutputCDR& os, PDCommand& x) { //... }
共享库本身的编译和链接没有问题,但其他程序与共享库链接时,提示找不到声明对应的符号引用:
"undefined reference to `myns::operator<<(ACE_OutputCDR&, myns::PDCommand&)'"。
用nm查看共享库:
nm libmy.so | grep PDCommand
发现两个符号
U _ZN2mynslsER13ACE_OutputCDRRNS_9PDCommandE
000000000000f3f2 T _ZlsR13ACE_OutputCDRRN2myns9PDCommand
仔细观察发现,第一个符号相当于:
myns::operator << (ACE_OutputCDR& os, myns::PDCommand& x);
第二个符号相当于:
operator << (ACE_OutputCDR& os, myns::PDCommand& x);
另外,nm的输出中,"U"表示符号未定义,“T”表示代码段(TEXT段)。
因此,我们可以推断出第一条记录是在.h文件中导出的,第二条记录是在.cpp文件中导出的。由于两者的命名空间不同,导致头文件中声明的函数没有相应的定义,进而导致外部程序链接时失败。
如果把cpp文件中的函数实现改为如下,可以解决问题:
#mylib.cpp #include "mylib.h" namespace myns { //... ACE_CDR::Boolean operator<< (ACE_OutputCDR& os, PDCommand& x) { //... } } //namespace myns
此时再用nm查看共享库中的导出符号,发现只有一条记录:
000000000000f332 T _ZN2mynslsER13ACE_OutputCDRRNS_9PDCommandE
即相当于:
myns::operator << (ACE_OutputCDR& os, myns::PDCommand& x);
这正是我们想要的。
以上问题是由于“using namespace xxx”这种写法是一种不精确的命名空间指定,有可能出现起义。不过,对于命名空间中的非操作符函数的导出和链接,我还没有发现类似问题。
因此,目前可以得出,在共享库中导出有命名空间的c++操作符函数时,其实现文件中最好显式指定函数的命名空间,而不要用“using namespace xxx”的形式。