cgo 調用 c-- 動態庫 so 文件

【導讀】golang 如果依賴外部動態鏈接庫要如何引入?本文做了詳細介紹。

如果你有一個 c++ 做的動態鏈接庫。so 文件,而你只有一些相關類的聲明,那麼你如何用 c 調用呢,

C++ 創始人在編寫 C++ 的時候,C 語言正盛行,他不得不讓 C++ 兼容 C。C++ 最大的特性就是封裝,繼承,多態,重載。而這些特性恰恰是 C 語言所不具備的。至於多態,核心技術是通過虛函數表實現的,其實也就是指針。而對於重載,與 C 語言相比,其實就是編譯方式不同而已:C++ 編譯方式和 C 編譯方式。

對於函數調用,編譯器只要知道函數的參數類型和返回值以及函數名就可以進行編譯連接。那麼爲了讓 C 調用 C++ 接口或者是說 C++ 調用 C 接口,就必須是調用者和被調用者有着同樣的編譯方式。

這既是 extern “C” 的作用,extern “C” 是的程序按照 C 的方式編譯。

C++ 代碼

/var/www/cgo_test/student.h

#include <iostream>
using namespace std;

class Student {
public:
    Student(){}
    ~Student(){}
    void Operation();
    void SetName(string name);
    string name;
};

/var/www/cgo_test/student.cpp

using namespace std;
void Student::Operation()
{
    cout << "Hi my name is " << name <<endl;
}
void Student::SetName(string name1)
{
        name = name1;
}

/var/www/cgo_test/interface.h

#ifdef __cplusplus
extern "C"{
#endif
 
void *stuCreate();
void initName(void *, char* name);
void getStuName(void *);
void getName();
 
#ifdef __cplusplus
}
#endif

/var/www/cgo_test/interface.cpp

#include "student.h"
#include "interface.h"
 
#ifdef __cplusplus
extern "C"{
#endif
 
void *stuCreate()
{
        return new Student(); //構造
}

void getStuName(void *p)
{
    static_cast<Student *>(p)->Operation();
}

void initName(void *p, char* name1)
{
    static_cast<Student *>(p)->SetName(name1);
}

void getName()
{
        Student obj;
        obj.Operation();
}
 
#ifdef __cplusplus
}
#endif

C 代碼

#include "interface.h"
 
int main()
{
    void *p = stuCreate();
    char *name = "test";
    initName(p, name);
    getStuName(p);
    getName();
    return 0;
}

編譯流程

  1. 首先生成動態庫
g++ student.cpp interface.cpp -fPIC -shared -o libstu.so
  1. 編譯 main.c
gcc main.c -L.  -lstu
  1. 運行。/a.out ./a.out

到這裏,c 已經成功調用 c++ 代碼

執行結果

Hi my name is test Hi my name is

其中,上面步驟 2 時可以會報錯

error while loading shared libraries: libstu.so: cannot open shared object file: No such file or directory

解決方法 more

  1. 增加配置路徑

vim /etc/ld.so.conf.d/test.conf

/var/www/cgo_test
  1. /sbin/ldconfig

  2. ldd a.out 查看已經可以成功鏈接到 libstu.so

go 代碼

/var/www/cgo_test/test.go

// +build linux
// +build amd64
// +build !noptlogin

package  main

/*
#cgo CFLAGS: -I./
#cgo LDFLAGS: -L./ -lstu 
#include <stdlib.h>
#include <stdio.h>
#include "interface.h" //非標準 c 頭文件,所以用引號
*/
import "C"

import(
    "unsafe"
)

func main() {
    name := "test!"
    cStr := C.CString(name)
    defer C.free(unsafe.Pointer(cStr))
    obj := C.stuCreate()
    C.initName(obj, cStr)
    C.getStuName(obj)
}

然後直接執行 go run test.go 時會報錯

package main: build constraints exclude all Go files in /var/www/cgo_test

/usr/bin/ld: skipping incompatible ./lib/libstu.so when searching for -lstu

/usr/bin/ld: cannot find -lstu

collect2: error: ld returned 1 exit status

解決方法

go 可能默認不支持 cgo,go env 可查看相關配置,可通過顯示指定 CGO_ENABLED=1GOARCH=amd64 解決

 GOARCH=amd64 GOOS=linux CGO_ENABLED=1 go ru n test.go

執行結果

Hi my name is test!

轉自:

aibenlin.com/golang/2019/09/05/golang-cgo_call_c++.html

Go 開發大全

參與維護一個非常全面的 Go 開源技術資源庫。日常分享 Go, 雲原生、k8s、Docker 和微服務方面的技術文章和行業動態。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/RhYORrhuRkcPr_k_ngb6tw