C++调用GO编写的dll时如何传递动态数组

GO语言中只有固定长度的数组,动态数组对应的是切片,但是切片与C++中的动态数组不一致,其结构比较复杂,无法与C++利用动态分配内存获得的数组相对应,所以将切片作为参数时,C++不能用指针或者数组进行接收,而需要用GoSlice结构体进行接收。
一、首先讲解一下如何用GO编译dll文件,有以下需要注意的几点:

  1. 导入“C”包,即:import “C”
  2. 导出函数的首字母必须大写,而且函数声明的上方必须有//export functionName的注释语句,其中functionName就是你要导出的函数名称。
  3. 编译时采用命令:go build -buildmode=c-shared -o yourDll.dll yourDll.go,其中yourDll.dll就是你生成的dll文件的名称,yourDll.go就是待编译的源代码

例如编写如下GO代码

package main
import "C"
import "fmt"
//terminal command :go build -buildmode=c-shared -o mdArray.dll main.go, 最后的main.go可以不写
//export mtArrayParamExample
func mtArrayParamExample(len1 int, len2 int, len3 int, array []int) { //resultArray must initilized outside
	fmt.Println("mtArrayParamExample called")
	//为三维数组赋值,实际上是用一维数组模拟三维数组
	for i := 0; i < len1; i++ {
		for j := 0; j < len2; j++ {
			for k := 0; k < len3; k++ {
				array[i*len1+j*len2+k] = i + j + k
			}
		}
	}
	//输出数组
	for i := 0; i < len1; i++ {
		for j := 0; j < len2; j++ {
			fmt.Printf("array value in go: row %d col %d: ", i, j)
			for k := 0; k < len3; k++ {
				fmt.Printf("%d ", array[i*len1+j*len2+k])
			}
			fmt.Printf("\n")
		}
	}
}

//export ArrayParamExample
func ArrayParamExample(length int, array []int) { // the slice of 'array' has initialized outside
	fmt.Println("ArrayParamExample called")
	//为数组赋值
	for i := 0; i < length; i++ {
		array[i] = i
		fmt.Printf("array value in go: %d\n", array[i])
	}
}

func main() {
}

经过上述步骤就会获得dll文件以及相对应的C++头文件。

二、GO编译dll的调用
如果你的dll文件是64位(或32位)的,则调用程序也必须是64位(或32位)的。刚刚生成的头文件有如下内容

/* Code generated by cmd/cgo; DO NOT EDIT. */
/* package command-line-arguments */
#line 1 "cgo-builtin-export-prolog"
#include  /* for ptrdiff_t below */
#ifndef GO_CGO_EXPORT_PROLOGUE_H
#define GO_CGO_EXPORT_PROLOGUE_H
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef struct { const char *p; ptrdiff_t n; } _GoString_;
#endif
#endif
/* Start of preamble from import "C" comments.  */
/* End of preamble from import "C" comments.  */
/* Start of boilerplate cgo prologue.  */
#line 1 "cgo-gcc-export-header-prolog"
#ifndef GO_CGO_PROLOGUE_H
#define GO_CGO_PROLOGUE_H
//这里是GO语言中数据类型与C++数据类型之间的对应关系,在你的调用程序中可以将这些代码放入头文件,不用全部拷贝,用到哪些拷贝哪些就可以,也可以直接把这个头文件纳入你的项目
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef __SIZE_TYPE__ GoUintptr;//这行注释掉,会引起错误
typedef float GoFloat32;
typedef double GoFloat64;
typedef float _Complex GoComplex64;//这行注释掉,会引起错误
typedef double _Complex GoComplex128;//这行注释掉,会引起错误
/*
  static assertion to make sure the file is being used on architecture
  at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
//GO语言中的引用类型的数据结构,可以看到切片是一个结构体
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
#endif
/* End of boilerplate cgo prologue.  */
#ifdef __cplusplus
extern "C" {
#endif
//导出函数的声明
extern void mtArrayParamExample(GoInt p0, GoInt p1, GoInt p2, GoSlice p3);
extern void ArrayParamExample(GoInt p0, GoSlice p1);
#ifdef __cplusplus
}
#endif

编写你的C++程序代码如下

#include 
#include 
using namespace std;
//下面的定义是从go生成dll时生成的头文件中拷贝过来的,这样你就不需要include GO生成的头文件
typedef signed char GoInt8;
typedef unsigned char GoUint8;
typedef short GoInt16;
typedef unsigned short GoUint16;
typedef int GoInt32;
typedef unsigned int GoUint32;
typedef long long GoInt64;
typedef unsigned long long GoUint64;
typedef GoInt64 GoInt;
typedef GoUint64 GoUint;
typedef float GoFloat32;
typedef double GoFloat64;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

typedef void (*funcmtArrayParamExample)(GoInt p0, GoInt p1, GoInt p2, GoSlice p3);//通过函数指针调用go的dll函数
typedef void (*funcArrayParamExample)(GoInt p0, GoSlice p1);//通过函数指针调用go的dll函数

int main() {
	DWORD dwError = 0;
	HMODULE h = LoadLibrary("yourDll.dll");
	if (NULL == h || INVALID_HANDLE_VALUE == h)
	{
		dwError = GetLastError();
		return -1;
	}
	//调用ArrayParamExample为外部传入的一维数组赋值
	funcArrayParamExample ArrayParamExample = (funcArrayParamExample)GetProcAddress(h, "ArrayParamExample");
	if (ArrayParamExample)
	{
		GoInt length = 5;
		GoSlice array;//声明切片结构体,并且必须在dll外部对切片进行初始化,一定要分配内存
		array.cap = length;
		array.len = length;
		array.data = malloc(sizeof(GoInt)*length);
		ArrayParamExample(length, array);//GO的dll的导出函数
		for (int k = 0; k < length; k++)
		{
			printf("array value in C++: %d \n", ((GoInt*)array.data)[k]);
		}
		free(array.data);
		printf("\n");
	}
	//如果需要使用多维数组,目前没有找到办法直接使用,只能利用一维数组实现多维数组的功能
	//调用mtArrayParamExample为外部传入的多维(实际上用一维数组模拟的多维数组)数组赋值
	funcmtArrayParamExample mtArrayParamExample = (funcmtArrayParamExample)GetProcAddress(h, "mtArrayParamExample");
	if (mtArrayParamExample)
	{
		GoInt row = 5;//第一维长度
		GoInt col = 3;//第二维长度
		GoInt length = 2;//第三维长度
		GoSlice array;//声明切片结构体,并且必须在dll外部对切片进行初始化,一定要分配内存
		array.cap = row*col*length;
		array.len = row*col*length;
		array.data = malloc(sizeof(GoInt)*array.len);
		mtArrayParamExample(row, col, length,array);//GO的dll的导出函数
		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j++)
			{
				printf("array value in C++: row %d, col %d: ", i, j);
				for (int k = 0; k < length; k++)
				{
					printf(" %d ", ((GoInt*)array.data)[i * row + j * col + k]);
				}
				printf("\n");
			}
		}
		free(array.data);
	}
	getchar();
	FreeLibrary(h);
	return 0;
}

编译运行,结果如下
C++调用GO编写的dll时如何传递动态数组_第1张图片
这样就实现了C++调用GO编译的dll时动态数组的传参问题

你可能感兴趣的:(GO,C/C++)