C++ 跨动态库的静态*模板*类成员
编辑:接受答案下面的评论表明这可能是Android动态加载程序的问题 我有一个带有静态成员的模板类的头。在运行时,静态成员的地址将在库和客户端代码中使用。模板在库和客户机代码中都隐式实例化。它在Linux和OSX上运行良好,符号是重复的,但标记为“uniqued”,如nm所示(见下文)。 然而,当我为ARM(Android)编译时,符号在DSO和可执行文件中都被标记为弱。加载程序不统一,符号在运行时被有效复制 我读到: 尤其是这个答案: 以及: 但我还是有点困惑。我知道可见性属性有助于优化,但我认为它在默认情况下应该可以工作。我知道C++标准不关心共享库,但这是否意味着使用共享库违反了标准?(或者至少这个实现不是C++标准符合?) 奖金:我怎样才能修好它?(不使用模板是不可接受的答案:)) 标题:C++ 跨动态库的静态*模板*类成员,c++,templates,c++11,android-ndk,shared-libraries,C++,Templates,C++11,Android Ndk,Shared Libraries,编辑:接受答案下面的评论表明这可能是Android动态加载程序的问题 我有一个带有静态成员的模板类的头。在运行时,静态成员的地址将在库和客户端代码中使用。模板在库和客户机代码中都隐式实例化。它在Linux和OSX上运行良好,符号是重复的,但标记为“uniqued”,如nm所示(见下文)。 然而,当我为ARM(Android)编译时,符号在DSO和可执行文件中都被标记为弱。加载程序不统一,符号在运行时被有效复制 我读到: 尤其是这个答案: 以及: 但我还是有点困惑。我知道可见性属性有助于优化,但
template<class T>
struct TemplatedClassWithStatic {
static int value;
};
template<class T>
int TemplatedClassWithStatic<T>::value = 0;
编辑和链接主要内容:
g++-4.8 src/main.cpp -I include/ -lshared -L.
符号标记为“唯一”:
编译和链接main
~/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
您可以调整一些编译器/链接器设置来启用此功能(是否查看了
-fvisibility
标志?)
可能值得尝试使用GCC属性修饰符(在变量上显式设置\uuuuuuu属性((可见性(“默认”))
)
如果做不到这一点,我唯一能建议的解决办法是:(所有这些都有点难看):
可执行文件还必须提供用于实例化模板的任何类型的实现。Android不支持唯一符号。它是ELF格式的GNU扩展,仅适用于GLIBC2.11及更高版本。Android根本不使用GLIBC,它使用了另一种叫做仿生的C运行时
(更新)如果弱符号不适用于您(结束更新),恐怕您必须修改代码,使其不依赖于静态数据。是的,我知道可能的解决方法,如果我要设计自己的库,我会这样做。但在这里,这是不可能的,因为我不“拥有”代码,我宁愿深入修改它。我和乌鲁阿有这个问题。现在我想知道我的测试用例是否应该按照标准工作。如果它应该工作,那么我可以认为它是一个GCC的错误!谢谢我应该得出结论,只要你开始使用共享库,Android工具链就不支持完全的C++标准吗?“只要你开始使用共享库,Android工具链就不支持完全的C++标准”---我自己对弱符号(Linux上)的实验表明,它们实际上是在共享库间工作的静态数据。也就是说,链接器将只强制使用一个弱符号,其他实例将被忽略。您是否尝试运行您的程序?是的,打印的地址不同(仅在Android上)。您是否使用
dlopen
打开共享库?当地址确实不同时就会出现这种情况。地址应该是相同的,在Linux上也是相同的。这意味着模板与此无关。这纯粹是安卓libc的问题。
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
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
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*);
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;
}