C/C++调用Golang 二

《C/C++调用Golang 一》简单介绍了C/C++调用Golang的方法步骤,只涉及一个简单的函数调用。本文总结具体项目中的使用场景,将介绍三种较复杂的调用方式:一,C++向golang传入复杂结构体;二,C++向golang传入回调函数,在golang中调用C++函数;三,C++调用golang函数,返回复杂的结构体。

(本文后面涉及三个例子,省略了编译步骤,仅展示关键代码。具体操作步骤参考《C/C++调用Golang 一》)

一 C++向golang传入复杂结构体

采用avro来序列化与反序列化结构体。C++版avro使用官方版本,golang版avro使用gopkg.in/alanctgardner/gogen-avro.v4 。(C++代码省略了avro结构体的序列化与反序列化,仅展示C++与Golang的交互部分)

1.1 Golang 代码

package main

import "C"

import "fmt"

//export WriteData

func WriteData(data []byte) int {

fmt.Println("WriteData ", data, len(data))

return 0

}

func main() {

}

编译生成的头文件

/* Created by "go tool cgo" - DO NOT EDIT. */

/* package c_references_to_go/sample1 */

/* 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

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 GoInt32 GoInt;

typedef GoUint32 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_32_bit_pointer_matching_GoInt[sizeof(void*)==32/8 ? 1:-1];

typedef struct { const char *p; GoInt n; } GoString;

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 GoInt WriteData(GoSlice p0);

#ifdef __cplusplus

}

#endif

1.2 C++代码

#include <Windows.h>

#include <stdio.h>

#include "sample1.h"

//#include "LargeStruct.h"

typedef GoInt (*funcPtrWriteData)(GoSlice p0);

int main(){

HMODULE h = LoadLibraryA("sample1.dll");

if (NULL == h || INVALID_HANDLE_VALUE == h)

{

return -1;

}

funcPtrWriteData pfWriteData = (funcPtrWriteData)GetProcAddress(h,"WriteData");

if (pfWriteData)

{

/* LargeStruct ls;

ls.ID = "100001";

ls.Name = "Peter";

Pet pet;

pet.Type = "Dog";

pet.Name = "WangCai";

pet.Age = 5;

ls.Pets.push_back(pet);*/

GoSlice p0;

p0.data = 0;  //serial ls to binary

p0.len = p0.cap = 0;  //binary len

pfWriteData(p0);

}

FreeLibrary(h);

return 0;

}

二 C++向golang传入回调函数

2.1 Golang 代码

设置回调需要中间的桥接函数 CReportData

package main

import (

"fmt"

)

/*

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

typedef int (*ptfFuncReportData)(const char* data,int len);

extern int CReportData(ptfFuncReportData pf,const char* data,int len);

*/

import "C"

import (

"bytes"

"c_references_to_go/sample3/avro_struct"

"unsafe"

)

var callBackFunc C.ptfFuncReportData

//export SetCallBack

func SetCallBack(f C.ptfFuncReportData) {

callBackFunc = f

}

//export BeginWork

func BeginWork() {

go func() {

for index := 0; index < 10; index++ {

var ls avro_struct.LargeStruct

ls.ID = fmt.Sprintf("ID%d", 1000+index)

ls.Name = fmt.Sprintf("Peter%d", index)

ls.Pets = []*avro_struct.Pet{&avro_struct.Pet{Type: "Dog", Name: "WangCai", Age: 5}}

var buf bytes.Buffer

ls.Serialize(&buf)

dataSlice := buf.Bytes()

GoReportData(dataSlice)

}

}()

}

func GoReportData(data []byte) {

C.CReportData(callBackFunc, (*C.char)(unsafe.Pointer(&data[0])), C.int(len(data)))

}

func main() {

}

bridge.c

#include "_cgo_export.h"

int CReportData(ptfFuncReportData pf,const char* data,int len){

return pf(data,len);

}

编译后产生的头文件

/* Created by "go tool cgo" - DO NOT EDIT. */

/* package c_references_to_go/sample2 */

/* Start of preamble from import "C" comments. */

#line 7 "Y:\\mygo\\src\\c_references_to_go\\sample2\\main.go"

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

typedef int (*ptfFuncReportData)(const char* data,int len);

extern int CReportData(ptfFuncReportData pf,const char* data,int len);

#line 1 "cgo-generated-wrapper"

/* 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

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 GoInt32 GoInt;

typedef GoUint32 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_32_bit_pointer_matching_GoInt[sizeof(void*)==32/8 ? 1:-1];

typedef struct { const char *p; GoInt n; } GoString;

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 SetCallBack(ptfFuncReportData p0);

extern void BeginWork();

#ifdef __cplusplus

}

#endif

2.2 C++代码

#include <Windows.h>

#include <stdio.h>

#include "sample2.h"

typedef void (*funcPtrSetCallBack)(ptfFuncReportData p0);

typedef void (*funcPtrBeginWork)();

int OnReportData(const char* data,int len){

printf("OnReportData %x %d\r\n",data,len);

return 0;

}

int main(){

HMODULE h = LoadLibraryA("sample2.dll");

if (NULL == h || INVALID_HANDLE_VALUE == h)

{

return -1;

}

funcPtrSetCallBack pfSetCallBack = (funcPtrSetCallBack)GetProcAddress(h,"SetCallBack");

funcPtrBeginWork pfBeginWork = (funcPtrBeginWork)GetProcAddress(h,"BeginWork");

if (pfSetCallBack)

{

pfSetCallBack(OnReportData);

}

if (pfBeginWork)

{

pfBeginWork();

}

Sleep(1000*10);

FreeLibrary(h);

return 0;

}

运行之后的输出:

三 C++调用golang函数返回复杂结构体

不能向C++程序返回Go slice、Go struct。(详情见master分支 src/cmd/cgo/doc.go ,参考5)

3.1 Golang代码

package main

import (

"bytes"

"unsafe"

)

/*

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

typedef struct {

char* Data;

int DataLen;

} GetLargeStruct_return;

extern GetLargeStruct_return* CopyLargeSturct(char* data,int dataLen);

extern void FreeLargeSturct(GetLargeStruct_return* ptr);

*/

import "C"

import (

"c_references_to_go/sample3/avro_struct"

)

//export GetLargeStruct

func GetLargeStruct(paraIn int) *C.GetLargeStruct_return {

var ls avro_struct.LargeStruct

ls.ID = "1000001"

ls.Name = "Peter"

ls.Pets = []*avro_struct.Pet{&avro_struct.Pet{Type: "Dog", Name: "WangCai", Age: 5}}

var buf bytes.Buffer

ls.Serialize(&buf)

dataSlice := buf.Bytes()

return C.CopyLargeSturct((*C.char)(unsafe.Pointer(&dataSlice[0])), C.int(len(dataSlice)))

}

//export FreeLargeStruct

func FreeLargeStruct(ptr *C.GetLargeStruct_return) {

C.FreeLargeSturct(ptr)

}

//export GetLargeStruct2

func GetLargeStruct2(paraIn int) (*C.char, int) {

var ls avro_struct.LargeStruct

ls.ID = "1000001"

ls.Name = "Peter"

ls.Pets = []*avro_struct.Pet{&avro_struct.Pet{Type: "Dog", Name: "WangCai", Age: 5}}

var buf bytes.Buffer

ls.Serialize(&buf)

dataSlice := buf.Bytes()

return (*C.char)(unsafe.Pointer(C.CBytes(dataSlice))), len(dataSlice)

}

//export FreeCBytes

func FreeCBytes(ptr *C.char) {

C.free(unsafe.Pointer(ptr))

}

func main() {

}

C函数源码文件 (释放C分配的内存)

#include "_cgo_export.h"

GetLargeStruct_return* CopyLargeSturct(char* data,int dataLen){

GetLargeStruct_return* result = (GetLargeStruct_return*)malloc(sizeof(GetLargeStruct_return));

result->DataLen = dataLen;

result->Data = 0;

if(dataLen>0){

result->Data = malloc(dataLen);

memcpy(result->Data,data,dataLen);

}

return result;

}

void FreeLargeSturct(GetLargeStruct_return* ptr){

if(ptr != 0){

if(ptr->Data != 0 ){

free(ptr->Data);

}

free(ptr);

}

}

编译后产生的头文件

/* Created by "go tool cgo" - DO NOT EDIT. */

/* package c_references_to_go/sample3 */

/* Start of preamble from import "C" comments. */

#line 8 "Y:\\mygo\\src\\c_references_to_go\\sample3\\main.go"

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

typedef struct {

char* Data;

int DataLen;

} GetLargeStruct_return;

extern GetLargeStruct_return* CopyLargeSturct(char* data,int dataLen);

extern void FreeLargeSturct(GetLargeStruct_return* ptr);

#line 1 "cgo-generated-wrapper"

/* 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

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 GoInt32 GoInt;

typedef GoUint32 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_32_bit_pointer_matching_GoInt[sizeof(void*)==32/8 ? 1:-1];

typedef struct { const char *p; GoInt n; } GoString;

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 GetLargeStruct_return* GetLargeStruct(GoInt p0);

extern void FreeLargeStruct(GetLargeStruct_return* p0);

/* Return type for GetLargeStruct2 */

struct GetLargeStruct2_return {

char* r0;

GoInt r1;

};

extern struct GetLargeStruct2_return GetLargeStruct2(GoInt p0);

extern void FreeCBytes(char* p0);

#ifdef __cplusplus

}

#endif

3.2 C++ 代码

#include <Windows.h>

#include <stdio.h>

#include "sample3.h"

typedef GetLargeStruct_return* (*funcPtrGetLargeStruct)(GoInt p0);

typedef void (*funcPtrFreeLargeStruct)(GetLargeStruct_return* p0);

typedef struct GetLargeStruct2_return (*funcPtrGetLargeStruct2)(GoInt p0);

typedef void (*funcPtrFreeCBytes)(char* p0);

int main(){

HMODULE h = LoadLibraryA("sample3.dll");

if (NULL == h || INVALID_HANDLE_VALUE == h)

{

return -1;

}

funcPtrGetLargeStruct pfGetLargeStruct = (funcPtrGetLargeStruct)GetProcAddress(h,"GetLargeStruct");

funcPtrFreeLargeStruct pfFreeLargeStruct = (funcPtrFreeLargeStruct)GetProcAddress(h,"FreeLargeStruct");

if (pfGetLargeStruct)

{

GetLargeStruct_return* result = pfGetLargeStruct(5);

if (result)

{

printf("GetLargeStruct(5) return  %x %d\r\n",result->Data,result->DataLen);

if (pfFreeLargeStruct)

{

pfFreeLargeStruct(result);

}

}

}

funcPtrGetLargeStruct2 pfGetLargeStruct2 = (funcPtrGetLargeStruct2)GetProcAddress(h,"GetLargeStruct2");

funcPtrFreeCBytes pfFreeCBytes = (funcPtrFreeCBytes)GetProcAddress(h,"FreeCBytes");

if (pfGetLargeStruct)

{

GetLargeStruct2_return result = pfGetLargeStruct2(5);

printf("GetLargeStruct2(5) return  %x %d\r\n",result.r0,result.r1);

if (pfFreeCBytes)

{

pfFreeCBytes(result.r0);

}

}

FreeLibrary(h);

return 0;

}

运行之后的输出:

 

本文只讲述C/C++怎么调用golang程序,细节、注意事项及其他在后续随笔中介绍。

参考文献:

  1. C? Go? Cgo!      https://blog.golang.org/c-go-cgo
  2. Command cgo     https://golang.org/cmd/cgo/
  3. Cgo             https://github.com/golang/go/wiki/cgo
  4. cmd/cgo: Go type not supported in export: struct #18412

https://github.com/golang/go/issues/18412

  1. https://go.googlesource.com/go/+/master/src/cmd/cgo/doc.go

C/C++调用Golang 二的更多相关文章

  1. .Net组件程序设计之远程调用(二)

    .Net组件程序设计之远程调用(二) 激活模式 引用封送对象激活类型两种, 一种是客户端激活类型,一种是服务器端激活. 客户端激活对象 客户端激活方式:当客户端创建一个远程对象时,客户端得到的是一个新 ...

  2. C/C++调用Golang 一

    C/C++调用Golang 一 (开发环境: 操作系统: windows 7 32位操作系统 C++: visual studio 2010 Golang:go version go1.9 windo ...

  3. 从源码(编译)安装golang 二

    h1 { margin-top: 0.6cm; margin-bottom: 0.58cm; direction: ltr; color: #000000; line-height: 200%; te ...

  4. 记一次坑爹的golang 二维map判断问题

    记一次坑爹的golang 二维map判断问题 2018年10月18日 23:16:21 yinnnnnnn 阅读数:32更多 个人分类: golang   版权声明:本文为博主原创文章,未经博主允许不 ...

  5. 函数和常用模块【day04】:函数参数及调用(二)

    本节内容 1.为什么要有参数 2.返回值 3.有参数函数调用 一.为什么要有参数? 无参数实现 def CPU报警邮件() #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 def 硬盘报警邮件() ...

  6. Android的JNI调用(二)

    Android Studio 2.3在native下已经有了代码提示功能,按照提示下载相应组件就可以debug native代码. 一.Java调用JNI与JNI调用Java 1.1 C调用Java ...

  7. electron/nodejs实现调用golang函数

    https://www.jianshu.com/p/a3be0d206d4c 思路 golang 支持编译成c shared library, 也就是系统中常见的.so(windows下是dll)后缀 ...

  8. Golang 二维切片初始化

    package main import "fmt" func main() { // 方法0 row, column := 3, 4 var answer [][]int for ...

  9. golang 二维切片

    初始化: res := make([][length]int, length), 例如: res := make([][2]int, 10) fmt.Println(res) 输出: [[0 0] [ ...

随机推荐

  1. pyhton 关于 configparser 配置 模块 实践使用中碰到的坑

    今天做一个ATM的练习,想要用configparser模块,写一个配置文件,存放用户信息. 结果状况连连,叫苦不迭. 我用configparser模块,想要对配置文件,进行读.写.改.查 功能. 其中 ...

  2. thinkphp 使用插件异步上传图片或者文件

    使用tp做一些上传的功能,的确挺方便.但是在一些特殊情况下无法单独的使用tp的上传功能, 或者需要做一些比较酷炫的上传效果,这里就需要用到框架了. 我在这里使用的是uploadify上传插件. 首先需 ...

  3. require.js模块化写法

    模块化 模块就是实现特定功能的一组方法.只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块. 下述两种写法等价 exports 对象是当前模块的导出对象,用于导出模块公有方法和属性. ...

  4. request和response中文乱码问题后台处理办法

    request接收参数的中文乱码的处理: GET: 方法一:使用String的构造方法: new String(request.getParameter("传过来的name").g ...

  5. linux mysql定时备份

    项目需要定时备份数据库,以下是自己的操作笔记 1.检查磁盘空间 # df -h Filesystem Size Used Avail Use% Mounted on /dev/vda1 40G 3.6 ...

  6. 使用Dagger2做静态注入, 对比Guice.

    Dagger 依赖注入的诉求, 这边就不重复描述了, 在上文Spring以及Guice的IOC文档中都有提及, 既然有了Guice, Google为啥还要搞个Dagger2出来重复造轮子呢? 因为使用 ...

  7. 》》豆瓣API

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  8. 做ppt经常使用站点

    推荐一下做PPT经常使用的站点 一.字体 http://font.chinaz.com 二.ppt模板:演界网 三.图标 http://www.easyicon.net/

  9. 赵雅智_Swift(1)_swift简单介绍及类型

    Swift 是 iOS 和 OS X 应用开发的一门新语言. 假设你有 C 或者 Objective-C 开发经验, Swift 的非常多内容都是你熟悉的. Swift 的类型是在 C 和 Objec ...

  10. hdu5303Delicious Apples

    题意大概就是有n框苹果放在长度为L的环上,每框有ai个苹果.你有一个容量为k的框.要你从0点处出发,随意走.框满了就回到0点把苹果放在那里.继续走直到把苹果都拿完为止.问你最少要走多少路程. 首先贪心 ...