zoukankan      html  css  js  c++  java
  • calling c++ from golang with swig--windows dll(一)

    calling c++ from golang with swig--windows dll

    之前项目组开发的项目核心代码全部使用C++语言,新项目可能会引入golang,花了一天多时间研究了windows环境下golang调用C++动态链接库的方法。

    谷歌加百度之后,很快发现官方推荐的方法,在官方FAQ页面可以找到答案:

    https://golang.org/doc/faq

    Do Go programs link with C/C++ programs?

    There are two Go compiler implementations, gc and gccgo. Gc uses a different calling convention and linker and can therefore only be linked with C programs using the same convention. There is such a C compiler but no C++ compiler. Gccgo is a GCC front-end that can, with care, be linked with GCC-compiled C or C++ programs.

    The cgo program provides the mechanism for a “foreign function interface” to allow safe calling of C libraries from Go code. SWIG extends this capability to C++ libraries.

        上文的大意是:golang有两个编译器实现,gcgccgo。gc编译器使用一个不同的调用约定和链接器,因此只能链接使用相同约定的C程序;gccgo是一个GCC的前端,可以链接GCC编译的CC++程序。cgo程序提供了一个“外部函数接口”机制,允许在Golang中安全地调用C库。SWIG扩展了这个能力,使得Golang可以调用C++库。

        由官方的问答可以知道,如果Golang要调用C++库,需要借助gccgoswig

        SWIG 是一个非常优秀的开源工具,支持您将 C/C++ 代码与任何主流脚本语言相集成。SWIG的一个入门介绍可以参考《开发人员 SWIG 快速入门

    http://www.ibm.com/developerworks/cn/aix/library/au-swig/

      

    SWIG官方网址 http://www.swig.org/

    Github上的开源代码仓库: https://github.com/swig/swig

    github上看介绍,swig支持很多种语言:

    SWIG is a compiler that integrates C and C++ with languages

    including Perl, Python, Tcl, Ruby, PHP, Java, C#, D, Go, Lua,Octave, R, Scheme (Guile, MzScheme/Racket, CHICKEN), Scilab, Ocaml, Modula-3, Common Lisp (CLISP, Allegro CL, CFFI, UFFI) and Pike. SWIG can also export its parse tree into XML and Lisp s-expressions.

    Swig最新的发布版本是2017128日发布的rel-3.0.12

    Github上下载的发布包没有预编译的swig.exe;自己编译比较麻烦,所以直接从http://www.swig.org/download.html 页面下载最新发布包,

     

    根据提示,从第二个链接下载,包含了预编译的可执行程序。

    解压缩后,文件夹下有swig.exe,后面会看到需要借助这个工具生成代码。Golang借助swig调用C++还是非常简单的,看下Examples/go目录下的示例就可以入门了。

    用浏览器打开Examplesgoindex.html看下文字说明,

     

    • When using the gccgo compiler, the steps look like this:

    % swig -go -cgo interface.i

    % mkdir -p gopath/src/interface

    % cp interface_wrap.c interface_wrap.h interface.go gopath/src/interface

    % GOPATH=`pwd`/gopath

    % export GOPATH

    % cd gopath/src/interface

    % go build

    % gccgo -c $(SRCDIR)/runme.go

    % gccgo -o runme runme.o interface.a

    这个文档介绍的使用说明与软件版本不太相符,文档更新落后软件太多。实际上只要一条命令就可以了。下面来介绍下go借助swig调用C++的方法,以Examplesgoclass为例:

    (我的电脑是windows 7 X64golang也是64位)

    将下载的swigwin-3.0.12.zip包解压到D盘,然后将D:swigwin-3.0.12swig.exe加入环境变量。

    打开cmd,工作目录切换到D:swigwin-3.0.12Examplesgoclass

    执行命令 swig -c++ -cgo -go -intgosize 64  example.i

    成功执行后在D:swigwin-3.0.12Examplesgoclass生成两个文件,example.goexample_wrap.cxx,这两个文件便是是在go中调用的包,runme.go属于main包,golang只运行一个文件夹对应一个包,所以需要在D:swigwin-3.0.12Examplesgoclass下创建example子文件夹,将example.goexample_wrap.cxxclass.cxxexample.h四个文件移动到D:swigwin-3.0.12Examplesgoclassexample路径下。

    命令行中执行go build, 编译成功后,在D:swigwin-3.0.12Examplesgoclass文件夹中生成class.exe,执行class.exe输出如下内容:

     

    回过头来看下详细的开发步骤。

    首先编写供golang调用的c++源文件,

      example.hclass.cxx

    /* File : example.h */

    class Shape {

    public:

      Shape() {

        nshapes++;

      }

      virtual ~Shape() {

        nshapes--;

      }

      double  x, y;

      void    move(double dx, double dy);

      virtual double area() = 0;

      virtual double perimeter() = 0;

      static  int nshapes;

    };

    class Circle : public Shape {

    private:

      double radius;

    public:

      Circle(double r) : radius(r) { }

      virtual double area();

      virtual double perimeter();

    };

    class Square : public Shape {

    private:

      double width;

    public:

      Square(double w) : width(w) { }

      virtual double area();

      virtual double perimeter();

    };

    该头文件包含了一个基类和两个派生类的声明。

     class.cxx包含了对应的源码实现:

    /* File : class.cxx */

    #include "example.h"

    #define M_PI 3.14159265358979323846

    /* Move the shape to a new location */

    void Shape::move(double dx, double dy) {

      x += dx;

      y += dy;

    }

    int Shape::nshapes = 0;

    double Circle::area() {

      return M_PI*radius*radius;

    }

    double Circle::perimeter() {

      return 2*M_PI*radius;

    }

    double Square::area() {

      return width*width;

    }

    double Square::perimeter() {

      return 4*width;

    }

    接下来需要定义swig工具生成代码需要的输入文件example.i,只需指定包名和包含的c++头文件,相当简单。

    /* File : example.i */

    %module example

    %{

    #include "example.h"

    %}

    /* Let's just grab the original header file here */

    %include "example.h"

    接下来就是在命令行中输入swig -c++ -cgo -go -intgosize 64  example.i 生成golang调用的包装代码:example.goexample_wrap.cxx;example_wrap.cxx文件将c++语法隐藏在C函数内部,对外暴露c接口,example.go调用example_wrap.cxx中的c函数,声明并实现golang版的接口。

    runme.go中引入example包  . "./example"

    c := NewCircle(10) 简短变量声明并定义了SwigcptrCircle类型的变量,SwigcptrCircle实现了Circle 接口。SwigcptrCircle的基础类型是uintptrNewCircle返回的值实际上就是C++对象的this指针,通过SwigcptrCircle类型的值调用Circle 接口定义的方法,实质上是通过example_wrap.cxx暴露的C函数及“this指针”在函数内部调用将指针转换成c++对象指针然后调用相应的方法。

    type Circle interface {

    Swigcptr() uintptr

    SwigIsCircle()

    Area() (_swig_ret float64)

    Perimeter() (_swig_ret float64)

    SetX(arg1 float64)

    GetX() (_swig_ret float64)

    SetY(arg1 float64)

    GetY() (_swig_ret float64)

    Move(arg1 float64, arg2 float64)

    SwigIsShape()

    SwigGetShape() Shape

    }

    Golang的角度看,借助swig生成的包装类型,调用c++类方法就像调用golang内部的接口一样,非常简单易用。

       在实际的项目开发中,包含了大量的C++头文件和源码文件,通常还会引入大量的第三方库文件。我们的项目中,开发人员编写的代码超过五十万行,还有大量的第三方库,例如boostthriftredis等,所有C++代码量超过几百万行,编译后的输出包含大量的动态链接库及少量的可执行程序;在服务端,进行跨语言混合开发无外乎涉及两方面,实现库时提供不同语言的实现,例如使用thrift框架可以生成C++JavaGolang等语言的客户端、服务端版本,不同的组件可实现跨语言跨平台调用;另外一种主要涉及跨语言库调用。Examplesgo目录下的示例没有讲述如何实现golang调用C++ dll,并且在官方文档中也没有找到如何实现。(通过D:swigwin-3.0.12DocManualindex.html 可以查看最新版的3.0文档)。Google加百度没有找到一篇文档能够详细地指出windows环境下golang调用c++ dll的方法,在不断地编码尝试下,最终自己搞定。具体细节在后两篇文档中指出。

  • 相关阅读:
    面试官问你如何解决web高并发这样回答就好了
    react-native页面间传递数据的几种方式
    图片上传知识点梳理
    使用Vue CLI 3将基于element-ui二次封装的组件发布到npm
    OpenCV学习笔记09--通过cvPtr2D或指针算法绘制图形
    [code segments] OpenCV3.0 SVM with C++ interface
    LINUX设备驱动程序笔记(一)设备驱动程序简单介绍
    redis 简单安装使用
    Linux常见压缩命令
    计算机网络13--网络应用的体系结构
  • 原文地址:https://www.cnblogs.com/majianguo/p/6551153.html
Copyright © 2011-2022 走看看