cgoでvoid **(ダブルポインター)を使う

TL;DR;

void **はどうやって表現する? *unsafe.Pointer .

void *は? unsafe.Pointer .

example

var u8v **C.uint8_t
var doubleVoidPointer *unsafe.Pointer
u8v = (**C.uint8_t)(C.malloc(size))
doubleVoidPointer = (*unsafe.Pointer)(unsafe.Pointer(u8v))
free(unsafe.Pointer(u8v))

failure example

// `()`をつけ忘れている
u8v = (**C.uint8_t)C.malloc(size)

キャスト時にカッコ()をつけ忘れると、expected ';', found C といったエラーが出る

Ref

理解できない場合は以下2つの公式ドキュメントを読むと良い ただしここでは翻訳したurlとする

xn--go-hh0g6u.com

kawawa.hatenablog.com

以下ポエム

GCがあることにより困るのは以下の2点

  • GCによる変数の格納場所の入れ替え(以降moveとする)
  • 未参照時にfreeされる

この2つの特徴を混ぜていて、理解するのに時間がかかった。 そのため明確に分けると良い。

uintptr

uintptrはポインタを格納することのできる大きさを持った整数

unsafe.Pointerはポインタ

  • moveによる影響

uintptrが指す先はあくまで整数であるため、moveが発生しても変化しない

(int型の値はgcが発生してもint型の値)

  • 未参照時のfree

ただし参照されなくなると、freeされてしまう

uintptr型の数値は解放されないが、uintptrが示すアドレスの先に、Goポインターがあると、解放される

GCはCPointerを勝手に解放しない=GCはCPinterをGC対象としない

CはCのプログラム設計者の意図でメモリ管理されているため、そこにGCが介入すると破綻するため

move時のpointer

move時はアドレスが変わる。

そのためポインターを保持していると、ポインターの書き換えが起こる。

疑問

Cポインターのアドレスが書き換えられると困らない?

こまる。そのためCポインターは書き換えが発生しない(GC管理対象外)。

CポインターをGoポインターに保持する場合は困らない?

例えば以下のような場合

stuct Piyo {
    int pon
}

struct Piyo* piyo_open(void) {
    p = (* struct Piyo)malloc(sizeof(struct Piyo))
    p.pon = 0
    return p
}

stuct Fuga {
    struct Piyo *piyo
}

struct Fuga* fuga_open(void) {
    f = (* struct Fuga)malloc(sizeof(struct Fuga))
    f.piyo = piyo_open(void)
    return f
}
type Hoge struct {
   fuga *C.Fuga
}

func main() {
    hoge := &Hoge{}

    hoge.fuga = C.fuga_open()
}

hogeはGoPointer GC対象 hoge.fugaはCPointer GC対象外

hogaのアドレスが変わっても、hogeの中身のfugaはGC対象外だから、そのままの値が維持される

つまり困らない

unsafe.Pointer

こちらはあくまでポインター

ダブルポインターの要素の参照。後で書く

[] indexは使えないため、uintptrに変換してアクセスする

struct Hoge {
   char *x
};
    for i := 0; i < int(num); i++ {
        p := *(**C.Hoge)(unsafe.Pointer(uintptr(unsafe.Pointer(xxx)) + uintptr(i)*unsafe.Sizeof(uintptr(0))))
        fmt.Println(i, C.GoString(p.x))
    }