Search code examples
cgocgo

How to add a golang string to a c struct in cgo


How do I add a golang string to a c struct that I made in cgo. Heres the code:

package main

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

typedef struct Point {
    int x, y;
} Point;

typedef struct Resp {
    char response;
} Resp;
*/
import "C"
import (
    "fmt"
    "io/ioutil"
    "unsafe"
)

type CPoint struct {
    Point C.Point
    Resp  C.Resp
}

func main() {

    var r string = "Hello World"
    resp := unsafe.Pointer(C.char(r))

    point := CPoint{
        Point: C.Point{x: 1, y: 2},
        Resp: C.Resp{response: resp},
    }
    fmt.Println(point)
}

But whenever I run this I get this error cannot convert r (type string) to type _Ctype_char How do I fix this? How can I conver r to type _Ctype_char?

Also how would you get the value in the c struct "Resp"?

package main

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

typedef struct Point {
    int x, y;
} Point;

typedef struct Resp {
    char response; // <-- How can I get this value in my go code?
} Resp;
*/

Solution

  • Your code needs 2 fixes:

    1. C char only represents a single character/byte - not a string. C strings are char*.
    2. You should use C.CString to allocate a C string from Go, and C.free to release it. See: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C.

    Here is your example with these fixes:

    package main
    
    // FIX: "char response" replaced with "char *response" below.
    
    /*
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    typedef struct Point {
        int x, y;
    } Point;
    
    typedef struct Resp {
        char *response;
    } Resp;
    
    void fill(const char *name, Resp *r) {
       printf("name: %s\n", name);
       printf("Original value: %s\n", r->response);
       r->response = "testing";
    }
    */
    import "C"
    import (
        "fmt"
        "unsafe"
    )
    
    // NOTE: You cannot pass this struct to C.
    // Types passed to C must be defined in C (eg, like "Point" above).
    type CPoint struct {
        Point C.Point
        Resp  C.Resp
    }
    
    func main() {
        // FIX: Use CString to allocate a *C.char string.
        resp := C.CString("Hello World")
    
        point := CPoint{
            Point: C.Point{x: 1, y: 2},
            Resp:  C.Resp{response: resp},
        }
        fmt.Println(point)
    
        // Example of calling a C function and converting a C *char string to a Go string:
        cname := C.CString("foo")
        defer C.free(unsafe.Pointer(cname))
        r := &C.Resp{
            response: resp, // Use allocated C string.
        }
        C.fill(cname, r)
        goResp := C.GoString(r.response)
        fmt.Println(goResp)
    
        // FIX: Release manually allocated string with free(3) when no longer needed.
        C.free(unsafe.Pointer(resp))
    }