C++加載動態鏈接庫(cmake生成,隱式鏈接,顯示鏈接)

C++程序在運行時有兩種方式加載動態連接庫:隱式鏈接顯式鏈接
加載動態庫文件就是將動態庫中的代碼邏輯映射到用戶進程地址空間,就在程序執行的時候進行函數調用。

隱式鏈接

隱式鏈接是在程序開始執行時就將動態庫文件加載到應用程序當中,需要在程序構建編譯階段就指定好,這種方式比較常用
unix的動態庫只有.so文件,使用時需要配置工程:

  • 包含庫頭文件
  • 包含庫.so文件地址
  • 鏈接庫名稱(多種方式)

windows的動態庫包括.lib和.dll文件,其中.lib文件包含了導出函數的入口信息,.dll文件包含了導出函數的具體實現,使用時需要配置工程:

  • 包含庫頭文件
  • 包含庫.lib文件地址
  • 鏈接庫名稱
  • 拷貝.dll文件到程序執行目錄

關於通過cmake構建工程隱式鏈接動態庫具體可參看cmake文檔

顯式鏈接

顯式鏈接是程序在執行過程中可以隨時加載、隨時卸載動態庫文件,所以顯式鏈接比較靈活,適合用於熱更新。

這裏通過一個簡單的demo來演示如何生成和使用,在動態連接庫中顯式鏈接導出的類。

生成

創建項目,生成動態庫。
頭文件中包含C++抽象類的接口和導出函數,將實際的派生類實現隱藏在動態庫中,通過這種方式導出類。
如果只是需要導出C風格的函數,那麼頭文件中聲明函數,實現部分隱藏在動態庫中,在每個函數聲明成導出即可。

項目結構

.
├── build
├── CMakeLists.txt
└── src
    ├── mytool.cpp
    └── mytool.h

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)

# support c++11
add_definitions(-std=c++11)

project(mytool)

set(MYTOOL_SRC
    src/mytool.h
    src/mytool.cpp
)

# use SHARED mode as dynamic link
add_library(mytool SHARED ${MYTOOL_SRC})

# if this lib also depend on other lib, should add link here
#target_link_libraries(mytool 
#    # some lib name
#)

mytool.h

#ifndef _MYTOOL_H
#define _MYTOOL_H

// you can also use a namespace to wrap the code

class MyTool
{
public:
	virtual ~MyTool() {}; // shoud be virtual

	// interface functions
	virtual void fun1() = 0;
	virtual int fun2(int x, int y) = 0;
};

// export the implementation, will generate both .lib and .dll if in windows and .so for unix
#ifdef _WIN32
#define MYTOOL_EXPORT extern "C"  __declspec (dllexport)
#else
#define MYTOOL_EXPORT extern "C" 
#endif

MYTOOL_EXPORT MyTool *CreateMyTool();

#endif // !_MYTOOL_H

mytool.cpp

#include <stdio.h>
#include "mytool.h"

class MyToolExtend : public MyTool
{
public:
	MyToolExtend() {}
	virtual ~MyToolExtend() {}
	
	virtual void fun1() override;
	virtual int fun2(int x, int y) override;
};

void MyToolExtend::fun1()
{
	printf("real fun1 called\n");
}

int MyToolExtend::fun2(int x, int y)
{
	printf("real fun2 called\n");
	return x + y;
}

MyTool *CreateMyTool()
{
	return new MyToolExtend();
}

通過cmake編譯
linux會生成libmytool.so
windows會生成mytool.libmytool.dll

使用

創建C++程序
main.cpp

#include <iostream>
#include "mytool.h" // include the lib header

// include shared lib load
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h> # in linux, should also link to dl system library when build
#endif

// define shared lib load handler
typedef MyTool *(*CreateMyToolFunc)();

#ifdef _WIN32
HINSTANCE gDllHandler = nullptr;
const char *gDefaultSharedLibPath = "mytool.dll"; // here put it the same path
#else
void *gSoHandler = nullptr;
const char *gDefaultSharedLibPath = "libmytool.so"; // here put it the same path, some linux must use ./libmytool.so
#endif

int main()
{

#ifdef _WIN32
	// load shared lib
	gDllHandler = LoadLibrary(gDefaultSharedLibPath);
	if (!gDllHandler)
		std::cout << "load shared lib failed" << std::endl;

	CreateMyToolFunc create_mytool = (CreateMyToolFunc)(GetProcAddress(gDllHandler, "CreateMyTool"));
	MyTool *my_tool = create_mytool(); // get the derived class instance from shared lib

	my_tool->fun1();
	int z = my_tool->fun2(2, 3);
	printf("z: %d", z);

	// when all done, unload shared lib
	FreeLibrary(gDllHandler);
	gDllHandler = nullptr;
#else
	gSoHandler = dlopen(gDefaultSharedLibPath, RTLD_LAZY);
	if (!gSoHandler)
		std::cout << "load shared lib failed" << std::endl;

	CreateMyToolFunc create_mytool = (CreateMyToolFunc)(dlsym(gSoHandler, "CreateMyTool"));
	MyTool *my_tool = create_mytool(); // get the derived class instance from shared lib

	my_tool->fun1();
	int z = my_tool->fun2(2, 3);
	printf("z: %d", z);

	// when all done, unload shared lib
	dlclose(gSoHandler);
	gSoHandler = nullptr;
#endif

	return 0;
}

運行結果

real fun1 called
real fun2 called
z: 5

注意:

  • linux下使用需要鏈接系統dl
  • linux下指定so文件路徑可能要./符號
  • windows下使用僅需要dll文件
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章