Solod 0.4: Better C interop

Lobsters Hottest Tools

Summary

Solod 0.4 enhances C interoperability with automatic binding generation, more freestanding packages, and improved tooling for developers.

<p><a href="https://lobste.rs/s/oz5ju3/solod_0_4_better_c_interop">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 09/12/26, 02:39 PM

# Solod 0.4: Better C interop Source: [https://antonz.org/solod-0.4/](https://antonz.org/solod-0.4/) Solod is a subset of Go that translates to regular C — with zero runtime, manual memory management, and source\-level interop\. It's designed for two main audiences: - Go developers who want low\-level control without having to learn another language\. - C developers who like Go's style\. The new Solod release provides an easy way to call third\-party C libraries, makes a large part of the standard library freestanding, and impoves the tooling\. [Automatic bindings](https://antonz.org/solod-0.4/#automatic-binding-generator)•[Freestanding packages](https://antonz.org/solod-0.4/#more-freestanding-packages)•[Type assertions](https://antonz.org/solod-0.4/#type-assertions)•[C interop](https://antonz.org/solod-0.4/#c-interop-helpers)•[Multi\-package testing](https://antonz.org/solod-0.4/#multi-package-testing)•[Checks and targets](https://antonz.org/solod-0.4/#checks-and-targets)•[Windows](https://antonz.org/solod-0.4/#limited-windows-support)•[Wrapping up](https://antonz.org/solod-0.4/#wrapping-up) ## Automatic binding generator [Sobind](https://github.com/solod-dev/sobind)generates bindings — stubs for calling third\-party C libraries from Solod\. It parses`\.h`files and emits a Solod source file with necessary structs, unions, constants, variables, function pointer typedefs, and function declarations\. You can then use the generated types and functions in regular Solod code: ``` package main import ( "solod.dev/raylib/libraylib" "solod.dev/so/c" ) func main() { // Using Raylib bindings. libraylib.InitWindow(screenWidth, screenHeight, "☀️ Solod / Raylib") defer libraylib.CloseWindow() // ... } ``` Usually, the generated bindings are good enough to use as they are, without any manual changes\. I have also prepared bindings for popular C libraries like[libuv](https://github.com/solod-dev/libuv),[raylib](https://github.com/solod-dev/raylib),[sodium](https://github.com/solod-dev/sodium), and[sqlite](https://github.com/solod-dev/sqlite)\. Unlike Go, calling C from Solod has zero overhead — Solod code is just regular C in the end\. ## More freestanding packages At some point I decided to make as many packages as possible freestanding — independent of any libc implementation or specific OS runtime\. That went pretty well\. Solod now has 37 standard library packages, and 31 of them work in freestanding mode\. These packages work in freestanding mode with no restrictions: ``` bufio bytealg bytes c cmp encoding encoding/binary encoding/hex encoding/json errors io maps math/bits math/rand mem path runtime slices strconv strings unicode unicode/utf8 unsafe ``` These packages work in freestanding mode with certain limitations: - `crypto/crand`depends on a user\-provided hook to read random bytes\. - `fmt`depends on a user\-provided hook to print formatted text\. - `math`offers a working subset of features\. - `net/netip`works fully, except it can't resolve an IPv6 zone name\. - `sync/atomic`works on targets that support lock\-free instructions\. - `testing`depends on a user\-provided hook to print test results\. - `time`reads the clock using user\-provided hooks\. - `uuid`depends on hooks from both`crypto/crand`and`time`\. There's a[separate post](https://antonz.org/going-freestanding/)with more details if you're interested\. ## Type assertions A comma\-ok type assertion is now fully supported for non\-empty interfaces: ``` var s1 Shape = &rect r, ok := s1.(*Rect) // r is &rect, ok is true var s2 Shape = &circle c, ok := s2.(*Rect) // c is nil, ok is false ``` Which translates to the following C code: ``` main_Shape s1 = (main_Shape){.self = &rect, .Area = main_Rect_Area}; bool ok = (s1.Area == main_Rect_Area); main_Rect* r = ok ? (main_Rect*)s1.self : NULL; // ok == true, r == &rect main_Shape s2 = (main_Shape){.self = &circle, .Area = main_Circle_Area}; ok = (s2.Area == main_Rect_Area); main_Rect* c = ok ? (main_Rect*)s2.self : NULL; // ok == false, c == NULL ``` Previously, the only two supported forms were a direct assertion like`r := s\.\(\*Rect\)`and a check\-only form like`\_, ok := s\.\(\*Rect\)`\. ## C interop helpers The`c`package now supports more common C types: ``` size_t - c.Size ssize_t - c.SSize ptrdiff_t - c.Ptrdiff intptr_t - c.Intptr long double - c.LongDouble ``` There's also a`c\.ConstVoid`type, which maps to a C`const void`\. You can use it where C expects a`const void\*`pointer: ``` // in c so_ssize_t find_first(const void* items, size_t count, size_t size, bool (*match)(const void*)); ``` ``` // in solod //so:extern func find_first(items *c.ConstVoid, count c.Size, size c.Size, match func(item *c.ConstVoid) bool) c.SSize ``` Finally, there are some useful cast functions\. `c\.Bitcast`reads the bits of a value as another type of the same size: ``` bits := c.Bitcast[uint64](1.0) // 0x3ff0000000000000 f := c.Bitcast[float64](bits) // 1.0 ``` You can use`c\.Bitcast`instead of a pointer conversion such as`\*\(\*float64\)\(unsafe\.Pointer\(&b\)\)`\. `c\.StringData`and`c\.SliceData`return a typed pointer to the string or slice data: ``` b := []byte{1, 2, 3} p := c.SliceData[c.UChar](b) // unsigned char* q := c.StringData[c.UChar]("ab") // unsigned char* ``` They replace`\(\*T\)\(unsafe\.SliceData\(b\)\)`and`\(\*T\)\(unsafe\.StringData\(s\)\)`\. ## Multi\-package testing `so test`can now run tests from multiple packages at once\. If you use a pattern that ends with`\.\.\.`, it will select every package that has a`test`subdirectory under its base directory: ``` so test ./so/... # the whole stdlib so test ./so/net/... # only the networking packages ``` The entire run only needs one translation, one compilation, and one execution, which is much faster than running it separately for each package\. The`\-pkg\-file`flag restricts the run to only the packages listed in a file: ``` # freestanding.txt so/bytes so/mem so/time ``` ``` so test -pkg-file=freestanding.txt ./so/... ``` ## Checks and targets `so build`,`so test`,`so bench`and`so run`take two new flags:`\-target`and`\-check`\. `\-target`specifies the target platform for cross\-compilation\. Use the same value that`clang`and`zig cc`accept after`\-\-target=`: ``` export CC="zig cc" so build -target=x86_64-windows-gnu -o app.exe . so build -target=wasm32-freestanding -o main.wasm . ``` `\-check`enables code analysis: ``` so test -check=warn . # -Wall -Wextra -Werror -Wno-shadow -Wno-unused-label so test -check=sanitize . # warn + AddressSanitizer + UndefinedBehaviorSanitizer so test -check=analyze . # warn + GCC static analyzer ``` The default optimization level is`\-O2`\. You can use`CFLAGS`to change it\. ## Limited Windows support The standard library now builds for`windows/amd64`and`windows/arm64`\. All packages in the freestanding set work\. Packages that require POSIX \(`conc`,`flag`,`log/slog`,`net`,`os`,`sync`\) are not supported\. You can use`zig cc`to cross\-compile for Windows: ``` export CC="zig cc" export CFLAGS="--target=x86_64-windows-gnu" export LDFLAGS="-lbcrypt -liphlpapi" so build -o app.exe . ``` Not the first\-class Windows support that Go offers, but it's better than nothing\. ## Wrapping up With v0\.4, Solod can work with almost any C library thanks to automatic bindings\. The freestanding\-aware standard library makes the language a viable option for bare metal programming\. Extra interop helpers make C\-calling code easy to read, and better tooling keeps tests fast\. There's still a lot to do, of course\. In the next release, I plan to focus on the standard library and bring over some hashing and crypto packages from Go\. More C library integrations are on the way too\! If you're interested, take a look at Solod's[readme](https://github.com/solod-dev/solod#readme)— it has everything you need to get started\. Or[try Solod online](https://codapi.org/solod)without installing anything\. [★ Subscribe](https://antonz.org/subscribe/)to keep up with new posts\.

Similar Articles

Solod: Go can be a better C

Hacker News Top

Solod is a strict subset of Go that compiles to readable C11 with zero runtime, no GC, and rich standard library, enabling systems-level control for Go developers and familiar tooling for C programmers.

Going freestanding

Lobsters Hottest

The article describes techniques for making a Go-to-C translation tool called Solod freestanding by porting standard library packages to work without libc, using methods like freestanding headers and compiler builtins.

Relying on Go

Lobsters Hottest

Solod is a new systems programming language that is a subset of Go, reusing Go's tooling and standard library while compiling to C11, offering manual memory management.

Better Sol

Product Hunt

Better Sol is a tool that enables developers to build end-to-end Solana applications using TypeScript.