算法工程师经常与前端对接,一般地,我们不会将源码打包直接发给对方,而是将函数方法编译成库(即: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_ProjectCameraProjectorCalibrationx64ReleaseMyDll.lib 和对象 D:VS2017_ProjectCameraProjectorCalibrationx64ReleaseMyDll.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_ProjectCameraProjectorCalibrationx64ReleaseMyDll.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 }
注意全局变量等,成功调用!