我有一个C ++项目,由于其目录结构被设置为静态库A
,它链接到共享库B
,它链接到可执行文件C
。 (这是一个使用CMake的跨平台项目,所以在Windows上我们得到A.lib
,B.dll
和C.exe
,在Linux上我们得到libA.a
,{{1} }和libB.so
。)库C
有一个init函数(A
,在A_init
中定义),从库A/initA.cpp
的init函数调用(B
中定义的B_init
,从B/initB.cpp
的主要调用。因此,在关联C
时,B
(以及A_init
中定义的所有符号)都会链接到initA.cpp
(这是我们想要的行为)。
问题在于B
库还定义了一个函数(A
,在Af
中定义),旨在通过动态加载(即A/Afort.f
/ { Windows上为{1}},Linux上为LoadLibrary
/ GetProcAddress
。由于库dlopen
中没有对dlsym
的引用,因此Af
中的符号不会包含在B
中。在Windows上,我们可以通过使用pragma来创建引用:
A/Afort.o
由于这是一个pragma,它只适用于Windows(使用Visual Studio 2008)。为了让它在Linux上运行,我们尝试将以下内容添加到B
:
#pragma comment (linker, "/export:_Af")
这不会导致符号A/initA.cpp
包含在extern void Af(void);
static void (*Af_fp)(void) = &Af;
的最后一个链接中。我们如何强制将符号Af
链接到B
?
答案 0 :(得分:12)
事实证明我最初的尝试主要在那里。以下作品:
extern "C" void Af(void);
void (*Af_fp)(void) = &Af;
对于那些想要一个自包含的预处理器宏来封装它的人:
#if defined(_WIN32)
# if defined(_WIN64)
# define FORCE_UNDEFINED_SYMBOL(x) __pragma(comment (linker, "/export:" #x))
# else
# define FORCE_UNDEFINED_SYMBOL(x) __pragma(comment (linker, "/export:_" #x))
# endif
#else
# define FORCE_UNDEFINED_SYMBOL(x) extern "C" void x(void); void (*__ ## x ## _fp)(void)=&x;
#endif
因此使用:
FORCE_UNDEFINED_SYMBOL(Af)
答案 1 :(得分:6)
答案 2 :(得分:3)
您可以在构建B时使用--undefined
选项:
g++ -Wl,--undefined,Af -o libB.so ...
答案 3 :(得分:3)
有一种更好的方法来编写FORCE_UNDEFINED_SYMBOL宏。只需将该函数指针强制转换为void *即可。然后它适用于任何功能 - 或者数据。此外,当宏的gcc部分也适用于MSVC时,为什么还要使用MSVC编译指示。所以我的简化版本是:
#define FORCE_UNDEFINED_SYMBOL(x) void* __ ## x ## _fp =(void*)&x;
因此使用:
FORCE_UNDEFINED_SYMBOL(Af)
但它必须在包含剥离符号的库的程序中使用。
答案 4 :(得分:0)
尝试将这些行放入B/initB.cpp
,以便他们(希望)在链接时强制进入libB.so
库。
但为什么你必须以这种方式去做呢?你不能设置它,以便可执行文件引用该函数(或它的调用者),导致链接器自动执行正确的操作吗?
答案 5 :(得分:0)
如果你可以使用gcc的 C ++ 0x 功能( -std = c ++ 0x ),那么函数默认模板参数可以做到这一点。从当前的c ++标准来看,函数模板不允许使用默认参数。在c ++ 0x中启用这些功能后,您可以执行以下操作: -
在静态库的一些头文件中......
template< class T = int >
void Af()
{
}
然后在其对应的cpp文件中使用显式模板实例化...
template void Af();
这将生成函数Af
的符号,尽管它尚未被调用/引用。
这不会影响调用者,因为由于默认模板参数,您无需指定类型。只需在函数声明之前添加template <class T = int >
,并在其实现文件中显式实例化它。
HTH,