前言
项目中主程序引用动态库的好处是,动态库本身自带模块化属性:维护改动量小,便于分配开发任务。本篇仅列举我常用的方法,不收录全部方法,主要目的是为了与QT生成及引用so动态库文件进行对比。
C++编译及引用so动态库
编译动态库
提供给外部使用的函数,需要在前面加上extern "C" ,类不需要加额外的声明,以下是一个完整的cpp文件(myClass.h)及说明
#pragma once
#include <iostream>class MyClass {
public:MyClass();~MyClass();void printMessage(const char* msg);
};//普通函数需要使用extern "C"声明,外部才可调用
#ifdef __cplusplus
extern "C" {
#endif
MyClass* createMyClass(); // 创建对象
void destroyMyClass(MyClass*); // 销毁对象#ifdef __cplusplus
}
#endif
myClass.cpp代码如下:
#include "myClass.h"MyClass::MyClass() {}
MyClass::~MyClass() {}
void MyClass::printMessage(const char* msg) {std::cout << "Message: " << msg << std::endl;
}//外部函数创建动态类
extern "C" MyClass* createMyClass() {return new MyClass();
}//外部函数销毁动态类
extern "C" void destroyMyClass(MyClass* obj) {delete obj;
}
编译的Makefile文件如下
DEBUG_FLAGS = -fPIC -g -D__MUTEX
LIB_FLAGS =
CC = g++# 自动获取所有 .cpp 文件
SRC_FILES := $(wildcard *.cpp)
# 生成对应的目标库文件名
TARGETS := $(patsubst %.cpp,lib%.so,$(SRC_FILES))# 默认目标: 编译所有库
all: $(TARGETS)# 通用规则: 将单个 .cpp 编译为对应的 .so
lib%.so: %.cpp$(CC) -shared -o $@ $< $(DEBUG_FLAGS) $(LIB_FLAGS)# 清理所有生成的库和中间文件
.PHONY: clean
clean:rm -f $(TARGETS) *.o *.so
引用动态库
需要使用<dlfcn.h>库的dlopen、dlsym打开及调用,且使用前需声明,格式如下
// 定义函数指针类型
typedef MyClass* (*CreateFunc)();
typedef void (*DestroyFunc)(MyClass*);
typedef void (*PrintFunc)(MyClass*, const char*);
完整代码
#include <iostream>
#include <dlfcn.h>
#include "myClass.h"
// 定义函数指针类型
typedef MyClass* (*CreateFunc)();
typedef void (*DestroyFunc)(MyClass*);
typedef void (*PrintFunc)(MyClass*, const char*);int main() {void* handle = dlopen("./libmyClass.so", RTLD_LAZY); //if (!handle) {std::cerr << "Error: " << dlerror() << std::endl;return -1;}// 使用extern "C"声明的函数,dlsym直接引用该函数名称CreateFunc create = (CreateFunc)dlsym(handle, "createMyClass");DestroyFunc destroy = (DestroyFunc)dlsym(handle, "destroyMyClass");//类中的成员函数,dlsym不能直接引用该函数名称,编译出动态库文件后,需要通过命令nm -D libmyClass.so | grep printMessage查看PrintFunc print = (PrintFunc)dlsym(handle, "_ZN7MyClass12printMessageEPKc");// 错误检查const char* dlsym_error = dlerror();if (dlsym_error || !create || !destroy || !print) {std::cerr << "Failed to load symbols: " << (dlsym_error ? dlsym_error : "Unknown error") << std::endl;dlclose(handle);return -1;}// 使用动态库功能MyClass* obj = create();//动态创建类print(obj, "Hello from dynamically loaded library!");destroy(obj);//销毁类dlclose(handle); // 卸载库return 0;
}
需要注意的是,使用extern "C"声明的函数,dlsym直接引用该函数名称
CreateFunc create = (CreateFunc)dlsym(handle, "createMyClass");
DestroyFunc destroy = (DestroyFunc)dlsym(handle, "destroyMyClass");
类中的成员函数,dlsym不能直接引用该函数名称,编译出动态库文件后,需要通过命令nm -D libmyClass.so | grep printMessage查看,如下图所示
C编译及引用so动态库
编译动态库
不需要额外声明,直接写函数,如下是待打包成动态库的logprint.c文件
#include <stdio.h>void logprint(const char *logstr) {printf("[LOG]:%s\n",logstr);
}
Makefile文件内容
DEBUG_FLAGS = -fPIC
LIB_FLAGS =
CC = gcc# 自动获取所有 .cpp 文件
SRC_FILES := $(wildcard *.c)
# 生成对应的目标库文件名
TARGETS := $(patsubst %.c,lib%.so,$(SRC_FILES))# 默认目标: 编译所有库
all: $(TARGETS)# 通用规则: 将单个 .c 编译为对应的 .so
lib%.so: %.c$(CC) -shared -o $@ $< $(DEBUG_FLAGS) $(LIB_FLAGS)# 清理所有生成的库和中间文件
.PHONY: clean
clean:rm -f $(TARGETS) *.o *.so
引用动态库
需要使用<dlfcn.h>库的dlopen、dlsym打开及调用,且使用前需声明,格式如下
// 声明要调用的函数原型
typedef void (*logprint)(const char *logstr);
完整代码
// ftp_main.c
#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>
#define SO_FILE "./liblogprint.so"
// 声明要调用的函数原型
typedef void (*logprint)(const char *logstr);int main() {void *handle;char *error;int ret;// 打开动态库handle = dlopen(SO_FILE, RTLD_LAZY);if (!handle) {fprintf(stderr, "%s\n", dlerror());return 1;}
// 获取要调用的函数指针logprint fun_logprint = (logprint)dlsym(handle, "logprint");if ((error = dlerror()) != NULL) {fprintf(stderr, "%s\n", error);dlclose(handle);return 1;}fun_logprint("hello world");// 关闭动态库dlclose(handle);return 0;
}
结尾
本篇Linux下C/C++调用动态库,都是用C风格的方式调用,所有C不需要增加额外的东西,而C++必须对函数进行extern "C"声明才能调用,并且类函数的调用还需要通过命令nm -D libxxx.so查看函数在动态库中的实际符合名。