C Interop
C3 is C ABI compatible. That means you can call C from C3, and call C3 from C without having to do anything special. As a quick way to call C, you can simply declare the function as a C3 function but with extern in front of it. As long as the function is linked, it will work:
extern fn void puts(char*); // C "puts"
fn void main()
{
// This will call the "puts"
// function in the standard c lib.
puts("Hello, world!");
}
To use a different identifier inside of your C3 code compared to the function or variable’s external name, use the @cname attribute:
extern fn void foo_puts(char*) @cname("puts"); // C "puts"
fn void main()
{
foo_puts("Hello, world!"); // Still calls C "puts"
}
While C3 functions are available from C using their external name, it's often useful to define an external name using @cname or @export with a name to match C usage.
module foo;
fn int square(int x) @export // @export ensures external visibility
{
return x * x;
}
fn int square2(int x) @export("square")
{
return x * x;
}
Calling from C:
extern int square(int);
int foo_square(int) __attribute__ ((weak, alias ("foo__square")));
void test()
{
// This would call square2
printf("%d\n", square(11));
// This would call square
printf("%d\n", foo_square(11));
}
Linking static and dynamic libraries¶
If you have a library foo.a or foo.so or foo.obj (depending on type and OS), just add -l foo on the command line, or in the project file add it to the linked-libraries value, e.g. "linked-libraries" = ["foo"].
To add library search paths, use -L <directory> from the command line and linker-search-paths the project file (e.g. "linker-search-paths" = ["../mylibs/", "/extra-libs/"])
Gotchas¶
- Bitstructs will be seen as its backing type, when used from C.
- C bit fields must be manually converted to a C3 bitstruct with the correct layout for each target platform.
- C assumes the enum size is
CInt - C3 uses fixed integer sizes, this means that
intandCIntdoes not need to be the same though in practice on 32/64 bit machines,longis usually the only type that differs in size between C and C3. - Atomic types are not supported by C3.
- In C3 there are generic Atomic types instead.
- There are no
volatileandconstqualifiers like in C.- C3 has global constants declared with
const. - Instead of the
volatiletype qualifier, there are standard library macros@volatile_loadand@volatile_store.
- C3 has global constants declared with
- Passing arrays by value like in C3 must be represented as passing a struct containing the array.
- In C3, fixed arrays do not decay into pointers like in C.
- When defining a C function that has an array argument, replace the array type with a pointer. E.g.
void test(int[] a)should becomeextern fn void test(int* a). If the function has a sized array, likevoid test2(int[4] b)replace it with a pointer to a sized array:extern fn void test2(int[4]* b); - Note that a pointer to an array is always implicitly convertable to a pointer to the first element. For example,
int[4]*may be implicitly converted toint*.
- When defining a C function that has an array argument, replace the array type with a pointer. E.g.
- The C3 names of functions are name-spaced with the module by default when using
@export, so when exporting a function with@exportthat is to be used from C, specify an explicit external name. E.g.fn void myfunc() @export("myfunc") { ... }.