跨动态库的静态*模板*类成员

时间:2014-04-24 06:46:37

标签: c++ templates c++11 android-ndk shared-libraries

编辑:接受答案下方的评论显示,它可能是Android动态加载程序的问题。

我有一个带有静态成员的模板类的标题。在运行时,静态成员的地址在库和客户端代码中使用。模板在库和客户端代码中都是隐式实例化的。它在Linux和OSX上运行良好,符号是重复的,但标记为" uniqued"如nm所示(见下文)。 但是,当我为ARM(Android)编译时,符号在DSO和可执行文件中都标记为弱。加载器不统一,符号在运行时有效复制!

我读过这些: two instances of a static member, how could that be? Static template data members storage 特别是这个答案: 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 %p\n", addressFromShared(),  &TemplatedClassWithStatic<int>::value);
}

构建,查看符号定义:

生产.so:

 g++-4.8  -shared src/shared.cpp  -o libshared.so -I include/ -fPIC

编译和链接main:

 g++-4.8 src/main.cpp -I include/ -lshared -L.

符号标记为&#34;唯一&#34;:

nm -C -A *.so a.out | grep 'TemplatedClassWithStatic<int>::value'
libshared.so:0000000000200a70 u TemplatedClassWithStatic<int>::value
a.out:00000000006012b0 u TemplatedClassWithStatic<int>::value

生产.so

~/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时,我的单元测试失败了。我没有&#34;拥有&#34;代码,我宁愿深深地修改它。

编辑,在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

2 个答案:

答案 0 :(得分:4)

Android不支持唯一符号。它是ELF格式的GNU扩展,仅适用于GLIBC 2.11及更高版本。 Android根本不使用GLIBC,它使用了一个名为Bionic的不同C运行时。

(更新)如果弱符号对你不起作用(结束更新)我担心你必须修改代码,使其不依赖关于静态数据。

答案 1 :(得分:1)

可能有一些编译器/链接器设置可以调整以启用此功能(您是否看过-fvisibility标志?)。

可能一个GCC属性修饰符可能值得尝试(在变量上明确设置__attribute__ ((visibility ("default"))))。

如果做不到这一点,我可以建议的唯一解决方法是:(都有点难看):

  1. 显式实例化在共享库中创建的所有模板形式,并在其实现中提供初始化器(而不是在头文件中)。这可能有效,也可能无效。
  2. 与(1)类似,但使用填充函数作为共享变量的myers singleton(下面的示例)。
  3. 基于rtti在类的映射中分配变量(在共享库边界上也可能失败)。
  4. e.g。

    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*);
    

    shared.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;
    }
    

    可执行文件还必须为它用于实例化模板的任何类型提供实现。