View Source CXX integration
Zigler offers several tools to integrate your code with C and C++ code.
compiling C using the C toolchain
If you want to compile C or C++ files using the C and C++ toolchain bundled with the zig programming
language, you should include a path to your include directory and a source path or list of source
paths to be compiled, via the include_dir
and src
module options.
source paths
source paths may contain a trailing *, which will pull all c or c++ files in that directory, (but not subdirectory)
included.h
// forwarded function definition
int plus_one(int);
src/src.c
int plus_one(int value) {
return value + 1;
}
Elixir code
defmodule CompilingC do
use ExUnit.Case, async: true
use Zig,
otp_app: :zigler,
c: [include_dirs: "include", src: "src/*"]
~Z"""
const c = @cImport(@cInclude("included.h"));
pub const plus_one = c.plus_one;
"""
test "c plus one" do
assert 48 = plus_one(47)
end
end
linking against a C abi library
This example shows you how to link in a system library (which can be .a
, .so
, .obj
, or
.dll
). Zig will resolve the extension based on the operating system native rules. To use the
functions in the library, there must also be an associated .h
file with extern
functions.
In this example we'll use the cblas_dasum
function, which takes a length, an pointer to
double-precision floating point list, and a integer stride. The result is a sum of the numbers in
the list.
The rules for collections apply to functions that are directly imported from C files.
if {:unix, :linux} == :os.type() do
# currently we only have access to the BLAS library on linux CI actions, so
# it's unavailable for other operating systems for automated testing purposes
defmodule LibraryTest do
use ExUnit.Case, async: true
use Zig,
otp_app: :zigler,
c: [link_lib: {:system, "blas"}]
~Z"""
pub const dasum = @cImport(@cInclude("cblas.h")).cblas_dasum;
"""
test "dasum" do
assert 6.0 == dasum(3, [1.0, 2.0, 3.0], 1)
end
end
linking against libcpp:
if you need to link against
libcpp
, the library has a special-cased option:link_libcpp: true
Easy-C
It's also possible to also automatically create nifs without writing zig function shims. This works
either with linking an external library with link_lib
or building your own code with src
.
Because the C ABI exposes all functions publically in a global namespace, we can't use zigler's automatic detection to decide which functions to surface.
Here are the steps to using easy_c
:
- declare which header file you'd like to use with the
easy_c
module option- you may want to add
include_dir
if the header isn't a system C header.
- you may want to add
- add a
link_lib
orsrc
option to make sure that the functions are built. - declare which functions you'd like to hoist into the module.
In this example we'll use the cblas_daxpy
function, which takes a length, a double-precision a
value, a list (x
) of double-precision values, a stride for the x
values, a list (y
) of
double-precision values, and a stride for y
.
It then calculates the linear transformation ax + y
using these vectors.
"in-out" parameters
The
y
parameter in this function is an "in-out" parameter. Instead of returning a result, the results are written into the y pointer.to specify that it's an in-out parameter, we put an integer (which is the 0-indexed index of the argument which is the out parameter).
since the returned pointer doesn't have a specified value, we have to specify a
length
option, which could either be a fixed number, or{:arg, n}
wheren
is the argument index corresponding to a variable length.We can also use nif options like alias and the return option
binary
alongside these specialeasy_c
options.
defmodule EasyCTest do
use ExUnit.Case, async: true
use Zig,
otp_app: :zigler,
easy_c: "cblas.h",
c: [link_lib: {:system, "blas"}],
nifs: [
cblas_daxpy: [params: %{4 => :in_out}, return: [length: {:arg, 0}]],
cblas_daxpy_bin: [alias: :cblas_daxpy, params: %{4 => :in_out}, return: [:binary, length: {:arg, 0}]]
]
test "daxpy as a list" do
assert [7.0, 11.0, 15.0] == cblas_daxpy(3, 3.0, [1.0, 2.0, 3.0], 1, [4.0, 5.0, 6.0], 1)
end
test "daxpy as a binary" do
assert <<7.0::float-native, 11.0::float-native, 15.0::float-native>> ==
cblas_daxpy_bin(3, 3.0, [1.0, 2.0, 3.0], 1, [4.0, 5.0, 6.0], 1)
end
end
end
# module