Interoperability
Native libraries
Import host C ABI symbols while keeping the managed boundary explicit.Maintained byC2Go contributorsA declared-only function is an unmanaged host import by default in the current implementation. An ordinary native function therefore needs only a normal C declaration; unmanaged extern is not required. The symbol is resolved from a .so, .dylib, or .dll through the host C ABI.
Explicit unmanaged extern is an optional, stronger contract: it says the function will not later receive a C2Go definition. It also selects the host-callback function-pointer rules when a parameter or return type is itself a function pointer. The callback chapter uses the explicit form for that specific reason.
Declare an import
#include <c2go.h>
extern double vendor_scale(double value);
c2go_extern double scaled_value(double value) {
return vendor_scale(value);
}
A plain unused declaration is omitted from generated import metadata. An explicit unmanaged extern declaration can remain available to generated Go even when C code does not reference it; this is another reason to use the explicit form only when that contract is intended.
c2go_extern is not an import marker: it exports a C definition to Go. These two concepts are intentionally separate.
Tell c2go-bind which library to load
Pass library names and optional probe directories during binding:
c2go-bind \
--out=native \
--sidecar=native.json \
-l vendor \
-L /opt/example/lib \
native.s
The manifest normally determines whether Unix or Windows dispatch glue is required. --extern-os=unix|windows exists for controlled overrides, but should not conceal a target mismatch.
Build without cgo
C2Go emits ordinary Go files and Plan 9 assembly; it does not generate import "C". On Unix, c2go-libc uses a version-pinned PureGo bridge for native calls, while Windows uses the corresponding syscall bridge. The generated package can therefore be built with cgo disabled:
CGO_ENABLED=0 go build ./...
The final build does not need a host C compiler. If the program calls an external dynamic library, that .so, .dylib, or .dll must still be present at run time; building without cgo does not statically embed third-party libraries.
Pointer ownership still matters
The wrapper handles calling convention and runtime transitions; it does not invent an ownership policy. Before passing a pointer, establish:
- which side allocated the memory;
- whether the callee retains it after returning;
- whether the buffer can contain managed pointers; and
- which side releases it.
If an external library retains a buffer, follow that library’s required allocator and release function, or use a stable handle. Do not assume c2go-libc’s malloc is the host system allocator. Passing a managed pointer across the boundary can produce a lossy-conversion warning and requires an explicit lifetime design.
Supported call shapes
Scalar, pointer, float, and double imports are supported on the current Unix and Windows targets. Small records by value are supported within target ABI limits; larger or unusual shapes use wrappers or fail closed. Variadic imports and platform-specific records deserve dedicated cross-target tests.
Direct calls versus function pointers
Calling vendor_scale(value) automatically uses the generated host bridge. If you need a C2Go-callable function pointer, use c2go_callout(vendor_scale). If you need the raw native address to pass back to another native API, use &vendor_scale; do not call that raw address directly from C2Go.
The next chapter explains both directions in detail through callbacks.