いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-520 (cgoメモ-14)(Goの関数をCの世界に公開 (export))

関連記事

Goメモ-502 (cgoメモ-01)(cgoヘッダ) - いろいろ備忘録日記

Goメモ-506 (cgoメモ-02)(cgoヘッダ) - いろいろ備忘録日記

Goメモ-507 (cgoメモ-03)(C.int) - いろいろ備忘録日記

Goメモ-508 (cgoメモ-04)(C言語の構造体) - いろいろ備忘録日記

Goメモ-509 (cgoメモ-05)(C.CString)(Cの文字列) - いろいろ備忘録日記

Goメモ-510 (cgoメモ-06)(C.GoString)(Cの文字列をGoの文字列へ) - いろいろ備忘録日記

Goメモ-511 (cgoメモ-07)(C.CBytes)([]byteをCのバイト列に) - いろいろ備忘録日記

Goメモ-512 (cgoメモ-08)(C.GoBytes)(Cのバイト列をGoの[]byteへ) - いろいろ備忘録日記

Goメモ-514 (cgoメモ-09)(C.GoStringN)(C.GoStringのサイズ指定版) - いろいろ備忘録日記

Goメモ-515 (cgoメモ-10)([]byteを(void *)へ変換) - いろいろ備忘録日記

Goメモ-516 (cgoメモ-11)([]byteを(char *)へ変換) - いろいろ備忘録日記

Goメモ-518 (cgoメモ-12)(Cのmallocをcgo経由で呼び出し) - いろいろ備忘録日記

Goメモ-519 (cgoメモ-13)(ポインタ演算) - いろいろ備忘録日記

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。

今回から複数回に渡って cgo についてメモしていこうと思います。

cgo は、文字通りGoからCにアクセスすることが出来るようになるものなのですが、とても便利な反面、結構クセが強いのでメモでも残しておかないとすぐ頭から消えてしまいそうだなって思いました。

Cgo is not Go

という格言があったりするので、Go界隈で標準で推奨されていない技術かもしれません。が、実務ではC言語で作成されたライブラリなどは山のようにあります。んで、プロジェクトの方針でGoで作り直すことも出来ない場合も多々あります。そのような場合に非常に便利です。

これからのサンプルは以下のリポジトリにアップしてありますので、良ければご参考ください。

github.com

今回は Go側の関数をCの世界に公開 (export) について。

cgoでは、Goの関数直上のコメントにて

//export function-name

とすると、この関数をC側にエクスポートすることが出来ます。つまり、C言語の方からGoで定義された関数を呼べるようになります。

今回のサンプルは、少しファイル数も多くなっていますので、サンプルの概要は以下のREADME.mdを見て頂いたほうが早いです。

try-golang-cgo/14.C_ExportGoFunc

サンプル

sample.h

#pragma once

extern void go_start();
extern void go_end();
extern void go_main(int id, void *data, size_t length);

sub.go

package main

/*
#include <stdio.h>
#include <stdlib.h>
#include "sample.h"
*/
import "C"
import (
    "log"
    "unsafe"
)

//export go_start
func go_start() {
    log.Println("[Go][go_start] start")
}

//export go_end
func go_end() {
    log.Println("[Go][go_end  ] end")
}

//export go_main
func go_main(cId C.int, cData unsafe.Pointer, cLength C.size_t) {
    var (
        id     = int(cId)
        length = int(cLength)
        data   = C.GoStringN((*C.char)(cData), C.int(length))
    )

    log.Println("[Go][go_main ] called")
    log.Printf("[Go][go_main ] id=%d, data=%q, length=%d\n", id, data, length)
}

main.go

package main

/*
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sample.h"

void c_run() {
   go_start();

   int id = 100;
   char data[] = "hello world";
   size_t szData = strnlen(data, 20);

   go_main(id, data, szData);

   go_end();
}
*/
import "C"

func main() {
    C.c_run()
}

Taskfile.yml

# https://taskfile.dev

version: '3'

tasks:
  default:
    cmds:
      - go run .
  build:
    cmds:
      - go build -o app
      - nm ./app | grep -E "T (go_start|go_end|go_main)$"

実行

$ task
task: [default] go run .
2024/12/12 02:30:56 [Go][go_start] start
2024/12/12 02:30:56 [Go][go_main ] called
2024/12/12 02:30:56 [Go][go_main ] id=100, data="hello world", length=11
2024/12/12 02:30:56 [Go][go_end  ] end

$ task build
task: [build] go build -o app
task: [build] nm ./app | grep -E "T (go_start|go_end|go_main)$"
000000000049b6d0 T go_end
000000000049b740 T go_main
000000000049b660 T go_start

参考情報

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。