1. ホーム
  2. スクリプト・コラム
  3. ゴラン

囲碁言語におけるCGOの実践的な使用

2022-02-15 16:30:06

       部門製品のビジネス関数は、Golangによって開発されたが、いくつかの関数は、これらのように、ネットSNMP、bfdプロトコルなどのC言語で書かれている、あなたが再編集するGO言語を使用する場合、両方の実装の複雑さもかなり長い時間がかかる、良いことは、GO言語は、直接大幅に効率を改善し、削減GOコード内のCライブラリ機能を呼び出すことができるので、CGOメカニズムを提供していますさらに、それはまた非常に強力ですC言語でGO関数の呼び出しをサポートします。

1. GoでのC関数呼び出しの例。

package main
 
//
// The referenced C header file needs to be declared in a comment, immediately after the comment needs to have import "C", and there should be no space between this line and the comment
//
 
/*
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void myprint(char* s) {
	printf("%s\n", s);
}
*/
import "C"
 
 
import (
	"fmt"
	"unsafe"
)
 
func main() {
	// Strings created with C.CString need to be freed manually.
	cs := C.CString("Hello World\n")
	C.myprint(cs)
	C.free(unsafe.Pointer(cs))
	fmt.Println("call C.sleep for 3s")
	C.sleep(3)
	return
}

実行します。

2. C言語ライブラリ関数のGo言語呼び出し。

hello.c

#include <stdio.h>
void hello()
{
    printf("hello world\n"); 
}

hello.h

#ifndef HELLO_H
#define HELLO_H
 
void hello(void);
#endif

コンパイルします。

gcc -c hello.c
ar -cru libhello.a hello.o

package main
 
//Use #cgo to define the library path
 
 
/*
#cgo CFLAGS: -I .
#cgo LDFLAGS: -L . -lhello
#include "hello.h"
*/
import "C"
 
func main() {
	C.hello()
}

実行します。

3. Go は C 言語で使用するための関数をエクスポートします。

main.go

package main
 
//
//#include <stdio.h>
//int add(int a, int b);
//
import "C"
 
import (
	"fmt"
)
 
// When using export, no other c functions can be defined in the same file, or an error will be reported.
//Use export to export functions to c calls.
 
//export GoAdd
func GoAdd(a, b int) int {
	return a + b
}
 
func main() {
	a := C.add(1, 2)
	fmt.Printf("C.add(1,2) return %d\n", a)
}

cfunc.go

package main
 
//
//int GoAdd(int a, int b); 
//
//int add(int a, int b)
//{
// return GoAdd(a, b);
//}
//
import "C"

実行します。

4. Go は、c で使用するための関数ポインタをエクスポートします。

もう一つ使い方があって、こちらの方がよく使います。それは、関数ポインタを渡すことです。GOの関数はアドレス指定ができないので、次の例のように、変換操作をするための中間関数を書く必要があるのです。

clibrary.c

#include <stdio.h>
 
#include "clibrary.h"
 
// the argument is a function pointer
void some_c_func(callback_fcn callback)
{
	int arg = 2;
	printf("C.some_c_func(): calling callback with arg = %d\n", arg);
	int response = callback(2);
	printf("C.some_c_func(): callback responded with %d\n", response);
}

 clibrary.h

#ifndef CLIBRARY_H
#define CLIBRARY_H
//define function pointer
typedef int (*callback_fcn)(int);
void some_c_func(callback_fcn);
#endif

囲碁のコードです。

package main
 
/*
#cgo CFLAGS: -I .
#cgo LDFLAGS: -L . -lclibrary
#include "clibrary.h"
int callOnMeGo_cgo(int in); // declare
*/
import "C"
 
import (
	"fmt"
	"unsafe"
)
 
//export callOnMeGo
func callOnMeGo(in int) int {
	return in + 1
}
 
func main() {
	fmt.Printf("Go.main(): calling C function with callback to us\n")
 
    //use unsafe.Pointer conversion
	C.some_c_func((C.callback_fcn)(unsafe.Pointer(C.callOnMeGo_cgo)))
}

中間的な機能。

package main
 
/*
 
#include <stdio.h>
int callOnMeGo(int);
 
// The gateway function
int callOnMeGo_cgo(int in)
{
	printf("C.callOnMeGo_cgo(): called with arg = %d\n", in);
    // Call the GO function
	return callOnMeGo(in);
}
*/
import "C"

実行します。

開発メモです。

1. コメントとインポート "C" の間に空白行がない。

2. C.CString関数を使って、GoStringを手動で解放してCStringに変換してください。

3. CGOはprintfのような可変パラメータを使用する関数をサポートしていません。使用したい場合は、ラッパー関数m'yprintfを書き、パラメータを渡して呼び出すとよいでしょう。

4. Go は //export を使って関数を C にエクスポートすることをサポートしていますが、 注意すべき点は、export と同じファイル内で c の関数を定義してはいけないということです。

multiple definition of "xxx" compile error, if the function is very tiny, another way to declare the function is using static inline, as follows.

package gocallback
 
import (
	"fmt"
	"sync"
)
 
/*
extern void go_callback_int(int foo, int p1);
// normally you will have to define function or variables
// in another separate C file to avoid the multiple definition
// errors, however, using "static inline" is a nice workaround
// for simple functions like this one.
static inline void CallMyFunction(int foo) {
	go_callback_int(foo, 5);
}
*/
import "C"
 

参考

1.  https://github.com/golang/go/wiki/cgo

Go言語でのCGOの使用に関するこの記事は以上です。Go言語でのCGOの使用については、Script Houseで過去の記事を検索するか、引き続き以下の関連記事を参照してください。