C++ 动态库依赖于可执行文件(依赖项反转)
主程序提供一些服务来注册插件 插件通过C++ 动态库依赖于可执行文件(依赖项反转),c++,linux,dynamic-library,C++,Linux,Dynamic Library,主程序提供一些服务来注册插件 插件通过dlopen动态加载 插件使用主程序提供的一些符号,但在加载时,它们不会被库看到 以下是Main.cpp的代码: #include "SampleMain.h" #include <dlfcn.h> #include <iostream> #include <map> #include <string> typedef std::map<std::string, Service&g
dlopen
动态加载
插件使用主程序提供的一些符号,但在加载时,它们不会被库看到
以下是Main.cpp
的代码:
#include "SampleMain.h"
#include <dlfcn.h>
#include <iostream>
#include <map>
#include <string>
typedef std::map<std::string, Service> Services;
static Services services;
extern "C" void AddService( const char * name, Service service ) {
std::cerr << "AddService( " << name << " )" << std::endl;
services[name] = service;
}
int main( void ) {
void * hLib = dlopen( "lib/libsample.so", RTLD_LAZY );
if( hLib ) {
typedef bool ( * RegisterServices )( void );
RegisterServices registerServices =
(RegisterServices)dlsym( hLib, "RegisterServices" );
if( registerServices ) {
if( registerServices()) {
Services::iterator it = services.find( "MyService" );
if( it != services.end()) {
Service service = it->second;
int retCode = service( 0 );
std::cerr << "'MyService' returns " << retCode << std::endl;
}
else {
std::cerr << "'MyService' not found!" << std::endl;
}
}
else {
std::cerr << "Registration failed" << std::endl;
}
}
else {
std::cerr << "dlsym error: " << dlerror() << std::endl;
}
dlclose( hLib );
}
else {
std::cerr << "dlopen error: " << dlerror() << std::endl;
}
return 0;
}
#include "SampleMain.h"
#if defined __linux__
# define API extern "C" __attribute((visibility("default")))
#elif defined WIN32
# define API extern "C" __declspec(dllexport)
#else
# error Unsupported platform
#endif
#include <iostream>
#include <vector>
static int ServiceOffer( void * arg ) {
std::cerr << "ServiceOffer|entry" << std::endl;
std::vector<void *> v;
v.push_back( arg );
v.push_back( arg );
v.push_back( arg );
size_t val = v.size();
std::cerr << "ServiceOffer|exit, val: " << val << std::endl;
return 0;
}
API bool RegisterServices( void ) {
AddService( "MyService", ServiceOffer );
return true;
}
这是Makefile
:
run: Main lib/libsample.so
LD_LIBRARY_PATH=./lib ./Main
lib/libsample.so: SampleLib.cpp
g++ -fPIC -shared -o $@ $<
Main: SampleMain.cpp
g++ -fPIC -o $@ $< -ldl
.PHONY: all clean
all: Main lib/libsample.so
LD_LIBRARY_PATH=./lib ./Main
clean:
rm -f lib/libsample.so Main
Main: SampleMain.cpp
g++ -o $@ $< -rdynamic -Wl,--dynamic-list=main-api -ldl
lib/libsample.so: SampleLib.cpp
g++ -o $@ $< -fvisibility=hidden -fpic -shared
这是带有RTLD\u LAZY
的执行日志:
$ make run
g++ -fPIC -o Main SampleMain.cpp -ldl
LD_LIBRARY_PATH=./lib ./Main
./Main: symbol lookup error: lib/libsample.so: undefined symbol: AddService
如何导出符号
AddService
?正如预期的那样,解决方案处于链接器级别
以下是Makefile
的内容:
run: Main lib/libsample.so
LD_LIBRARY_PATH=./lib ./Main
lib/libsample.so: SampleLib.cpp
g++ -fPIC -shared -o $@ $<
Main: SampleMain.cpp
g++ -fPIC -o $@ $< -ldl
.PHONY: all clean
all: Main lib/libsample.so
LD_LIBRARY_PATH=./lib ./Main
clean:
rm -f lib/libsample.so Main
Main: SampleMain.cpp
g++ -o $@ $< -rdynamic -Wl,--dynamic-list=main-api -ldl
lib/libsample.so: SampleLib.cpp
g++ -o $@ $< -fvisibility=hidden -fpic -shared
执行日志变成:
$ make run
g++ -o Main SampleMain.cpp -rdynamic -Wl,--dynamic-list=main-api -ldl
g++ -o lib/libsample.so SampleLib.cpp -fvisibility=hidden -fpic -shared
LD_LIBRARY_PATH=./lib ./Main
AddService( MyService )
ServiceOffer|entry
ServiceOffer|exit, val: 3
'MyService' returns 0
完整的交叉编译解决方案可用。您没有。您的主应用程序应该通过
registerService
的函数指针参数公开它,即重新工具RegisterServices
以获取库代码可以调用的函数指针。调用看起来像registerService(AddServices)代码>。仅供参考,您可能希望使用一个可以容纳多个函数指针的结构来设置它,并传递该结构的地址,而不仅仅是一个函数指针。我想你迟早会需要它的。完成了,看看我的解决方案。所以是为了技术帮助,保留您的意见。这种协作模式很大程度上受到了N-API本机模块Node.JS的启发。“为您保留您的意见”-这不是一种意见;这是简单的逻辑。您指定的平台绝对没有任何允许或限制。如果您包括了可接受的条件,那么这仅限于Linux,我的评论会有很大的不同。很高兴你找到了一个解决方案,顺便说一句。我已经交叉编译了这个解决方案。插件使用mingw交叉编译器在Linux下完成,由Visual Studio 2017编译的主插件托管。