Linux 上的 C++ 动态共享库
- 2024-10-09 09:10:00
- admin 原创
- 83
问题描述:
这是使用 g++ 进行动态共享库编译的后续。
我正在尝试在 Linux 上用 C++ 创建一个共享类库。我能够编译该库,并且可以使用我在这里和这里找到的教程调用一些(非类)函数。当我尝试使用库中定义的类时,我的问题就开始了。我链接到的第二个教程展示了如何加载用于创建库中定义的类的对象的符号,但没有使用这些对象来完成任何工作。
有谁知道创建共享 C++ 类库的更完整的教程,其中还展示了如何在单独的可执行文件中使用这些类?一个非常简单的教程,展示对象的创建、使用(简单的 getter 和 setter 就足够了)和删除,那就太好了。一个链接或一些开源代码的参考,说明如何使用共享类库,也同样不错。
尽管codelogic和nimrodm的答案确实有效,但我只想补充一点,自从问了这个问题后,我拿起了一本《Linux 编程入门》,它的第一章有示例 C 代码,以及创建和使用静态和共享库的良好解释。这些示例可通过 Google 图书搜索在该书的旧版本中找到。
解决方案 1:
我的类名
#ifndef __MYCLASS_H__
#define __MYCLASS_H__
class MyClass
{
public:
MyClass();
/* use virtual otherwise linker will try to perform static linkage */
virtual void DoSomething();
private:
int x;
};
#endif
我的班级
#include "myclass.h"
#include <iostream>
using namespace std;
extern "C" MyClass* create_object()
{
return new MyClass;
}
extern "C" void destroy_object( MyClass* object )
{
delete object;
}
MyClass::MyClass()
{
x = 20;
}
void MyClass::DoSomething()
{
cout<<x<<endl;
}
类用户.cc
#include <dlfcn.h>
#include <iostream>
#include "myclass.h"
using namespace std;
int main(int argc, char **argv)
{
/* on Linux, use "./myclass.so" */
void* handle = dlopen("myclass.so", RTLD_LAZY);
MyClass* (*create)();
void (*destroy)(MyClass*);
create = (MyClass* (*)())dlsym(handle, "create_object");
destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object");
MyClass* myClass = (MyClass*)create();
myClass->DoSomething();
destroy( myClass );
}
在 Mac OS X 上,使用以下命令进行编译:
g++ -dynamiclib -flat_namespace myclass.cc -o myclass.so
g++ class_user.cc -o class_user
在 Linux 上,使用以下命令进行编译:
g++ -fPIC -shared myclass.cc -o myclass.so
g++ class_user.cc -ldl -o class_user
如果这是针对插件系统的,则可以使用 MyClass 作为基类,并将所有必需的函数定义为虚拟函数。然后,插件作者将从 MyClass 派生,覆盖虚拟函数并实现create_object
和destroy_object
。您的主应用程序无需进行任何更改。
解决方案 2:
下面显示了共享类库 shared.[h,cpp] 和使用该库的 main.cpp 模块的示例。这是一个非常简单的示例,makefile 可以做得更好。但它有效并且可能对您有所帮助:
shared.h 定义类:
class myclass {
int myx;
public:
myclass() { myx=0; }
void setx(int newx);
int getx();
};
shared.cpp 定义 getx/setx 函数:
#include "shared.h"
void myclass::setx(int newx) { myx = newx; }
int myclass::getx() { return myx; }
main.cpp 使用该类,
#include <iostream>
#include "shared.h"
using namespace std;
int main(int argc, char *argv[])
{
myclass m;
cout << m.getx() << endl;
m.setx(10);
cout << m.getx() << endl;
}
以及生成 libshared.so 并将 main 与共享库链接起来的 makefile:
main: libshared.so main.o
$(CXX) -o main main.o -L. -lshared
libshared.so: shared.cpp
$(CXX) -fPIC -c shared.cpp -o shared.o
$(CXX) -shared -Wl,-soname,libshared.so -o libshared.so shared.o
clean:
$rm *.o *.so
要实际运行“main”并与 libshared.so 链接,您可能需要指定加载路径(或将其放在 /usr/local/lib 或类似位置)。
以下将当前目录指定为库的搜索路径并运行 main(bash 语法):
export LD_LIBRARY_PATH=.
./main
要查看程序是否与 libshared.so 链接,您可以尝试 ldd:
LD_LIBRARY_PATH=. ldd main
在我的机器上打印:
~/prj/test/shared$ LD_LIBRARY_PATH=. ldd main
linux-gate.so.1 => (0xb7f88000)
libshared.so => ./libshared.so (0xb7f85000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e74000)
libm.so.6 => /lib/libm.so.6 (0xb7e4e000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xb7e41000)
libc.so.6 => /lib/libc.so.6 (0xb7cfa000)
/lib/ld-linux.so.2 (0xb7f89000)
解决方案 3:
除了以前的答案之外,我想提高人们对这样一个事实的认识:您应该使用RAII(资源获取即初始化)习语来确保处理程序销毁的安全。
这是一个完整的工作示例:
接口声明Interface.hpp
::
class Base {
public:
virtual ~Base() {}
virtual void foo() const = 0;
};
using Base_creator_t = Base *(*)();
共享库内容:
#include "Interface.hpp"
class Derived: public Base {
public:
void foo() const override {}
};
extern "C" {
Base * create() {
return new Derived;
}
}
动态共享库处理程序Derived_factory.hpp
::
#include "Interface.hpp"
#include <dlfcn.h>
class Derived_factory {
public:
Derived_factory() {
handler = dlopen("libderived.so", RTLD_NOW);
if (! handler) {
throw std::runtime_error(dlerror());
}
Reset_dlerror();
creator = reinterpret_cast<Base_creator_t>(dlsym(handler, "create"));
Check_dlerror();
}
std::unique_ptr<Base> create() const {
return std::unique_ptr<Base>(creator());
}
~Derived_factory() {
if (handler) {
dlclose(handler);
}
}
private:
void * handler = nullptr;
Base_creator_t creator = nullptr;
static void Reset_dlerror() {
dlerror();
}
static void Check_dlerror() {
const char * dlsym_error = dlerror();
if (dlsym_error) {
throw std::runtime_error(dlsym_error);
}
}
};
客户端代码:
#include "Derived_factory.hpp"
{
Derived_factory factory;
std::unique_ptr<Base> base = factory.create();
base->foo();
}
笔记:
为了简洁起见,我将所有内容都放在头文件中。在实际生活中,您当然应该将代码拆分到
.hpp
和.cpp
文件之间。new
为了简化,我忽略了您想要处理/过载的情况delete
。
两篇清晰的文章可以了解更多详细信息:
C++ dlopen 迷你指南
C++ 运行时动态加载共享对象
解决方案 4:
基本上,您应该在要使用共享库中的类的代码中包含类的头文件。然后,在链接时,使用“-l”标志将您的代码与共享库链接。当然,这要求 .so 位于操作系统可以找到它的地方。请参阅3.5. 安装和使用共享库
使用 dlsym 适用于您在编译时不知道要使用哪个库的情况。这听起来不像是这种情况。也许令人困惑的是,无论您是在编译时还是运行时进行链接(使用类似的方法),Windows 都会调用动态加载的库?如果是这样,那么您可以将 dlsym 视为 LoadLibrary 的等价物。
如果您确实需要动态加载库(即,它们是插件),那么这个常见问题解答应该会有所帮助。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件