-
-
Notifications
You must be signed in to change notification settings - Fork 652
Binding to C
Odin makes it fairly easy to bind to C code (but not C++ code, unless wrapped) that is compiled as a local static or dynamic/shared library, or to a system library.
This is primarily done in two parts, first by using foreign import
to create an import name and link to the library file, and foreign
blocks to link to declare individual exported functions and variables.
Make a file called foo.c
containing the following:
foo.c:
int foo_add_int(int a, int b) {
return a + b;
}
int foo_add_double(double a, double b) {
return a + b;
}
then, depending on your platform:
gcc -c foo.c
ar rcs foo.a foo.o
cl -TC -c foo.c
lib -nologo foo.obj -out:foo.lib
And finally, to use it in Odin, you could do:
import "core:fmt.odin"
when ODIN_OS=="windows" do foreign import foo "foo.lib";
when ODIN_OS=="linux" do foreign import foo "foo.a";
foreign foo {
foo_add_int :: proc(a, b: i32) -> i32 ---;
foo_add_double :: proc(a, b: f64) -> f64 ---;
}
main :: proc() {
fmt.println(foo_add_int(2, 2));
fmt.println(foo_add_double(2.71828, 3.14159));
}
Note that the procedure declaration has to end in ---
inside the foreign
block to denote that the body is found elsewhere. This is to prevent a parsing ambiguity with procedure variables and types. [!]
Odin supports multiple ways of making binding to C easier.
By default, the foreign block links to a function of the same name as the name you specify inside the foreign
block. This can be explicitly changed by using the @(link_name=<string>)
and `@(link_prefix=") attributes. The following are all equivalent:
foreign foo {
foo_add_int :: proc(a, b: i32) -> i32 ---;
foo_add_double :: proc(a, b: f64) -> f64 ---;
}
foreign foo {
@(link_name="foo_add_int")
add_int :: proc(a, b: i32) -> i32 ---;
@(link_name="foo_add_double")
add_double :: proc(a, b: f64) -> f64 ---;
}
@(link_prefix="foo_");
foreign foo {
add_int :: proc(a, b: i32) -> i32 ---;
add_double :: proc(a, b: f64) -> f64 ---;
}
Note that you can also overload foreign function names:
foreign foo { @(link_name="foo_add_int") add :: proc(a, b: i32) -> i32 ---; @(link_name="foo_add_double") add :: proc(a, b: f64) -> f64 ---; }
## Static vs shared libraries
Static libraries work the same in on every platform: you specify the relative paths of the library file in the .odin file you are importing it to.
Dynamic/shared libraries are slightly different depending on which operating system you are using:
- In Windows you link to an import lib (.lib) relative to the file you're importing from, and the user have to make sure the corresponding .dll is visible to the exectutable.
- In Linux you link directly to the shared object (.so) that is visible to the executable.
Not that for local libraries and for system libraries in Windows you need to specify the full filename, including file extension. For system libraries in Linux you can omit the extension, and it will link to the appropriate library file available in the system library search paths.
To summarize, if the current file is `/path/too/file/foo.odin` ```
foreign import foo "foo.a" // static lib in linux, links to /path/too/file/foo.a
foreign import foo "foo.so" // shared lib in linux links to /path/too/executable/foo.so
foreign import foo "foo.lib" // static/import library in windows, links to /path/to/file/foo.lib
foreign import foo "system:foo" // system library (static or shared) in linux. Links to /usr/lib/libfoo.a
foreign import foo "system:foo.lib" // system library (static or import) in windows. Links to foo.lib