C#调用C++
C#作为极受欢迎的桌面窗口编写语言,如何使用C#调用C++代码一直是备受讨论的话题,这里简单介绍C#调用C++代码的方式。
首先C#相比于C++而言更高级一点,所以C#不能直接调用C++的代码。常用的方法是将C++代码编译成动态链接库,让C#去调用动态链接库,这里的原理底层就是将机器码之间的互相调用了。但是dll的调用在C++里需要头文件的引用,但是C#里依旧不需要头文件,C#是直接引用dll到内存里,这里就需要C#对dll中需要引用的函数进行一些预处理的定义。这一步C#分为两种模式,托管和非托管。
托管和非托管
- 托管引用
托管引用是基于CLR在C++代码编译dl时引入CLR支持,将C++代码编译成托管代码,而托管代码和C#代码是通用的,一样的两次编译。托管代码的好处就是,托管代码运行在CLR上可以跨平台、跨语言,并且CLR有自己的内存回收和安全检测,减少了C++部分的开发压力。但是tensorflow的C++项目是不支持托管的,如果在编译tensorflow c++项目的dll,会报C1189 <mutex> is not supported when compiling with /clr or /clr:pure
,这里主要是tensorflow不支持CLR,所以对于C#想要调用tensorflow的C++代码,只可以用非托管引用。 - 非托管引用
那么什么是非托管引用?其实就是直接在dll编译的时候使用__declspec(dllexport)
暴露出调用的函数接口,然后在C#引用dll支持,定义对于的接口,确定参数和返回值的类型,直接在内存中引用机器码。1
2[// 引用TestLib.dll ]
public static extern void DisplayHelloFromDLL(); // 定义接口
Tensorflow C++代码引用样例
C++的封装:函数封装和类封装
以下封装方式只是C++内部的不同,C#的调用 不会有区别。
第一步在visual studio中创建一个链接库的项目
不用去管里面的文件结构,接下来就是分为函数封装和类封装两种情况来讨论,首先请先配置好tensorflow的C++环境。
- 函数封装
首先创建要编译的类DLLTest,则得到两个文件DLLTest,h
和DLLTest.cpp
然后我们在DLLTest.h
中进行编译声明:接下来继续在1
2
3
4
5
6DLLTest.h
中进行要暴露的接口声明:接下来在1
EXTERN_C TFDLL_API int Add(int x, int y);
DLLTest.cpp
中进行声明的接口实现,这部分和普通C++代码编写没有区别。最后直接编译运行,在编译的时候记得选择适合自己项目的编译模式,最后会得到DLLTest.dll
、DLLTest.lib
和DLLTest.pdb
,则C++函数编译部分就结束了。 - 类封装
类的封装就复杂了一些,其实就是在外面套了个壳,将类方法暴露出来。如下有DLLClass.cpp
和DLLClass.h
为内部类。那么我们不用修改内部类,而修改DLLTest.h
和DLLTest.cpp
。最后编译部分和函数编译一样,最后得到1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// DLLTest.h
namespace DLLTestWrapper {
extern "C" DETECTDLL_API void __stdcall add();
}
// DLLTest.cpp
namespace DLLTestWrapper {
DLLApi* api = new DLLApi(); // 此处声明一个内部类的指针
void add() {
api->add(); // 此处调用内部类的方法实现暴露类方法
}
}DLLTest.dll
、DLLTest.lib
和DLLTest.pdb
。C#的引用
C#引用分两种,一种是引用托管代码,直接在项目属性里引入dll文件,然后就可以直接当作C#的类一样使用。
而非托管的引用必须先定义一个C#的方法接口,而且数据的格式必须和C++一样,如下:
1 | [ ] |
C#和C++之间的数据类型问题
那么这里又涉及了一个C#和C++之间数据类型的问题,其实大部分的数据类型是通用的,下面的文字简单的介绍一下:
C#与C++数据类型对应关系总结