使用swig工具爲go語言與c++進行交互

環境:

  centos_7_x86_x64,gcc_4.8.5linux

1、安裝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參數,具體見官方文檔。ios

 

2、安裝golang

  1. 安裝go

yum install -y go

  2. 驗證安裝是否成功

go version

 

3、建立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使用。c++

 

4、使用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

  有幾個點咱們須要注意:git

    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.cxxgithub

  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.sogolang

 

4、在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都屬於同一個包。centos

  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

 

資源下載連接: http://pan.baidu.com/s/1jIA5mXS 密碼: ksnqapp