zoukankan      html  css  js  c++  java
  • 使用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.
    ");
    %}
    
    %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] = '';
    
          /*Work well*/
          _gostring_ *ps = &a[i];
          $1[i] = (char*) malloc(ps->n + 1);
          memcpy($1[i], ps->p, ps->n);
          $1[i][ps->n] = '';
        }
        $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
    ", 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 密码: ksnq

  • 相关阅读:
    团队开发——描绘用户场景
    学习EXTJS6(2)“Hello Usegear”
    D2007从win7升级到win10下的莫名其妙问题。
    学习EXTJS6(1)安装环境
    raize5的修改。
    EurekaLog是什么鬼?
    EditorLineEnds.ttr的困扰
    dev的汉化
    cxGrid主从表删除从表记录的困惑
    痛苦的Windows下的temp目录
  • 原文地址:https://www.cnblogs.com/dongc/p/6896850.html
Copyright © 2011-2022 走看看