https://github.com/beaver-lodge/kinda
Ship C library with Zig-based NIF for Elixir
Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Committers with academic emails
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (13.0%) to scientific vocabulary
Keywords
Repository
Ship C library with Zig-based NIF for Elixir
Basic Info
Statistics
- Stars: 14
- Watchers: 1
- Forks: 1
- Open Issues: 0
- Releases: 0
Topics
Metadata Files
README.md
Kinda
Kinda is an Elixir package using Zig to bind a C library to BEAM the Erlang virtual machine. The core idea here is using comptime features in Zig to create a "resource kind" which is "higher-kinded" type abstracts the NIF resource object, C type and Elixir module.
The general source code generating and building approach here is highly inspired by the TableGen/.inc in LLVM. Kinda will generate NIFs exported by resource kinds and and provide Elixir macros to generate higher level API to call them and create resource. With Kinda, NIFs generated and hand-rolled co-exist and complement each other.
Installation
If available in Hex, the package can be installed
by adding Kinda to your list of dependencies in mix.exs:
elixir
def deps do
[
{:kinda, "~> 0.7.1"}
]
end
Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/Kinda.
Usage
A full example could be found in kinda_example
More examples
- define a root module for the C library
elixir
defmodule Foo.CAPI do
use Kinda.Library, kinds: [Foo.BarKind]
end
- define a forwarder module
elixir defmodule Foo.Native do use Kinda.Forwarder, root_module: CAPI end - define kinds
elixir defmodule Foo.BarKind do use Kinda.ResourceKind, forward_module: Foo.Native end
What Kinda does
- Make NIF more of a purely function dispatch. So that you can break the complicity among C/Zig and Elixir.
- Make it possible to pattern matching a C type in Elixir.
- Everything in Kinda could be a NIF resource, including primitive types like integer and C struct. This makes it possible to pass them to C functions as pointers.
- Kinda will generate a NIF function for every C function in your wrapper header, and register every C struct as NIF resource.
Cool features in Kinda enabled by Zig
- Packing anything into a resource
Almost all C++/Rust implementation seems to force you to map a fixed size type to a resource type.
In fact for same resource type, you can have Erlang allocate memory of any size.
With Zig's comptime sizeof you can easily pack a list of items into an array/struct without adding any abstraction and overhead. An illustration:
[(address to item1), item1, item2, item3, ...]
So the memory is totally managed by Erlang, and you can use Zig's comptime feature to infer everything involved.
- Saving lib/include path to a Zig source and use them in your
build.zig. You can use Elixir to find all the paths. It is way better than configuring with make/CMake because you are using Elixir a whole programming language to do it. It is described in Zig doc as:
Surfacing build configuration as comptime values by providing a file that can be imported by Zig code.
- Inter NIF resource exchange. Because it is Zig, just import the Zig source from another Hex package.
Differences from Zigler
Kinda borrows a lot of good ideas and code from Zigler (Zigler is awesome~) but there are some differences:
- Kinda's primary goal is to help you consume a C library, not helping you write NIFs in Zig.
- Kinda expects you to have a
build.zig. So if you want to also sneak CMake/Bazel inside, go for it. - In functions generated by Kinda, all memory are allocated and managed by Erlang as resource.
Differences from Rustler
Kinda is also inspired by Rustler. Rustler really define what a ergonomic NIF lib should be like.
- Kinda should have the sugar for resource open and NIF declaration similar to Rustler but are provided in Elixir (
nif_genandtype_gen). - Due to the absence of official Zig package indexing, as for now Kinda's approach could be more of a monolithic NIF lib while in Rustler, you can break things into different crates which is really nice.
- The only protection Kinda might provide is the resource type checks. Lifetime and other more sophisticated checks are expected to be provided by the C library you are consuming.
Differences from TableGen
- Usually TableGen generates C/C++ source code. While in Kinda it is expected to generate Elixir AST and get compiled directly.
- To generate Zig code, Kinda takes in C
.hfiles instead of.tdfiles.
Core concepts
ResourceKind: a Zig struct to bundle:- C types
- Erlang NIF resource object type
- functions to open/fetch/make a resource object.
- there could be higher order
ResourceKindto bundle one or more differentResourceKinds
root_module: the NIF module will load the C shared libraryforward_module: module implement functions likearray/2,ptr/1to forward functions toroot_module. By implementing different callbacks, you might choose to use Elixir struct to wrap a resource or use it directly.Recommended module mapping convention:
- let's say you have a Elixir module to manage a C type. And the NIF module is
SomeLib.CAPI
elixir defmodule SomeLib.I64 do defstruct ref: nil def create() do ref = apply(SomeLib.CAPI, Module.concat([__MODULE__, :create]) |> Kinda.check! struct!(__MODULE__, %{ref: ref}) end end- inSomeLib.CAPIthere should be a NIF generated by Kinda with name:"Elixir.SomeLib.I64.create"/0- let's say you have a Elixir module to manage a C type. And the NIF module is
wrapper: a
.hC header file including all the C types and functions you want to use in Elixir. Kinda will generate a NIF module for every wrapper file.wrapped functions, C function with corresponding NIF function name, will be called in the NIF module.
kinds to generate: return type and argument types of every functions in wrapper will be generated. User will need to implement the behavior
Kinda.CodeGenfor a type with a special name (usually it is C function pointer), or it is the type name in C source by default.raw nifs: nifs doesn't follow involved in the resource kind naming convention. Insides these NIFs it is recommended to use NIF resource types registered by Kinda.
Internals
Source code generation
- calling Zig's
translation-cto generate Zig source code from wrapper header - parse Zig source into ast and then:
- collect C types declared as constants in Zig (mainly structs), generate kinds for them
- generate kinds for primitive types like int, float, etc.
- generate pointer/array kinds for all types
- generate NIF source for every C function in the wrapper header
- generate kind source for every C type used in C functions
Pre-built mode
- Out of the box Kinda supports generating and loading pre-built NIF library.
elixir
use Kinda.Prebuilt,
otp_app: :beaver,
base_url: "[URL]",
version: "[VERSION]"
- It reuses code in rustler_precompiled to follow the same convention of checksum checks and OTP compatibility rules.
- In Kinda, besides the main NIF library, there might be
kinda-meta-*.exfor functions signatures and multiple shared libraries the main NIF library depends on. (Zig doesn't support static linking yet so we have to ship shared ones. Related issue)
Release
- run the example
cd kinda_example
mix deps.get
mix test --force
mix compile --force
Owner
- Name: Beaver
- Login: beaver-lodge
- Kind: organization
- Repositories: 3
- Profile: https://github.com/beaver-lodge
Build ML with LLVM/MLIR/Elixir/Zig
GitHub Events
Total
- Issues event: 2
- Watch event: 4
- Issue comment event: 15
- Push event: 28
- Pull request review comment event: 10
- Pull request review event: 16
- Pull request event: 24
- Create event: 12
Last Year
- Issues event: 2
- Watch event: 4
- Issue comment event: 15
- Push event: 28
- Pull request review comment event: 10
- Pull request review event: 16
- Pull request event: 24
- Create event: 12
Committers
Last synced: over 1 year ago
Top Committers
| Name | Commits | |
|---|---|---|
| Shenghang Tsai | j****r@g****m | 29 |
Issues and Pull Requests
Last synced: 6 months ago
All Time
- Total issues: 1
- Total pull requests: 46
- Average time to close issues: N/A
- Average time to close pull requests: about 17 hours
- Total issue authors: 1
- Total pull request authors: 2
- Average comments per issue: 0.0
- Average comments per pull request: 0.46
- Merged pull requests: 43
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 1
- Pull requests: 21
- Average time to close issues: N/A
- Average time to close pull requests: about 13 hours
- Issue authors: 1
- Pull request authors: 2
- Average comments per issue: 0.0
- Average comments per pull request: 1.0
- Merged pull requests: 20
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- m0rt3nlund (1)
- jackalcooper (1)
Pull Request Authors
- jackalcooper (64)
- watsy0007 (2)
Top Labels
Issue Labels
Pull Request Labels
Dependencies
- actions/cache v3 composite
- actions/checkout v2 composite
- actions/upload-artifact v3 composite
- erlef/setup-beam v1 composite
- goto-bus-stop/setup-zig v1 composite
- seanmiddleditch/gha-setup-ninja master composite
- kinda HEAD
- ex_doc >= 0.0.0
- rustler_precompiled ~> 0.5
- zig_parser ~> 0.1.0