2014-04-24 2 views
5

편집 : 승인 된 답변 아래의 댓글은 Android 동적 로더에 문제가 있음을 보여줍니다.동적 라이브러리의 정적 * 템플릿 * 클래스 멤버

정적 멤버가있는 템플릿 클래스의 헤더가 있습니다. 런타임시 정적 멤버의 주소는 라이브러리 및 클라이언트 코드에서 사용됩니다. 템플리트는 라이브러리와 클라이언트 코드에서 내재적으로 인스턴스화됩니다. 그것은 Linux와 OSX에서 잘 작동하지만 심볼은 복제되지만 nm으로 표시된 것처럼 "uniqued"로 표시됩니다 (아래 참조). 그러나 ARM (Android) 용으로 컴파일하면 심볼이 DSO 및 실행 파일에서 weak로 표시됩니다. 로더가 통합되지 않으며 런타임에 심볼이 효과적으로 복제됩니다! 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 

컴파일 메인 링크 :

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 

를 .so를 제조 컴파일 및 링크 주

~/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, 루아 내 unittests에 ++ 바인딩 C를 돕는 라이브러리 놀고 있었는데 나는 안드로이드를 대상으로 시작 실패했을 때. 나는 코드를 "소유"하지 않고 차라리 깊이 수정하려고한다.

편집, 안드로이드에서 실행합니다 :

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 

답변

4

Android는 고유 한 기호를 지원하지 않습니다. GLIBC 2.11 이상에서만 작동하는 ELF 형식의 GNU 확장입니다. Android는 GLIBC를 전혀 사용하지 않으며 Bionic이라는 다른 C 런타임을 사용합니다.

(갱신) 약한 기호는 작동하지 않는 경우 (최종 업데이트) 나는 당신이 정적 데이터에 의존하지 않도록 코드를 수정해야 할 것 같군요.

+0

감사. 공유 라이브러리를 사용하기 시작하면 안드로이드 툴체인이 완전한 C++ 표준을 지원하지 않는다고 결론을 맺어야합니까? – Joky

+0

"안드로이드 툴체인은 공유 라이브러리를 사용하기 시작하면 C++ 표준을 완벽하게 지원하지 않습니다."- 약한 심볼을 사용하여 실험 한 결과 (Linux에서) 공유 라이브러리에서 실제로 정적 데이터를 처리하는 것으로 나타났습니다. 즉, 링커는 약한 기호 중 하나만 사용하도록 강제하고 다른 인스턴스는 무시됩니다. 프로그램을 실행하려고 시도 했습니까? –

+0

예, 인쇄 된 주소가 다릅니다 (Android에서만) – Joky

1

이 가능하도록 조정할 수있는 몇 가지 컴파일러/링커 설정이있을 수 있습니다 (당신이 -fvisibility 플래그를 살펴 보았다?).

아마도 GCC 속성 수정자가 시도해 볼 가치가 있습니다 (변수에 __attribute__ ((visibility ("default"))) 명시 적으로 설정). 그 실패

, 내가 제안 할 수있는 유일한 해결 방법은 다음과 같습니다 (모두 다소 못생긴) :

  1. 가 명시 적으로 공유 라이브러리에서 만든 템플릿의 모든 형태를 인스턴스화하고 (그 구현의 초기화를 제공 헤더에 없음). 이것은 작동 할 수도 있고 작동하지 않을 수도 있습니다.
  2. 공유 변수 (아래 예)에 대한 myers 싱글 톤으로 shim 함수를 사용합니다.
  3. 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*); 

실행 파일은 또한 템플릿의 인스턴스를 사용하는 모든 종류의 구현을 제공 할 것입니다

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

shared.cpp.

+0

그래, 가능한 해결 방법을 알고 있고 내 자신의 라이브러리를 디자인한다면 그렇게 할 것입니다. 하지만 여기서는 코드를 "소유"하지 않고 오히려 깊이 수정하려고합니다. OOLUA와 관련하여이 문제가 있습니다. 이제 테스트 케이스가 표준에 따라 작동해야하는지 아닌지 궁금합니다. 그것이 작동한다면 GCC 버그로 간주 할 수 있습니다! – Joky

관련 문제