Skip to content
vassvik edited this page Nov 12, 2017 · 18 revisions

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.

Example

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. [!]

Foreign blocks

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=<string>") 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:

```go
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
Clone this wiki locally