Skip to content

Call C/C++ from Codon

C/C++ functions can be called from Codon via the from C import import statement. Unlike standard imports, from C import must specify the imported function's argument and return types. For example:

# import the C standard library 'sqrt' function
from C import sqrt(float) -> float
print(sqrt(2.0))  # 1.41421

You can also rename C-imported functions:

# cobj is a C pointer (void*, char*, etc.)
# None can be used to represent C's void
from C import puts(cobj) -> None as print_line
print_line("hello".c_str())  # prints "hello"; c_str() converts Codon str to C string

You can also add annotations such as @pure to C-imported functions by instead declaring them with the @C attribute:

@C
@pure
def sqrt(x: float) -> float:
    pass

print(sqrt(2.0))  # 1.41421

Warning

If you're using C++, remember to declare any functions you want to call from Codon with extern "C" to enable C linkage, which Codon expects.

Type conversions

The following table shows the conversions between Codon and C/C++ types:

Codon C/C++
int int64_t
float double
bool bool
complex {double, double} (real and imag.)
str {int64_t, char*} (length and data)
tuple Struct of fields
class Pointer to corresponding tuple
Ptr[T] T*

Warning

Use caution when returning structures from C-imported functions, as C compilers might use an ABI that differs from Codon's ABI. It is recommended to instead pass a pointer as an argument that the callee can populate with the return value.

Optionals

Codon also has an Optional[T] type for representing None values, which is represented in one of two ways:

  • If T is a reference type (i.e. a type defined with class), then Optional[T] is represented the same way as T (i.e. a pointer to dynamically-allocated member data) with null representing None.

  • Otherwise, Optional[T] is represented as a C structure {bool, T} where the boolean field indicates whether the value is present.

NumPy arrays

NumPy array types are parameterized by the data type (dtype) and array dimension (ndim). They correspond to the following C structure definition:

struct ndarray {
  int64_t shape[ndim];
  int64_t strides[ndim];
  dtype *data
};

Refer to the NumPy documentation for an explanation of these fields.

Dynamic loading

Shared libraries can be loaded dynamically as follows:

LIBRARY = "libhello.so"

# load dynamically from 'libhello.so'
from C import LIBRARY.foo(int, float) -> None
from C import LIBRARY.bar() -> int as baz

x = foo(1, 2.2)
y = baz()

Dynamic C imports are implemented by calling dlopen() on the given shared library, followed by dlsym() to obtain the required functions.