12

Stuck with this problem. Able to get only the first member of passed structure... What I do wrong? And what is the right way to pass the structure from Go to C?

This is my example of how it doesn't work:

package main

/*
#include <stdio.h>

typedef struct {
    int a;
    int b;
} Foo;

void pass_array(Foo **in) {
    int i;

    for(i = 0; i < 2; i++) {
        fprintf(stderr, "[%d, %d]", in[i]->a, in[i]->b);
    }
    fprintf(stderr, "\n");
}

void pass_struct(Foo *in) {
    fprintf(stderr, "[%d, %d]\n", in->a, in->b);
}

*/
import "C"

import (
    "unsafe"
)

type Foo struct {
    A int
    B int
}

func main() {

    foo := Foo{25, 26}
    foos := []Foo{{25, 26}, {50, 51}}

    // wrong result = [25, 0]
    C.pass_struct((*_Ctype_Foo)(unsafe.Pointer(&foo)))

    // doesn't work at all, SIGSEGV
    // C.pass_array((**_Ctype_Foo)(unsafe.Pointer(&foos[0])))

    // wrong result = [25, 0], [50, 0]
    out := make([]*_Ctype_Foo, len(foos))
    out[0] = (*_Ctype_Foo)(unsafe.Pointer(&foos[0]))
    out[1] = (*_Ctype_Foo)(unsafe.Pointer(&foos[1]))
    C.pass_array((**_Ctype_Foo)(unsafe.Pointer(&out[0])))
}

3 Answers 3

9

The problem is that Foo and _Ctype_Foo are different structures.

I would guess you are running on 64 bit. Note that int is 64 bit in go, but is quite likely to be 32 bit in C.

If I change the definition of Foo to this then it works on my machine (64 bit linux)

type Foo struct {
    A int32
    B int32
}

However I would say that is a recipe for trouble - make your Go and C code use the same structure with

type Foo _Ctype_Foo
Sign up to request clarification or add additional context in comments.

1 Comment

Agree with your conclusion about using the C type explicitly. Just a note, golang's int is not necessarily 64bit; it's at least 32bit: golang.org/pkg/builtin/#int
7

I know this is a fairly old topic but I stumbled across it. Here's a modified (correct) version with some additional manipulation from C land on the Go structs.

package main

/*
#include <stdio.h>

typedef struct {
    int a;
    int b;
} Foo;

void pass_struct(Foo *in) { printf("%d : %d\n", in->a, in->b); }

void pass_array(Foo **in, int len) {
    for(int i = 0; i < len; i++) {
        pass_struct(in[i]);
        in[i]->a += 1;
        in[i]->b += 1;
    }
}
*/
import "C"

import (
    "fmt"
    "unsafe"
)

type Foo struct{ a, b int32 }

func main() {
    foo := Foo{10, 20}
    foos := []*Foo{&Foo{1, 2}, &Foo{3, 4}}

    fmt.Println("from C land")
    C.pass_struct((*C.Foo)(unsafe.Pointer(&foo)))
    C.pass_array((**C.Foo)(unsafe.Pointer(&foos[0])), C.int(len(foos)))
    fmt.Println("a & b should have incremented with 1")

    fmt.Println("from Go land")
    for _, foo := range foos {
        fmt.Printf("%d : %d\n", foo.a, foo.b)
    }
}

Output:

from C land
10 : 20
1 : 2
3 : 4
a & b should have incremented with 1
from Go land
2 : 3
4 : 5

1 Comment

I get this error info panic: runtime error: cgo argument has Go pointer to Go pointer
7

Neither of these answers work now (at least under Go 1.12). I wrote two another solutions:

package main

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

typedef struct {
    int a;
    int b;
} Foo;

int pass_array(Foo **in) {
    int i;
    int r = 0;

    for(i = 0; i < 2; i++) {
        r += in[i]->a;
        r *= in[i]->b;
    }
    return r;
}

*/
import "C"
import (
    "fmt"
    "unsafe"
)

type Foo struct {
    A int32
    B int32
}

func a() {
    foos := []Foo{{1, 2}, {3, 4}}

    l := len(foos)
    values := (*[1 << 28]*C.Foo)(C.malloc(C.size_t(C.sizeof_Foo * l)))
    for i, f := range foos {
        foo := (*C.Foo)(C.malloc(C.size_t(C.sizeof_Foo)))
        (*foo).a = C.int(f.A)
        (*foo).b = C.int(f.B)
        values[i] = foo
    }
    val := C.pass_array(&values[0])
    for i := 0; i < l; i++ {
        C.free(unsafe.Pointer(values[i]))
    }
    C.free(unsafe.Pointer(values))
    fmt.Println("A finished", val)
}

func b() {
    foos := []Foo{{5, 6}, {7, 8}}

    values := make([]*C.Foo, len(foos))
    for i, f := range foos {
        p := (*C.Foo)(C.malloc(C.size_t(C.sizeof_Foo)))
        values[i] = p
        (*p).a = C.int(f.A)
        (*p).b = C.int(f.B)
    }
    val := C.pass_array(&values[0])
    for _, v := range values {
        C.free(unsafe.Pointer(v))
    }
    fmt.Println("B finished", val)
}

func main() {
    a()
    b()
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.