purego: Fix string conversion when calling Go callbacks from C
Signed-off-by: Felicitas Pojtinger <felicitas@pojtinger.com>
- commit
- a44f95eedfa12c7a98d733ff4c4ab4490d2d61ad
- author
- Felicitas Pojtinger · · 2026-01-28 03:13 UTC
- committer
- brice · · 2026-09-27 05:38 UTC
- parent
- dea872e
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +37 -0
M callback_test.go +37 -0
| @@ -51,6 +51,43 @@ func TestCallGoFromSharedLib(t *testing.T) { | |||
| 51 | 51 | } | |
| 52 | 52 | } | |
| 53 | 53 | ||
| 54 | + | // TestCallbackStringConversion tests that C char* arguments are correctly | |
| 55 | + | // converted to Go strings when the callback signature uses string type. | |
| 56 | + | func TestCallbackStringConversion(t *testing.T) { | |
| 57 | + | libFileName := filepath.Join(t.TempDir(), "libcbtest.so") | |
| 58 | + | t.Logf("Build %v", libFileName) | |
| 59 | + | ||
| 60 | + | if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_test.c")); err != nil { | |
| 61 | + | t.Fatal(err) | |
| 62 | + | } | |
| 63 | + | defer os.Remove(libFileName) | |
| 64 | + | ||
| 65 | + | lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL) | |
| 66 | + | if err != nil { | |
| 67 | + | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | var callCallback func(p uintptr, s string) int | |
| 71 | + | purego.RegisterLibFunc(&callCallback, lib, "callCallback") | |
| 72 | + | ||
| 73 | + | const expected = "a test string" | |
| 74 | + | var got string | |
| 75 | + | goFunc := func(s string, n int) int { | |
| 76 | + | got = s | |
| 77 | + | if len(s) != n { | |
| 78 | + | t.Errorf("string length mismatch: got %d, want %d", len(s), n) | |
| 79 | + | } | |
| 80 | + | return 1 | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | cb := purego.NewCallback(goFunc) | |
| 84 | + | callCallback(cb, expected) | |
| 85 | + | ||
| 86 | + | if got != expected { | |
| 87 | + | t.Errorf("string callback got %q, want %q", got, expected) | |
| 88 | + | } | |
| 89 | + | } | |
| 90 | + | ||
| 54 | 91 | func TestNewCallbackFloat64(t *testing.T) { | |
| 55 | 92 | // This tests the maximum number of arguments a function to NewCallback can take | |
| 56 | 93 | const ( | |
| @@ -51,6 +51,43 @@ func TestCallGoFromSharedLib(t *testing.T) { | |||||
| 51 | } | 51 | } | ||
| 52 | } | 52 | } | ||
| 53 | 53 | ||||
| 54 | + | // TestCallbackStringConversion tests that C char* arguments are correctly | |||
| 55 | + | // converted to Go strings when the callback signature uses string type. | |||
| 56 | + | func TestCallbackStringConversion(t *testing.T) { | |||
| 57 | + | libFileName := filepath.Join(t.TempDir(), "libcbtest.so") | |||
| 58 | + | t.Logf("Build %v", libFileName) | |||
| 59 | + | ||||
| 60 | + | if err := buildSharedLib("CC", libFileName, filepath.Join("testdata", "libcbtest", "callback_test.c")); err != nil { | |||
| 61 | + | t.Fatal(err) | |||
| 62 | + | } | |||
| 63 | + | defer os.Remove(libFileName) | |||
| 64 | + | ||||
| 65 | + | lib, err := purego.Dlopen(libFileName, purego.RTLD_NOW|purego.RTLD_GLOBAL) | |||
| 66 | + | if err != nil { | |||
| 67 | + | t.Fatalf("Dlopen(%q) failed: %v", libFileName, err) | |||
| 68 | + | } | |||
| 69 | + | ||||
| 70 | + | var callCallback func(p uintptr, s string) int | |||
| 71 | + | purego.RegisterLibFunc(&callCallback, lib, "callCallback") | |||
| 72 | + | ||||
| 73 | + | const expected = "a test string" | |||
| 74 | + | var got string | |||
| 75 | + | goFunc := func(s string, n int) int { | |||
| 76 | + | got = s | |||
| 77 | + | if len(s) != n { | |||
| 78 | + | t.Errorf("string length mismatch: got %d, want %d", len(s), n) | |||
| 79 | + | } | |||
| 80 | + | return 1 | |||
| 81 | + | } | |||
| 82 | + | ||||
| 83 | + | cb := purego.NewCallback(goFunc) | |||
| 84 | + | callCallback(cb, expected) | |||
| 85 | + | ||||
| 86 | + | if got != expected { | |||
| 87 | + | t.Errorf("string callback got %q, want %q", got, expected) | |||
| 88 | + | } | |||
| 89 | + | } | |||
| 90 | + | ||||
| 54 | func TestNewCallbackFloat64(t *testing.T) { | 91 | func TestNewCallbackFloat64(t *testing.T) { | ||
| 55 | // This tests the maximum number of arguments a function to NewCallback can take | 92 | // This tests the maximum number of arguments a function to NewCallback can take | ||
| 56 | const ( | 93 | const ( | ||