LoginSignup
3
5

More than 5 years have passed since last update.

C の共用体メンバーに cgo からアクセスする方法の一例

Last updated at Posted at 2019-03-02

環境

  • go: 1.8.1 linux/amd64

昔書いたメモの公開なので公開日より環境がかなり古くなりますがご了承ください.

問題の概要

go の仕様上の制限(?)で cgo で C の共用体の各フィールドにフィールド名を用いてアクセスすることができない.

例えば下記のような共用体 “myunion1” を例にする.

typedef union myunion1 {
    int  iv;
    long lv;
} myunion1;

cgo で myunion1 内の iv に C.int 型の値を代入する場合, 例えば単純に下記のような方法ではコンパイルエラーとなる.

var uni C.myunion1
uni.iv = C.int(100) // これは NG

コンパイルすると下記の様なエラーメッセージ.

$ go run cgouniontest.go
# command-line-arguments
./cgouniontest.go:51: uni.iv undefined (type C.myunion1 has no field or method iv)

解決策

共用体変数への C ポインタの変数を作ってポインタ変数の実体にアクセスするという方法を取れば問題を回避できる.

    var uni C.myunion1
    p := (*C.int)(unsafe.Pointer(&uni))
    *p = C.int(2) // 値の代入
    fmt.Printf("uni.iv=%d\n", int(*p)) // 読み出し

より実践的な例

構造体や enum と組み合わせたより実践的な例を載せておきます.
C.mystruct 型の変数 “str” の値を C から参照した場合 (C の関数 “printstruct()”) と cgo から参照した場合 (fmt.Printf() の行) で並べて表示しています.

cgouniontest.go
package main

//#include <stdlib.h>
//#include <stdio.h>
//#include <string.h>
//
//typedef enum {
//    ev1,
//    ev2,
//    ev3,
//    ev4
//} myenum1;
//
//typedef enum {
//    eev1,
//    eev2,
//    eev3,
//    eev4
//} myenum2;
//
//typedef union myunion1 {
//    int  iv;
//    long lv;
//} myunion1;
//
//typedef union myunion2 {
//    myenum1 uv1;
//    myenum2 uv2;
//} myunion2;
//
//typedef struct mystruct {
//    myunion1 u1;
//    myunion2 u2;
//    int sv;
//} mystruct;
//
//void printstruct(mystruct str) {
//    printf("native c: iv=%d, lv=%ld, uv1=%d, uv2=%d, sv=%d\n", str.u1.iv, str.u1.lv, str.u2.uv1, str.u2.uv2, str.sv);
//}
//
import "C"

import (
    "unsafe"
    "fmt"
    "math"
)

func main() {
    var str C.mystruct
    p1i := (*C.int)(unsafe.Pointer(&str.u1))
    p1l := (*C.long)(unsafe.Pointer(&str.u1))
    p2  := (*C.int)(unsafe.Pointer(&str.u2))
    *p1i = C.int(100)
    *p2  = C.ev2
    str.sv = 3
    C.printstruct(str)
    fmt.Printf("cgo     : iv=%d, lv=%v, uv1=%d, uv2=%d, sv=%d\n", int(*p1i), uint64(*p1l), int(*p2), int(*p2), int(str.sv))
    *p1l = C.long(math.MaxInt32 * 4)
    *p2 =  C.eev3
    C.printstruct(str)
    fmt.Printf("cgo     : iv=%d, lv=%v, uv1=%d, uv2=%d, sv=%d\n", int(*p1i), uint64(*p1l), int(*p2), int(*p2), int(str.sv))
}

実行結果は下記の通り. C からも cgo からも同じ結果が得られた.

$ go run cgouniontest.go
native c: iv=100, lv=100, uv1=1, uv2=1, sv=3
cgo     : iv=100, lv=100, uv1=1, uv2=1, sv=3
native c: iv=-4, lv=8589934588, uv1=2, uv2=2, sv=3
cgo     : iv=-4, lv=8589934588, uv1=2, uv2=2, sv=3
3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5