zoukankan      html  css  js  c++  java
  • Golang 调用 C/C++,例子式教程

    大部分人学习或者使用某样东西,喜欢在直观上看到动手后的结果,才会有继续下去的兴趣。

    前言:

    Golang 调用 C/C++ 的教程网上很多,就我目前所看到的,个人见解就是比较乱,坑也很多。希望本文能在一定程度上,做到更通俗明了。

    下面 golang 简称 go , 一如既往,少说废话,我们现在开始。


    go 调用 c/c++ 函数的实现方式有:

    • 直接嵌套在go文件中使用,最简单直观的
    • 导入动态库 .so 或 dll 的形式,最安全但是很不爽也比较慢的
    • 直接引用 c/c++ 文件的形式,层次分明,容易随时修改看结果的

    第三个直接引用 c/c++ 文件的形式 是我要介绍的重点。

    需要的环境支持

    • Linux 具备 gcc 与 g++ 即可
    • Windows 需要安装 mingw,否则编译时会有这类错:cannot find -lmingwex
    • Mac 参考 Linux

    1,直接嵌套在go文件

    package main
    /*
    // C 标志io头文件,你也可以使用里面提供的函数
    #include <stdio.h> 
    
    void pri(){
    	printf("hey");
    }
    
    int add(int a,int b){
    	return a+b;
    }
    */
    import "C"  // 切勿换行再写这个
    
    import "fmt"
    
    func main() {
    	fmt.Println(C.add(2, 1))
    }

    上面的代码,直接拷贝运行就能输出结果:3

    结论:

    • 但凡要引用与 c/c++ 相关的内容,写到 go 文件的头部注释里面
    • 嵌套的 c/c++ 代码必须符合其语法,不与 go 一样
    • import "C" 这句话要紧随,注释后,不要换行,否则报错
    • go 代码中调用 c/c++ 的格式是: C.xxx(),例如 C.add(2, 1)

    2,导入动态库 .so 或 .dll 的形式

    假设项目目录如下

    |-project
    |  |-lib
    |  |  |-libvideo.dll
    |  |  |-libvideo.so
    |  |-include
    |  |  |-video.h
    |  |-src
    |  |  |-main.go

    头文件 .h 如下面这样

    //video.h
    #ifndef VIDEO_H
    #define VIDEO_H
    void exeFFmpegCmd(char* cmd); // 声明
    #endif

    源文件 .c 如下面这样

    #include <stdio.h>
    #include "video.h"
    
    void exeFFmpegCmd(char* cmd){ // 实现
        // ....
        printf("finish");
    }

    使用 gcc 或 g++ 生成 .so库,或 win 下生成 dll

    例如: gcc video.c -fPIC -shared -o libvideo.so

    最后 main.go

    把动态库放到一个你喜欢的目录,也可以放到当前项目里面,像上面列出的例子一样。再引用

    package main
    
    /*
    
    #cgo CFLAGS: -Iinclude
    
    #cgo LDFLAGS: -Llib -llibvideo
    
    #include "video.h"
    
    */
    import "C"
    
    import "fmt"
    
    func main() {
        cmd := C.CString("ffmpeg -i ./xxx/*.png ./xxx/yyy.mp4")
        C.exeFFmpegCmd(&cmd)
    }

    先回答为什么说这种是最安全的和最不爽的?原因如下:

    • 动态库破解十分困难,如果你的 go 代码泄露,核心动态库没那么容易被攻破
    • 动态库会在被使用的时候被加载,影响速度
    • 操作难度比方式一麻烦不少

    结论

    • CFLAGS: -I路径 这句话指明头文件所在路径,-Iinclude 指明 当前项目根目录的 include 文件夹
    • LDFLAGS: -L路径 -l名字 指明动态库的所在路径,-Llib -llibvideo,指明在 lib 下面以及它的名字 video
    • 如果动态库不存在,将会爆找不到定义之类的错误信息

    3,直接引用 c/c++ 文件的形式 (重点)

    假设项目目录如下

    |-util
    |  |-util.h
    |  |-util.c
    |  |-util.go

    util.h

    int sum(int a,int b);

    util.c

    #include "util.h"
    int sum(int a,int b){
        return (a+b);
    }

    util.go

    package util
    
    /*
    #include "util.c"
    */
    import "C"
    
    import "fmt"
    
    func GoSum(a,b int) int {
        s := C.sum(C.int(a),C.int(b))
        fmt.Println(s)
    }

    这样调用 main.go

    package main
    
    func main(){
        util.GoSum(4,5)
    }

    第三种方式便是如此简洁明了

    最后,补充一下,一般需要 go 调用 c/c++ 的,主要是使用一些著名的开源库,例如 ffmpegopencv,等这些源码是基于 c/c++ 语言的,除此之外还有一个很重要的点,便是运行速度!

  • 相关阅读:
    适配器模式(2)
    设计模式之6大设计原则(1)
    Mybatis框架基础支持层——反射工具箱之MetaClass(7)
    Mybatis框架基础支持层——反射工具箱之实体属性Property工具集(6)
    Mybatis框架基础支持层——反射工具箱之对象工厂ObjectFactory&DefaultObjectFactory(5)
    Mybatis框架基础支持层——反射工具箱之泛型解析工具TypeParameterResolver(4)
    Guava动态调用方法
    数据库的数据同步
    springboot(二十二)-sharding-jdbc-读写分离
    springboot(二十一)-集成memcached
  • 原文地址:https://www.cnblogs.com/Cruise-Yang/p/11534151.html
Copyright © 2011-2022 走看看