cbindgen icon indicating copy to clipboard operation
cbindgen copied to clipboard

Support generation of Zig bindings

Open kassane opened this issue 4 years ago • 26 comments

Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.

Status: [ WIP ]

  • [x] Add primitive types and C primitive types (c_void renamed to anyopaque - see) support
  • [x] Add struct, enum, union with C compatibility
  • [x] Functions
  • [x] Casts & Functors
  • [x] Pointer & Array types
  • [x] Include compatibility ~~- [ ] try comptime support~~
  • [x] review all stages of tests

Test Project

Clone and build: https://github.com/kassane/zFFI

kassane avatar Jan 23 '22 16:01 kassane

I ask for a little patience on this adjustment as I am still learning about the internal parsing mechanism of this api. So any help is welcome!

@emilio, there any limit or deadline to still have PR open/draft?

kassane avatar Jan 23 '22 16:01 kassane

Definitely not! I'll try to get to it and provide some feedback, but I don't close PRs unless they're superseded by other work, is definitely-unwanted functionality, or have gone completely stale for other reasons. So no rush :)

emilio avatar Jan 25 '22 01:01 emilio

That said, a high-level question from taking a look at Zig and so on: Is there anything cbindgen would be able to do that @cImport-ing a cbindgen-generated header wouldn't? Just trying to understand what the improvements over that would be.

emilio avatar Jan 25 '22 01:01 emilio

That said, a high-level question from taking a look at Zig and so on: Is there anything cbindgen would be able to do that @cImport-ing a cbindgen-generated header wouldn't? Just trying to understand what the improvements over that would be.

First of all, thanks for the feedback.

The goal in this feature would not only be the issue of compatibility with cImport because zig allows you to reuse C implementations (ffi) and can also translate C99 code into zig with zig translate-c.

But I think it would be interesting to evaluate a possible direct compatibility between rust and zig without relying on C code to make the transition for better security, even though for the rust side any foreign language would still be unsafe. If you look at the test file you will notice that I added zig-build ReleaseSafe mode which could be explained here.

kassane avatar Jan 25 '22 11:01 kassane

Pointers & Arrays

https://github.com/eqrion/cbindgen/blob/b94318a8b700d0d94e0da0efe9f2c1bcc27c855f/tests/rust/ptrs_as_arrays.rs#L1-L19 Generate code:

const std = @import("std")

pub fn ptr_as_array( n: u32,  arg: [3]u32,  _v: ?*u64) anyopaque;

pub fn ptr_as_array1( n: u32,  arg: [3]u32,  v: [4]u64) anyopaque;

pub fn ptr_as_array2( n: u32,  arg: []u32,  v: []u64) anyopaque;

pub fn ptr_as_array_wrong_syntax( _arg: ?*u32,  _v: ?*u32, _: ?*u32) anyopaque;

pub fn ptr_as_array_unnamed(_: ?*u32, _: ?*u32) anyopaque;

kassane avatar Jan 31 '22 14:01 kassane

@eqrion @emilio Who wants to test the syntax binding, just use zig fmt (lint) or build|run. There is also the possibility to use zig compiler-explorer

Since yesterday I focused on fixing the ones that are already marked. Now, I will need to look for other compatibilities between the languages.

kassane avatar Feb 01 '22 15:02 kassane

Looks great so far, awesome job! I made some quick observations, but please take them with a grain of salt.

The comptime code may be able to merged into the existing C++ constexpr code, as I believe comptime is acceptable in a superset of cases of constexpr.

It would be interesting to see if there is a way to use custom global allocators once that RFC goes through.

camconn avatar Mar 27 '22 18:03 camconn

Looks great so far, awesome job! I made some quick observations, but please take them with a grain of salt.

The comptime code may be able to merged into the existing C++ constexpr code, as I believe comptime is acceptable in a superset of cases of constexpr.

It would be interesting to see if there is a way to use custom global allocators once that RFC goes through.

No problem, thanks for the review! I was waiting for some feedback before adding any other changes.

Also, I would like to share an important detail that discovered. The zig does not depend on C ABI to interoperate with Rust or C++, because it can read the mangled function.


Example

Rust lib:

// #[no_mangle]
pub fn sum_add(x: i32, y: i32) -> i32 { x + y }

Cargo config (toml) => lib: staticlib => :x: cdylib, rlib, lib or dylib => :ok:

Run

$> objdump -x rust_mangle.so | grep sum_add
...   F.text   .... _ZN11rust_mangle7sum_add17hf6faed35db54da66E
$> echo "_ZN11rust_mangle7sum_add17hf6faed35db54da66E" | c++filt
rust_mangle::sum_add::hf6faed35db54da66

Zig app:

const std = @import("std");

extern "C++" fn @"_ZN11rust_mangle7sum_add17hf6faed35db54da66E" (x: i32, y: i32) i32;
//Why not rust? Missing librust

pub fn main() void {
     const x: i32 = 43;
     const y: i32 = 32;
     const add = @"_ZN11rust_mangle7sum_add17hf6faed35db54da66E";
     std.log.info("Value add {} + {} = {}\n",.{x, y, add(x, y)});
}

kassane avatar Mar 28 '22 12:03 kassane

New Progress

Source

https://github.com/eqrion/cbindgen/blob/a151cbfb720fa331cc24c5e439eb2c47cfcfb037/tests/rust/fns.rs#L1-L16

C

https://github.com/eqrion/cbindgen/blob/a151cbfb720fa331cc24c5e439eb2c47cfcfb037/tests/expectations/fns.c#L1-L16

Expected

// zig - translate-c generator

pub const Fns = extern struct {
    noArgs: ?fn () callconv(.C) void,
    anonymousArg: ?fn (i32) callconv(.C) void,
    returnsNumber: ?fn () callconv(.C) i32,
    namedArgs: ?fn (i32, i16) callconv(.C) i8,
    namedArgsWildcards: ?fn (i32, i16, i64) callconv(.C) i8,
};
pub extern fn root(_fns: Fns) void;
pub extern fn no_return() void;

Generated (cbindgen)

https://github.com/kassane/cbindgen/blob/a151cbfb720fa331cc24c5e439eb2c47cfcfb037/tests/expectations/fns.zig#L1-L13

const std = @import("std");

pub const Fns = extern struct {
   _noArgs: ?fn() anyopaque,
   _anonymousArg: ?fn() anyopaque,
   _returnsNumber: ?fn() i32,
   _namedArgs: ?fn(first: i32, snd: i16) i8,
   _namedArgsWildcards: ?fn(_: i32, named: i16, _1: i64) i8,
};

extern fn root(_fns: Fns) anyopaque;

extern fn no_return() anyopaque;

kassane avatar Apr 08 '22 15:04 kassane

If it is possible to review it, thank you. @emilio I haven't solved the comptime issue yet, due to the fact that I need to refactor ty.rs in the arguments part regarding the generic types. Other than that, I think the basics already work.

kassane avatar Jun 26 '22 18:06 kassane

Latest function_ptr test:

  • rust https://github.com/eqrion/cbindgen/blob/e793448932cc550b2d7e0f9e15ea6f39eff5f870/tests/rust/function_ptr.rs#L1-L6

  • zig https://github.com/eqrion/cbindgen/blob/d18704e69c90aa7e918aa7d94a72039680fd3c4a/tests/expectations/function_ptr.zig#L1-L11

expected:

pub const MyCallback = ?fn(a: usize, b: usize) anyopaque; // zig stage1
pub const MyCallback = ?* const fn(a: usize, b: usize) anyopaque; // zig stage2/3 (self-hosting)

Fix - removing : in function_ptr.

References:

kassane avatar Nov 22 '22 20:11 kassane