So provides several tools for easy C interop.
Includes • Extern declarations • Extern options • Inlining • Qualifiers • Embeds • Raw C • Helpers
Include a C header file. By default, so:include emits in the .h file, making the header visible to consumers:
//so:include <stdint.h>Use so:include.c when the include is purely an implementation detail that should only appear in the .c file:
//so:include.c "internal_helper.h"Declare an external C type (excluded from emission) with so:extern:
//so:extern
type Account struct {
name string
balance int64
flags []uint8
}Declare an external C function:
//so:extern
func dec_balance(acc *Account, amount int64) int64 {
return 42 // for testing
}When calling extern functions, string and []T arguments are automatically decayed to their C equivalents: string literals become raw C strings ("hello"), string values become char* (.ptr), and slices become raw pointers (.ptr). This means C macros don't need to extract .ptr themselves:
//so:extern
func fopen(path string, mode string) *File { return nil }
// Go call:
f := fopen("/tmp/test.txt", "w")
// Generated C:
// fopen("/tmp/test.txt", "w")
// not fopen(so_str("/tmp/test.txt"), so_str("w"))The so:extern directive supports two optional parameters: a C name override and the nodecay flag.
Name override specifies the C name to use instead of the default package-prefixed name. Useful for extern types that must match a C header:
//so:extern Account
type Account struct {
name string
balance int64
}
// Uses "Account" in C instead of "main_Account"Nodecay skips the automatic decay of So types (so_String, so_Slice) to raw C types. Use this for C functions that are "So-aware" and accept So types directly:
//so:extern nodecay
func set_name(acc *Account, name string)
// Generated C passes so_String directly:
// set_name(&acc, name)
// not set_name(&acc, so_cstr(name))Both options can be combined:
//so:extern MyFunc nodecay
func MyFunc(s string)Force a function to be emitted as static inline in the header file using //so:inline. This is useful for small, frequently used functions when the compiler won't inline them automatically:
//so:inline
func add(a, b int) int {
return a + b
}The function body is emitted directly in the .h file and skipped from the .c file. Works with both functions and methods.
Mark a package-level variable as volatile using //so:volatile:
//so:volatile
var counter intOnly allowed on var declarations.
Mark a package-level variable as thread-local using //so:thread_local. Uses C11 _Thread_local:
//so:thread_local
var perThread intCan be combined with //so:volatile:
//so:volatile
//so:thread_local
var flags intOnly allowed on var declarations.
Add GCC/Clang __attribute__ annotations using //so:attr. The text after so:attr is used as the attribute value:
//so:attr packed
type header struct {
version byte
length int
}Multiple //so:attr lines on the same declaration are combined:
//so:attr packed
//so:attr aligned(16)
type aligned struct {
x int
}Allowed on var, const, type, and func declarations.
Embed C files directly into the generated output using //so:embed:
//so:embed main.h
var main_h string
//so:embed main.c
var main_c string.h files are embedded into the generated header, .c files into the generated implementation. The embed variable declarations are not emitted as C variables - they serve as markers only.
For ad-hoc C interop, the so/c package provides two compiler intrinsics that emit their string argument as raw C code. The argument must be a string literal.
c.Val[T](expr) emits a typed C expression. Use it to access C constants, macros, or call C functions inline:
nan := c.Val[float64]("NAN")
x := c.Val[float64]("sqrt(49)")c.Raw(code) emits a raw block of C code as a statement:
var b int
c.Raw(`
int a = 7;
b = a * a;
`)Be careful when using c.Val and c.Raw. C code written as string literals bypasses the type system and is hard to maintain, so it's usually better to use so:extern and so:embed instead.
The so/c package also provides low-level interop helpers for pointers, strings, and type information.
Functions:
AlignofandSizeofreturn the alignment and size of type T.Allocaallocates an array on the stack.Assertaborts with a message if a condition is false.Bytes,SliceandStringwrap C pointers to So types.CStringconverts a So string to a null-terminated C string.PtrAdd,PtrAsandPtrAtmanipulate pointers.Zeroreturns the zero value of type T.
Types:
CharandConstCharrepresent a Cchartype.