いろいろ備忘録日記

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

Goメモ-641 (cgoメモ-34)(C側で定義された配列の操作)(アクセサ関数を用意)

関連記事

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)(ポインタ演算) - いろいろ備忘録日記

Goメモ-520 (cgoメモ-14)(Goの関数をCの世界に公開 (export)) - いろいろ備忘録日記

Goメモ-522 (cgoメモ-15)(Goでsoファイルを作成してC言語から呼び出し) - いろいろ備忘録日記

Goメモ-525 (cgoメモ-16)(C側にて関数ポインタを引数に要求する関数にGo側で定義した関数を設定) - いろいろ備忘録日記

Goメモ-526 (cgoメモ-17)(cgoとdlopen関数を使って既存ライブラリの呼び出しをフックする) - いろいろ備忘録日記

Goメモ-527 (cgoメモ-18)(cgoを利用している場合のinit関数について) - いろいろ備忘録日記

Goメモ-528 (cgoメモ-19)(cgoを利用して作成したsoファイル経由でのinit関数の呼び出し) - いろいろ備忘録日記

Goメモ-529 (cgoメモ-20)(C言語のNULLをcgoから渡す) - いろいろ備忘録日記

Goメモ-530 (cgoメモ-21)(CGOヘッダーで指定出来るCFLAGS, LDFLAGS) - いろいろ備忘録日記

Goメモ-531 (cgoメモ-22)(CGOで利用するコンパイラを変更する) - いろいろ備忘録日記

Goメモ-532 (cgoメモ-23)(CGOヘッダで使えるSRCDIR変数) - いろいろ備忘録日記

Goメモ-534 (cgoメモ-24)(C側の構造体にて固定要素数の文字配列をGo側で文字列に変換) - いろいろ備忘録日記

Goメモ-535 (cgoメモ-25)(cgo.Handleを用いてCとGoの間で値をやり取りする) - いろいろ備忘録日記

Goメモ-536 (cgoメモ-26)(C側の構造体をGo側で利用する) - いろいろ備忘録日記

Goメモ-537 (cgoメモ-27)(Go側でCの構造体のサイズを知る方法) - いろいろ備忘録日記

Goメモ-539 (cgoメモ-28)(Go側でCの文字列リスト(**char)を扱う) - いろいろ備忘録日記

Goメモ-623 (cgoメモ-29)(memcpyの呼び出し) - いろいろ備忘録日記

Goメモ-627 (cgoメモ-30)(Goでビルドした静的ライブラリのC言語からの利用) - いろいろ備忘録日記

Goメモ-628 (cgoメモ-31)(Cの関数からGoの関数をコールバックする) - いろいろ備忘録日記

Goメモ-629 (cgoメモ-32)(Cの関数からGoの関数をコールバックする(2)) - いろいろ備忘録日記

Goメモ-630 (cgoメモ-33)(LD_PRELOAD を利用したモック) - いろいろ備忘録日記

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

概要

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

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

Cgo is not Go

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

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

github.com

今回は C側で定義された配列を操作する について。基本的にcgo上ではC側で定義された配列に普通にアクセス出来ますが、cgoでは制約としてC側で定義されている define が見えないなどの制約事項があります。Cで配列操作する際に大抵は define で要素数などを定義していることが多いので無理に Go 側で頑張るよりも C 側にアクセサ関数などを定義して処理したほうが楽なときが多いです。

また、元の配列の型が例えば struct A item_list[ITEM_COUNT]; ってなってたとして、いつのまにか struct A *item_list[ITEM_COUNT]; などになってたりすると、Go側では気づかないときもあったりします。Cでは具象の場合とポインタの場合で .-> で使い分けしてアクセスしますが、Goの場合はどちらも . でアクセス出来てしまうので、いつのまにか既定の型が変わってしまっても気づかずにバグってしまうことも有りえます。(何かの関数に引数で指定してたりすると、そこでコンパイルエラー出るので発見できたりしますが)

サンプル

main.go

package main

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

#define ITEM_COUNT (3)
#define KEY_LENGTH (32)

typedef struct Item {
   char key[KEY_LENGTH];
   int value;
} Item;

Item item_list[ITEM_COUNT];

// 初期化関数
void init_item_list(void) {
   for (size_t i = 0; i < ITEM_COUNT; i++) {
       Item *item = &item_list[i];

       char buf[KEY_LENGTH];
       int len = snprintf(buf, KEY_LENGTH, "item-%02d", i);
       strncpy(item->key, buf, len);
       item->key[len] = '\0';

       item->value = (int)i;
   }
}

// 表示関数
void disp_item_list(void) {
   for (size_t i = 0; i < ITEM_COUNT; i++) {
       Item *item = &item_list[i];
       printf("[C ] key=%s, value=%d\n", item->key, item->value);
   }
}

// アクセサ関数
Item *get_item(size_t index) {
   return &item_list[index];
}

// 設定関数
void mod_item(Item *item, const char *key, int value) {
   strcpy(item->key, key);
   item->value = value;
}
*/
import "C"
import (
    "fmt"
    "unsafe"
)

const (
    ITEM_COUNT = 3
)

func main() {
    //
    // cgoで配列を扱う場合は、できれば要素を取得する アクセサ関数 を用意したほうが安全
    //

    C.init_item_list()

    display("init")
    modify()
    display("modify")
}

func display(msg string) {
    fmt.Printf("======[%s]======\n", msg)
    C.disp_item_list()

    for i := range ITEM_COUNT {
        item := C.get_item(C.size_t(i))
        fmt.Printf("[Go] key=%s, value=%d\n", C.GoString(&item.key[0]), int(item.value))
    }
    fmt.Println("==================")
}

func modify() {
    for i := range ITEM_COUNT {
        func() {
            item := C.get_item(C.size_t(i))

            key := fmt.Sprintf("ITEM-%02d", i)
            keyPtr := C.CString(key)
            defer C.free(unsafe.Pointer(keyPtr))

            C.mod_item(item, keyPtr, C.int(0xFF-i))
        }()
    }
}

Taskfile.yml

# https://taskfile.dev

version: '3'

vars:
  APP: app

tasks:
  default:
    cmds:
      - go build -o {{.APP}} main.go
      - ./{{.APP}}

実行

$ task
task: [default] go build -o app main.go
task: [default] ./app
======[init]======
[C ] key=item-00, value=0
[C ] key=item-01, value=1
[C ] key=item-02, value=2
[Go] key=item-00, value=0
[Go] key=item-01, value=1
[Go] key=item-02, value=2
==================
======[modify]======
[C ] key=ITEM-00, value=255
[C ] key=ITEM-01, value=254
[C ] key=ITEM-02, value=253
[Go] key=ITEM-00, value=255
[Go] key=ITEM-01, value=254
[Go] key=ITEM-02, value=253
==================

参考情報

個人的Goのおすすめ書籍

個人的に読んでとても勉強になった書籍さんたちです。


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

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