动态库中的静态*template*类成员
Static *template* class member across dynamic library
编辑:接受的答案下面的注释表明这可能是Android动态加载程序的问题。
我有一个带有静态成员的模板类的头。在运行时,静态成员的地址在库和客户端代码中使用。模板在库和客户端代码中都是隐式实例化的。它在Linux和OSX上运行良好,符号是重复的,但标记为"uniqued",如nm所示(见下文)。然而,当我为ARM(Android)编译时,DSO和可执行文件中的符号都被标记为弱。加载程序没有统一,符号在运行时被有效地复制!
我读到:一个静态成员的两个实例,怎么可能呢?静态模板数据成员存储尤其是这个答案:https://stackoverflow.com/a/2505528/2077394以及:http://gcc.gnu.org/wiki/Visibility
但我还是有点困惑。我知道可见性的属性有助于优化,但我认为它在默认情况下应该有效。我知道C++标准并不关心共享库,但这是否意味着使用共享库会破坏标准?(或者至少这个实现不符合C++标准?)额外奖励:我如何修复它?(不使用模板是不可接受的答案:)
标题:
template<class T>
struct TemplatedClassWithStatic {
static int value;
};
template<class T>
int TemplatedClassWithStatic<T>::value = 0;
shared.cpp:
#include "TemplateWithStatic.hpp"
int *addressFromShared() {
return &TemplatedClassWithStatic<int>::value;
}
main.cpp:
#include "TemplateWithStatic.hpp"
#include <cstdio>
int *addressFromShared();
int main() {
printf("%p %pn", addressFromShared(), &TemplatedClassWithStatic<int>::value);
}
建筑,看符号定义:
因此:
g++-4.8 -shared src/shared.cpp -o libshared.so -I include/ -fPIC
编译和链接主文件:
g++-4.8 src/main.cpp -I include/ -lshared -L.
符号标记为"唯一":
nm -C -A *.so a.out | grep 'TemplatedClassWithStatic<int>::value'
libshared.so:0000000000200a70 u TemplatedClassWithStatic<int>::value
a.out:00000000006012b0 u TemplatedClassWithStatic<int>::value
因此
~/project/android-ndk-r9/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++ -o libshared.so src/shared.cpp -I include/ --sysroot=/Users/amini/project/android-ndk-r9/platforms/android-14/arch-arm/ -shared
编译和链接主
~/project/android-ndk-r9/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++ src/main.cpp libshared.so -I include/ --sysroot=${HOME}/project/android-ndk-r9/platforms/android-14/arch-arm/ -I ~/project/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.8/include -I ~/project/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include -I ~/project/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.8/include/backward -I ~/project/android-ndk-r9/platforms/android-14/arch-arm/usr/include ~/project/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/libgnustl_static.a -lgcc
符号很弱!
nm -C -A *.so a.out | grep 'TemplatedClassWithStatic<int>::value'
libshared.so:00002004 V TemplatedClassWithStatic<int>::value
a.out:00068000 V TemplatedClassWithStatic<int>::value
编辑,注意上下文:我正在玩OOLua,这是一个帮助将C++绑定到Lua的库,当我开始以Android为目标时,我的单元测试失败了。我不"拥有"代码,我宁愿对它进行深入修改。
编辑,在Android上运行:
adb push libshared.so data/local/tmp/
adb push a.out data/local/tmp/
adb shell "cd data/local/tmp/ ; LD_LIBRARY_PATH=./ ./a.out"
0xb6fd7004 0xb004
Android不支持唯一符号。它是ELF格式的GNU扩展,仅适用于GLIBC 2.11及更高版本。Android根本不使用GLIBC,它使用了一个名为Bionic的不同的C运行时。
(更新)如果弱符号对您不起作用(结束更新)恐怕您将不得不修改代码,使其不依赖于静态数据。
您可以调整一些编译器/链接器设置以启用此功能(您查看过-fvisibility
标志吗?)。
GCC属性修饰符可能值得尝试(在变量上显式设置__attribute__ ((visibility ("default")))
)。
如果做不到这一点,我唯一能建议的解决方案是:(所有这些都有点丑陋):
- 显式实例化在共享库中创建的模板的所有形式,并在其实现中(而不是在头中)提供初始化器。这可能起作用,也可能不起作用
- 类似于(1),但使用shim函数作为共享变量的myers单例(下面的示例)
- 基于rtti在映射中为类分配一个变量(这也可能在共享库边界上失败)
例如
template<class T>
struct TemplatedClassWithStatic {
static int& getValue() { return TemplatedClassWithStatic_getValue((T const*)0); }
};
// types used by the shared library.. can be forward declarations here but you run the risk of violating ODR.
int& TemplatedClassWithStatic_getValue(TypeA*);
int& TemplatedClassWithStatic_getValue(TypeB*);
int& TemplatedClassWithStatic_getValue(TypeC*);
共享.cpp
int& TemplatedClassWithStatic_getValue(TypeA*) {
static int v = 0;
return v;
}
int& TemplatedClassWithStatic_getValue(TypeB*) {
static int v = 0;
return v;
}
int& TemplatedClassWithStatic_getValue(TypeC*) {
static int v = 0;
return v;
}
可执行文件还必须提供用于实例化模板的任何类型的实现。
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 表示"accepting anything for this template argument" C++概念的通配符
- 对RValue对象调用的LValue ref限定成员函数
- 为什么使用 "this" 指针调用派生成员函数?
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 助记符和指向成员语法的指针
- 用于访问容器<T>数据成员的正确 API
- 内置函数可查看CPP中的成员变量
- 是否可以初始化不可复制类型的成员变量(或基类)
- 具有模板成员函数的 C++ 模板专用化<template>
- 模板类静态成员变量在使用'extern template class'语法时的特殊化
- 无法从参数中的Template类对象访问成员
- 具有Non-type template Parameter成员函数的template类
- C++-专门化Template类的成员函数
- Template函数不是命名空间的成员
- 在类template的成员模板函数上启用le_if
- C++:模板成员函数,如 template<typename T> int foo<T>()
- 如何将"extern template"与同一类中的模板成员使用的嵌套类一起使用?
- 动态库中的静态*template*类成员
- 对静态成员使用"template<>"