View Source Nif options
Zigler gives you several ways to configure compilation options for nifs. These options are part of
the use Zig
directive, in the keyword options under the key :nifs
. This key itself points to a
keyword list where the keys are the names of the nif functions and the values are a keyword list of
options that apply to that function.
This guide shows you all of the nif options except for options related to Resources, C integration, or Concurrency.
Automatic options (elixir)
To declare that functions should have their options automatically determined, use ...
in the nifs
parameter list. Nifs which sholud have manually decided options should come after the ...
as a
keyword list. If all options are automatically determined, then omitting the :nif
keyword
completely is valid.
defmodule AutomaticOptions do
use Zig,
otp_app: :zigler,
nifs: [...]
~Z"""
pub fn noop() void {}
"""
end
Automatic options (erlang)
Erlang cannot interpret the ...
AST in elixir, so you must use [auto]
atom in the nifs options
instead.
-zig_opts([{nifs, [auto]}])
Return type
In Collections we saw how certain collection types could be manually
marshalled into alternative representations using beam.make
. This can be handled
as a nif configuration as follows. The advantage to doing it this way is that the typespec for the
function will correctly reflect the return type.
defmodule ReturnTypeTest do
use ExUnit.Case, async: true
use Zig,
otp_app: :zigler,
nifs: [
returns_binary: [return: :binary],
returns_list: [return: :list]
]
~Z"""
pub fn returns_binary() [3]u16 {
return [3]u16{47, 48, 49};
}
pub fn returns_list() []const u8 {
return "Hello world!";
}
"""
test "returns binary" do
assert <<47, 0, 48, 0, 49, 0>> = returns_binary()
end
test "returns list" do
assert ~C'Hello world!' = returns_list()
end
end
Alias
It's possible to create a new function which is an alias of another function. This is how it's done:
defmodule AliasTest do
use ExUnit.Case, async: true
use Zig,
otp_app: :zigler,
nifs: [
...,
new_function: [alias: :old_function]
]
~Z"""
pub fn old_function() u32 {
return 47;
}
"""
test "both main and alias functions work" do
assert 47 == old_function()
assert 47 == new_function()
end
end
Args options
Arguments can also take options, using args: [...]
Noclean
If you want to disable automatic allocator cleanup of datatypes, you can do so either in the
:return
or :args
section by including the :noclean
option. This is most useful if your args or
return data are going to be persisted beyond the lifetime of the nif call, though doing this in many
cases is not recommended.
This flag can be stacked with previous options, for example: return: [:noclean, :binary]
.
Leak Check
It's possible to wrap each function call in its own instance of
beam.general_purpose_allocator
bound into the
beam.allocator
threadlocal variable. If you tag your nif as leak_check
,
it will check that beam.allocator
has cleared all of its contents at the end of the function call,
and if that hasn't happened, it raises.
defmodule LeakCheckTest do
use ExUnit.Case, async: true
use Zig,
otp_app: :zigler,
nifs: [check_me: [leak_check: true]]
~Z"""
const beam = @import("beam");
pub fn check_me() !void {
_ = try beam.context.allocator.create(u8);
}
"""
test "leak check" do
require Logger
Logger.warning("====== the following leak message is expected: =========== START")
Process.sleep(200)
assert_raise RuntimeError, "memory leak detected in function `LeakCheckTest.check_me/0`", fn ->
check_me()
end
Logger.warning("=========================================================== END")
end
end
leak_check
can also be applied to all nifs in the module:
defmodule LeakCheckAllTest do
use ExUnit.Case, async: true
use Zig,
otp_app: :zigler,
leak_check: true
~Z"""
const beam = @import("beam");
pub fn check_me() !void {
_ = try beam.context.allocator.create(u8);
}
"""
test "leak check" do
require Logger
Logger.warning("====== the following leak message is expected: =========== START")
Process.sleep(200)
assert_raise RuntimeError, "memory leak detected in function `LeakCheckAllTest.check_me/0`", fn ->
check_me()
end
Logger.warning("=========================================================== END")
end
end
Typespec override
Typespecs generation can be suppressed by setting spec: false
. If you want typespecs for such
functions, specify using @spec
elsewhere in your module.
For example:
defmodule Override do
use Zig,
otp_app: :zigler,
nifs: [typespec_override: [spec: false]]
@spec typespec_override(integer) :: integer
~Z"""
const beam = @import("beam");
pub fn typespec_override(term: beam.term) !beam.term {
const input = try beam.get(u32, term, .{});
return beam.make(input + 1, .{});
}
"""
end
Disable documentation
Documentation can be disabled with the docs: false
option.
defmodule DisableDoc do
use Zig,
otp_app: :zigler,
nifs: [nodocs: [docs: false]]
~Z"""
pub fn nodocs() void {}
"""
end
#module