算法工程師經常與前端對接,一般地,我們不會將源碼打包直接發給對方,而是將函數方法編譯成庫(即:win下的DLL or LIB文件)
一方面是為了保密,另一方面是為方便使用(假如你的算法幾十個cpp,別人連帶界面cpp編譯很費時間)。常用的庫打包有如下三個方式,本文采用第一種方式作為范例。
三種方式區別:
1、采用第一種方法時,需要包含文件:.h .lib .dll,有頭文件,具有可讀性;
2、第二種和第三種庫使用時,需要預編譯、預加載等操作,第三種直接連頭文件都看不到,
保密性最好,這里不作多討論。
下文采用第一種導出方式,即:動態鏈接庫(DLL),導出文件有:.h .lib .dll
一、創建導出庫工程
打開VS2019, 點擊:文件 -> 新建 -> 項目->動態連接庫(DLL),選中如上圖第一項,新建工程;自動生成如下幾個文件,我們全部刪除。
項目屬性-C/C++-預編譯頭默認如下圖設置
將其修改為如下內容:
最后,別忘記設置工程為Release X64,
二、編寫庫導出.h .cpp文件
所謂編寫庫導出文件,也就是編寫代碼,告訴編譯器,那些函數、類、變量要導出到庫文件中。這里給出一個范例,以后用照抄就行(該工程不需要
執行,所以沒有main函數);
下面的聲明的集合函數,可以改為任何自定義函數,記得:函數和類前面加宏定義:API_SYMBOL(也即:__declspec(dllexport))
,下面MyDll.cpp文件記得要加 #define BUILD_MYDLL
注意:這里給出了C風格函數、C++模板函數、全局變量、類的導出方式,經過多個工程測試,方法有效。
MyDll.h
1 #pragma once 2 3 #ifdef BUILD_MYDLL 4 #define API_SYMBOL __declspec(dllexport) 5 #else 6 #define API_SYMBOL __declspec(dllimport) 7 #endif // BUILD_MYDLL 8 9 // OS 10 #include<iostream> 11 // ThirdParty 12 #include<opencv2/opencv.hpp> 13 #include<Eigen/Core> 14 15 // 導出全局變量 16 extern "C" API_SYMBOL int globa_a; 17 18 // 導出函數 19 extern "C" API_SYMBOL int FunAdd(int a, int b); 20 21 extern "C" API_SYMBOL cv::Mat FunMatAdd(const cv::Mat& m1, const cv::Mat& m2); 22 23 // 導出C++模板函數 24 API_SYMBOL Eigen::Matrix2d FunMatAdd(const Eigen::Matrix2d& m1, const Eigen::Matrix2d& m2); 25 26 //導出類 27 class API_SYMBOL MyClass 28 { 29 public: 30 MyClass(); 31 ~MyClass(); 32 33 private: 34 35 };
MyDll.cpp

1 #define BUILD_MYDLL // 由於是導出函數,所以徐婭哦定義.h文件中的BUILD_MYDLL 2 #include "MyDll2.h" 3 4 int globa_a = 999; 5 6 // 刪除宏API_SYMBOL 7 int FunAdd(int a, int b) 8 { 9 std::cout << "globa_a = " << ++globa_a << std::endl; 10 return a + b; 11 } 12 13 cv::Mat FunMatAdd(const cv::Mat & m1, const cv::Mat & m2) 14 { 15 std::cout << "globa_a = " << ++globa_a << std::endl; 16 return m1 + m2; 17 } 18 19 Eigen::Matrix2d FunMatAdd(const Eigen::Matrix2d & m1, const Eigen::Matrix2d & m2) 20 { 21 std::cout << "globa_a = " << ++globa_a << std::endl; 22 return m1 + m2; 23 } 24 25 MyClass::MyClass() 26 { 27 std::cout << "MyClass" << std::endl; 28 } 29 30 MyClass::~MyClass() 31 { 32 }
直接編譯,可以看到如下信息:
1 1> 正在創建庫 D:\VS2017_Project\CameraProjectorCalibration\x64\Release\MyDll.lib 和對象 D:\VS2017_Project\CameraProjectorCalibration\x64\Release\MyDll.exp 2 1>正在生成代碼 3 1>All 939 functions were compiled because no usable IPDB/IOBJ from previous compilation was found. 4 1>已完成代碼的生成 5 1>ExportDLL.vcxproj -> D:\VS2017_Project\CameraProjectorCalibration\x64\Release\MyDll.dll 6 1>已完成生成項目“ExportDLL.vcxproj”的操作。 7 ========== 生成: 成功 1 個,失敗 0 個,最新 0 個,跳過 0 個 ==========
三、庫打包與測試
現在將上述MyDll.h、MyDll.lib、MyDll.dll三個文件放到一起,我們新建一個控制台工程測試這些庫,注意:上述編譯庫的時候,我使用了第三方庫,如:Eigen等,
在測試工程中,也需要將這些庫提前包含進來。
新建好測試工程后,我們引入上面三個文件,這和配置OpenCV沒什么區別,不多說。
測試文件,test.cpp
1 // DemoInvokeDll2.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。 2 // 3 4 #include <iostream> 5 #include<MyDll.h> 6 7 int main() 8 { 9 globa_a = 66; 10 11 cv::Mat m1 = (cv::Mat_<uchar>(3, 1) << 1, 2, 3); 12 cv::Mat m2 = (cv::Mat_<uchar>(3, 1) << 1, 9, 2); 13 cv::Mat m3 = FunMatAdd(m1, m2); 14 15 Eigen::Matrix2d m4, m5, m6; 16 m4 << 1, 2, 3, 4; 17 m5 << 2, 3, 4, 5; 18 m6 = FunMatAdd(m4, m5); 19 std::cout << m6(0, 0) << ", " << m6(0, 1) << std::endl; 20 std::cout << m6(1, 0) << ", " << m6(1, 1) << std::endl; 21 22 MyClass mc; 23 return 1; 24 }
注意全局變量等,成功調用!