使用swig工具为go语言与c++进行交互
·
使用swig工具为go语言与c++进行交互
环境:
centos_7_x86_x64,gcc_4.8.5
一、安装swig
1. 安装pcre
yum install -y pcre pcre-tools pcre-devel
2. 安装yacc
yum install -y byacc
3. 下载swig-rel-3.0.12.tar.gz
4. 解压到任意目录下,并生成configure文件
tar -xvzf swig-rel-3.0.12.tar.gz cd swig-rel-3.0.12 ./autogen.sh
5. 生成Makefile文件
./configure
6. 编译和安装
make && make install
7. 验证安装是否成功

swig -version
这里说一下为什么要编译swig源码来进行安装,在yum上安装swig的版本比较低,而较低版本的swig不支持-cgo参数,具体见官方文档。
二、安装golang
1. 安装go
yum install -y go
2. 验证安装是否成功

go version
三、创建C++动态链接库
1. 编写test_cpp.h文件

#ifndef _TEST_CPP_H_
#define _TEST_CPP_H_
#include <stdint.h>
#include <string>
/// 回调类
class ICallback {
public:
virtual void notify(const std::string& s) = 0;
};
/// 测试类
class TestCall {
public:
static TestCall* Create() { return new TestCall(); }
void SetCallback(ICallback* callback) { callback_ = callback; }
int32_t Test(const std::string& s);
private:
TestCall() : callback_(NULL) {}
ICallback* callback_;
};
#endif

2. 编写test_cpp.cpp文件

#include <iostream>
#include "test_cpp.h"
int32_t TestCall::Test(const std::string & s) {
std::cout << "TestCall::Test(" << s << ")" << std::endl;
if (callback_) {
callback_->notify(s);
}
return 0;
}

3. 编写CMakeLists.txt文件
cmake_minimum_required(VERSION 2.8)
project(test_cpp C CXX)
set(SRC_LISTS test_cpp.cpp)
add_library(test_cpp SHARED ${SRC_LISTS})
4. 编译生成动态链接库libtest_cpp.so
mkdir cmake cd cmake cmake .. make
由于在golang中不能直接使用C++函数,所以我们稍后会使用swig工具,生成C函数提供给golang使用。
四、使用swig从C++生成C函数接口
1. 编写定义文件go_test_cpp.swigcxx

/* Copyright 2011 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */
/* An example of writing a C++ virtual function in Go. */
%module(directors="1") go_test_cpp
%init %{
//printf("Initialization rms done.\n");
%}
%typemap(gotype) (char **ppInstrumentID, int nCount) "[]string"
%typemap(in) (char **ppInstrumentID, int nCount)
%{
{
int i;
_gostring_* a;
$2 = $input.len;
a = (_gostring_*) $input.array;
$1 = (char **) malloc (($2 + 1) * sizeof (char *));
for (i = 0; i < $2; i++) {
/* Not work */
//_gostring_ *ps = &a[i];
//$1[i] = (char *) ps->p;
//$1[i][ps->n] = '\0';
/*Work well*/
_gostring_ *ps = &a[i];
$1[i] = (char*) malloc(ps->n + 1);
memcpy($1[i], ps->p, ps->n);
$1[i][ps->n] = '\0';
}
$1[i] = NULL;
}
%}
%typemap(argout) (char **ppInstrumentID, int nCount) "" /* override char *[] default */
%typemap(freearg) (char **ppInstrumentID, int nCount)
%{
{
int i;
for (i = 0; i < $2; i++)
{
free ($1[i]);
}
free($1);
}
%}
/* 在复杂的企业环境中,可能有一些 C/C++ 头文件定义了您希望向脚本框架公开的全局变量和常量。
* 在接口文件中使用 %include <header.h> 和 %{ #include <header.h> %},可解决在头文件中重复所有元素的声明的问题。
*/
/* Includes the header files in the wrapper code */
%header %{
#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(WIN32)
#include "test_cpp.h"
#else
#include "test_cpp.h"
#endif
%}
/* Parse the header files to generate wrappers */
%include "std_string.i"
%feature("director") ICallback;
#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(WIN32)
%include "./../cpp/test_cpp.h"
#else
%include "./../cpp/test_cpp.h"
#endif

有几个点我们需要注意:
1.1 指定生成go源文件中的包名
%module(directors="1") go_test_cpp
1.2 指定生成C++源文件中的include代码

%header %{
#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(WIN32)
#include "test_cpp.h"
#else
#include "test_cpp.h"
#endif
%}

1.3 可以让go支持继承某个C++类
%feature("director") ICallback;
1.4 指定需要解析C++头文件,生成go和C++的包装代码

#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(WIN32) %include "./../cpp/test_cpp.h" #else %include "./../cpp/test_cpp.h" #endif

2. 生成go源文件和C++源文件
swig -go -cgo -intgosize 64 -c++ ./go_test_cpp.swigcxx
总共生成了3个文件:go_test_cpp.go、go_test_cpp_wrap.h和go_test_cpp_wrap.cxx
3. 在该目录下编写CMakeLists.txt文件

cmake_minimum_required(VERSION 2.8)
project(go_test_cpp C CXX)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../cpp)
set(SRC_LISTS go_test_cpp_wrap.cxx)
add_library(go_test_cpp STATIC ${SRC_LISTS})

4. 编译生成静态库libgo_test_cpp.a
mkdir cmake cd cmake cmake .. make
最后编译的静态库是给go使用的,大致调用流程是:go <=> libgo_test_cpp.a <=> libtest_cpp.so
四、在go中使用C函数接口
1. 将go_test_cpp.go文件拷贝到go工程目录下的go_test_cpp目录下
2. 编写libgo_test_cpp.go文件

// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package go_test_cpp
//#cgo linux LDFLAGS: -fPIC -L${SRCDIR}/../../cpp/cmake -L${SRCDIR}/../../swig/cmake -Wl,-rpath=${SRCDIR}/../../cpp/cmake -ltest_cpp -lgo_test_cpp -lstdc++
//#cgo linux CPPFLAGS: -fPIC -I.
import "C"

链接libtest_cpp.so和libgo_test_cpp.a模块;应该将libgo_test_cpp.go文件保存在go_test_cpp目录下,它和go_test_cpp.go都属于同一个包。
3. 编写main.go文件调用C函数

package main
import(
"fmt"
"unsafe"
"time"
"./go_test_cpp"
)
type my_callback struct {
go_test_cpp.ICallback
}
func (this my_callback) Notify(arg2 string) {
fmt.Printf("c++ Notify:%s\n", arg2)
}
func main() {
cb := go_test_cpp.NewDirectorICallback(my_callback{})
test := go_test_cpp.TestCallCreate()
test.SetCallback(cb)
res_ptr := test.Test("Hello World!").Swigcptr()
res := *(*int32)(unsafe.Pointer(res_ptr))
if res == 0 {
fmt.Println("Test success!")
} else {
fmt.Println("init failed[", res, "]!")
}
go_test_cpp.Swig_free(res_ptr)
time.Sleep(time.Second*1)
fmt.Println("end")
}

4. 运行main.go

go run main.go
更多推荐
所有评论(0)