detached a44f95e

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 VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · 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) {
5151 }
5252 }
5353
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+
5491 func TestNewCallbackFloat64(t *testing.T) {
5592 // This tests the maximum number of arguments a function to NewCallback can take
5693 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 take92 // This tests the maximum number of arguments a function to NewCallback can take
56 const (93 const (

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close