• 締切済み

Golang/cgo GoからCへの構造体実体渡し

Windows用のとあるDLLがあり、Goから呼び出そうとしています。DLL内のAPIに、構造体の実体を受け取る引数があるのですが、Goからはどのように呼び出せば良いですか? (DLLライブラリ側の例) typedef struct { uint32_t id; uint16_t age; char name[20]; } Person; bool AddPerson(Person p); (Go側の実装(エラー処理省略)) type Person struct { id uint32 age uint16 name [20]byte } dll, err := syscall.LoadDLL("とあるDLL") proc, err := dll.FindProc("AddPerson") p1 := Person{id:1, age:18} copy(p1.name[:], []byte("taro")) proc.Call( p1を渡したい、ここに何と書くべき? ) 以上、よろしくお願いします。

みんなの回答

回答No.2

C言語では、構造体は実体を渡すか、ポインタを渡すかの2つの方法があります。実体を渡す場合は、Go側の構造体の各フィールドの値がレジスタやスタックに順にコピーされ、C側の関数に渡されます。ポインタを渡す場合は、Go側の構造体のアドレスがC側の関数に渡されます。 Go言語からC言語の関数を呼び出す際には、構造体をポインタに変換し、C.uintptr型にキャストしてC側の関数に渡すことができます。これにはunsafeパッケージのPointer関数を利用することができます。 上記の例では、Person構造体をポインタに変換し、C.uintptr型にキャストしています。C側の関数に渡す前に、Go側の構造体のフィールドの値を設定しておく必要があります。 C言語とGo言語間の関数呼び出しに関しては、ABIに準拠することが重要です。ABIは呼び出し規約を定め、呼び出し元と呼び出し先が適切に通信するために必要な情報を提供します。呼び出し元と呼び出し先の両方がC/C++で実装されている場合、ABIはおおむね同じものを使用するため、構造体の「実体渡し」がC言語で規約されている場合には、Go言語からも同様に実体を渡すように実装することができます。

haniriito2019
質問者

お礼

アドバイスありがとうございます。 いただいたアドバイスの前半は、あくまでもポインタ渡しの話なので、補足に書いたように、今回必要となる要件を満たしていないと思います。 アドバイスの後半は、ABIとか呼び出し規約を守りましょう、ということ以外の情報が含まれていないので、残念ですが私の質問・疑問は少しも解決されませんでした。

回答No.1

構造体をCの関数に渡すためには、unsafe.Pointerを利用して、C側のメモリに構造体のポインタを渡す必要があります。 以下は実装例です: p1 := Person{id: 1, age: 18} copy(p1.name[:], []byte("taro")) ptr := unsafe.Pointer(&p1) proc.Call(uintptr(ptr)) 注意点としては、Go側の構造体とC側の構造体が同じフィールド順になっていることが必要です。また、構造体のサイズが同じであることも保証する必要があります。

haniriito2019
質問者

補足

アドバイスありがとうございます。 せっかくアドバイスをいただいたのですが、Cライブラリ側のAPIインターフェースを変えることができないので、「構造体の実体受け取り」は変更できないという前提でお願いしたいです。 呼び出し元と呼び出し先の両方がC/C++で実装されるケースでは、構造体の「実体渡し」であったとしてもその内容はABI的にはレジスタ渡しand/orスタック渡しで順に渡す規約があったと思います。それに倣ったパラメータの「渡し方」でGoから呼び出す方法は無いものだろうか?と思った次第です。

関連するQ&A