Cgo: can't find way to use callbacks with const char* argument
Asked Answered
C

2

5

I'm using a C library from Go using Cgo, and all is good except for callbacks. The library has a callback setter, which takes a pointer to the callback function. The callback function is written in Go and exported using Cgo syntax.

Problem: I can make and export function with char * argument, but can't with const char *.

Code to illustrate:

test.go:

package main

/*
typedef void (*cb_func)(const char *, int);
void callback(cb_func);
void myFunc(const char *, int);
*/
import "C"
import (
        "fmt"
        "unsafe"
)

//export myFunc
func myFunc(buf *C.char, ln C.int) {
        fmt.Printf("Got: %s\n", C.GoStringN(buf, ln))
}

func main() {
        C.callback((C.cb_func)(unsafe.Pointer(C.myFunc)))
}

test.c:

typedef void (*cb_func)(const char *, int);

void callback(cb_func cb) {
        cb("test", 4);
}

Output from go build:

In file included from $WORK/test/_obj/_cgo_export.c:2:0:
./test.go:54:13: error: conflicting types for 'myFunc'
./test.go:7:6: note: previous declaration of 'myFunc' was here
 void myFunc(const char *, int);
      ^
/tmp/go-build994908053/test/_obj/_cgo_export.c:9:6: error: conflicting types for 'myFunc'
 void myFunc(char* p0, int p1)
      ^
In file included from $WORK/test/_obj/_cgo_export.c:2:0:
./test.go:7:6: note: previous declaration of 'myFunc' was here
 void myFunc(const char *, int);
      ^

Without the const qualifiers, the code compiles and works as expected.

What can be used instead of *C.char to get a const string in C?

Caliginous answered 4/10, 2015 at 21:23 Comment(0)
M
7

Since Go does not have const modifiers for pointers there is no way to translate this behaviour from inside Go code. cgo will always generate headers that do not have the const modifier. This is also the reason why your code does not build correctly: cgo creates myFunc only based on what it knows: buf should be char*, not const char*.

The best way to handle this is to use wrapper on the C side that casts that parameter to const char*. In your case it is enough to change the definition of myFunc to void myFunc(char*, int). Passing the function to cb_func will work regardless since casting myFunc to (*cb_func)(const char*,int) only adds type information but does not change the memory layout.

Moly answered 5/10, 2015 at 2:22 Comment(1)
Ok, wrapper is a solution. However, for now I redefined callback prototypes without const's directly in go file (as in my example above). It looks a bit hacky but works too.Caliginous
B
8

I had the same issue. One solution for the still-needee is to something like:

typedef const char cchar_t
void myNewFunction(cchar_t* data);

then use it in go as data* C.cchar_t

Brigitta answered 20/7, 2020 at 9:17 Comment(1)
Typo: use in Go as *C.cchar_t not *C.char_t (I would suggest an edit, but the edit queue is full)Georganngeorge
M
7

Since Go does not have const modifiers for pointers there is no way to translate this behaviour from inside Go code. cgo will always generate headers that do not have the const modifier. This is also the reason why your code does not build correctly: cgo creates myFunc only based on what it knows: buf should be char*, not const char*.

The best way to handle this is to use wrapper on the C side that casts that parameter to const char*. In your case it is enough to change the definition of myFunc to void myFunc(char*, int). Passing the function to cb_func will work regardless since casting myFunc to (*cb_func)(const char*,int) only adds type information but does not change the memory layout.

Moly answered 5/10, 2015 at 2:22 Comment(1)
Ok, wrapper is a solution. However, for now I redefined callback prototypes without const's directly in go file (as in my example above). It looks a bit hacky but works too.Caliginous

© 2022 - 2025 — McMap. All rights reserved.