2012-06-11 55 views
1

有很多关于C++模板类的问题,它们包含静态成员变量以及从动态库或共享对象中导出它们。但是这个更深入一点:如果有多个共享对象应该怎么做,每个共享对象都有自己的一组实例,但可能使用来自另一个共享对象的实例?跨多个DLL/DSO的静态成员的模板类

考虑下面的示例代码:

/* file: common.h */ 

#include <stdio.h> 
#define PRINT fprintf (stderr, "(template %d) %d -> %d\n", parameter, data, new_data) 

template <int parameter> 
class SharedClass 
{ 
    static int data; 
public: 
    static void Set(int new_data) { PRINT; data = new_data; } 
}; 

template <int parameter> 
int SharedClass<parameter>::data = parameter; 


/* file: library1.h */ 

extern template class SharedClass<1>; 
void Library1Function(); 


/* file: library1.cpp */ 

#include "common.h" 
#include "library1.h" 
#include "library2.h" 

template class SharedClass<1>; 

void Library1Function() 
{ 
    SharedClass<1>::Set (100); 
    SharedClass<2>::Set (200); 
} 


/* file: library2.h */ 

extern template class SharedClass<2>; 
void Library2Function(); 


/* file: library2.cpp */ 

#include "common.h" 
#include "library1.h" 
#include "library2.h" 

template class SharedClass<2>; 

void Library2Function() 
{ 
    SharedClass<1>::Set (1000); 
    SharedClass<2>::Set (2000); 
} 


/* file: main.cpp */ 

#include "common.h" 
#include "library1.h" 
#include "library2.h" 

int main() 
{ 
    Library1Function(); 
    Library2Function(); 
    SharedClass<1>::Set (-1); 
    SharedClass<2>::Set (-2); 
} 

让我们再假设,我们建立了两个库,并使用GCC的应用程序:

$ g++ -fPIC -fvisibility=default -shared library1.cpp -o lib1.so 
$ g++ -fPIC -fvisibility=default -shared library2.cpp -o lib2.so 
$ g++ -fvisibility=default main.cpp -o main -Wl,-rpath=. -L. -l1 -l2 

然后运行可执行文件,我们会得到以下结果:

$ ./main 
(template 1) 1 -> 100 
(template 2) 2 -> 200 
(template 1) 100 -> 1000 
(template 2) 200 -> 2000 
(template 1) 1000 -> -1 
(template 2) 2000 -> -2 

这意味着库和可执行文件都访问相同的每个模板静态存储。
如果我们运行上的二进制文件“纳米-C”,我们会发现每个静态成员的定义只有一次,在相应的库:

$ nm -C -A *.so main | grep ::data 
lib1.so:0000000000001c30 u SharedClass<1>::data 
lib2.so:0000000000001c30 u SharedClass<2>::data 

但我有一些问题。

  1. 为什么,如果我们从两个头取出extern template class ...,我们会看到,静态成员在每个二进制,但测试的应用程序将继续正常工作?

    $ nm -C -A *.so main | grep ::data 
    lib1.so:0000000000001c90 u SharedClass<1>::data 
    lib1.so:0000000000001c94 u SharedClass<2>::data 
    lib2.so:0000000000001c94 u SharedClass<1>::data 
    lib2.so:0000000000001c90 u SharedClass<2>::data 
    main:0000000000401e48 u SharedClass<1>::data 
    main:0000000000401e4c u SharedClass<2>::data 
    
  2. 是否有可能在MSVC下构建它?
    或者更具体地说,如何处理__declspec(dllexport)__declspec(dllimport)来使一些实例化出口,还有一些 - 导入?

  3. 最后:这是一个未定义行为的例子吗?

回答

1

要回答第1点:当动态连接器解析符号,它采用模块来链接的列表。首先检查加载的第一个模块,然后检查第二个模块,依此类推。

IIRC,当data构件在主,lib1.so,和lib2.so使用的,这仍然视为动态符号引用,即使部件被同一模块中的声明。因此,当链接程序在运行程序时解析符号时,所有三个模块都会在以下三个模块中的一个模块中使用data成员实现:无论哪个先载入。另外两对仍然被加载到内存中,但未被使用。

(尝试在所有三个模块std::cout << &(SharedClass<n>::data) << std::endl;印刷应该是所有6例相同的地址。)

要回答3点,我不认为这种行为是不确定的。到底发生了什么取决于你的系统的动态链接器,但我不知道任何链接器不会以完全相同的方式处理这种情况。

我不能说第2点,因为我没有很多MSVC的经验。

+0

是的,两种情况下的地址都是相同的。谢谢。 – intelfx