-
Notifications
You must be signed in to change notification settings - Fork 318
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support generation of Zig bindings #732
base: master
Are you sure you want to change the base?
Conversation
I ask for a little patience on this adjustment as I am still learning about the internal parsing mechanism of this api. @emilio, there any limit or deadline to still have PR open/draft? |
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 :) |
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 |
First of all, thanks for the feedback. The goal in this feature would not only be the issue of compatibility with 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 |
Pointers & Arrayshttps://github.com/eqrion/cbindgen/blob/b94318a8b700d0d94e0da0efe9f2c1bcc27c855f/tests/rust/ptrs_as_arrays.rs#L1-L19 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; |
@eqrion @emilio Since yesterday I focused on fixing the ones that are already marked. Now, I will need to look for other compatibilities between the languages. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good except for the empty test stubs and commented portions.
Looks great so far, awesome job! I made some quick observations, but please take them with a grain of salt. The 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! Also, I would like to share an important detail that discovered. ExampleRust lib: // #[no_mangle]
pub fn sum_add(x: i32, y: i32) -> i32 { x + y } Cargo config (toml) => lib: 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)});
} |
1dabc3a
to
5fcb6f4
Compare
New ProgressSourceCExpected// 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)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; |
f6f4270
to
044a0a9
Compare
If it is possible to review it, thank you. @emilio |
Why did you close it? |
Hi @BratishkaErik , This PR needs some refactoring due to the recent changes. I've closed it, although will not erase work already done. Edit: Any help will be appreciated! |
Hi, thank you for explanation! I was asking because I thought you met some difficult obstacle in design of language itself and was worrying if somehow it's impossible to generate using cbindgen (silly thought yes, sorry, accent on cbindgen). I'm not very proficient in Rust so if someone want to continue this work, I'll be glad to help by testing. |
* fn funtions * extern struct * none_argname
@BratishkaErik , My sugestion: run |
Obstacles always occur. Some points are difficult to see right away (such as the struct/enum/union correlation). Since the project is focused on c-like syntax, the zig syntax requires a bit of rework and this should not compromise other languages. |
Running $ cargo run -- -l zig tests/rust/union.rs | zig fmt --check --stdin
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/cbindgen -l zig tests/rust/union.rs`
$ cargo run -- -l zig tests/rust/union.rs > union.zig
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/cbindgen -l zig tests/rust/union.rs`
$ zig fmt union.zig
union.zig
$ zig run union.zig
union.zig:15:1: error: non-extern function has no body
fn root(a: ?*Opaque, b: Normal, c: NormalWithZST) callconv(.C) void;
^~ union.zig: const std = @import("std");
const Opaque = ?*anyopaque;
const Normal = union {
x: i32,
y: f32,
};
const NormalWithZST = union {
x: i32,
y: f32,
};
fn root(a: ?*Opaque, b: Normal, c: NormalWithZST) callconv(.C) void;
const std = @import("std");
pub const 10
pub const ':'
pub const '{'
pub const '\''
pub const '\t'
pub const '\n'
pub const U'\U00002764'
pub const U'\U00010083'
pub const 3.14
///
/// A single-line doc comment.
///
pub const 1
///
/// A
/// multi-line
/// doc
/// comment.
///
pub const -1
pub const 3
pub const 1
pub const ((0 << SHIFT) | XBOOL)
pub const (1 << (SHIFT | XBOOL))
pub const ()'A'
pub const ()()1
const Foo = extern struct {
x: [FOO]i32,
};
fn root(x: Foo) callconv(.C) void;
$ zig fmt constant.zig
constant.zig:4:11: error: expected 'an identifier', found 'a number literal'
pub const 10
^~ |
@BratishkaErik , make pull and try build again (union sample). |
Yep, it works now: const std = @import("std");
const Opaque = ?*anyopaque;
const Normal = union {
x: i32,
y: f32,
};
const NormalWithZST = union {
x: i32,
y: f32,
};
extern fn root(a: ?*Opaque, b: Normal, c: NormalWithZST) callconv(.C) void;
UPD: Was
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct Opaque Opaque;
typedef union Normal {
int32_t x;
float y;
} Normal;
typedef union NormalWithZST {
int32_t x;
float y;
} NormalWithZST;
void root(struct Opaque *a, union Normal b, union NormalWithZST c); pub const struct_Opaque = opaque {};
pub const Opaque = struct_Opaque;
pub const union_Normal = extern union {
x: i32,
y: f32,
};
pub const Normal = union_Normal;
pub const union_NormalWithZST = extern union {
x: i32,
y: f32,
};
pub const NormalWithZST = union_NormalWithZST;
pub extern fn root(a: ?*struct_Opaque, b: union_Normal, c: union_NormalWithZST) void; Normalized: pub const Opaque = opaque {};
pub const Normal = extern union {
x: i32,
y: f32,
};
pub const NormalWithZST = extern union {
x: i32,
y: f32,
};
pub extern fn root(a: ?*Opaque, b: Normal, c: NormalWithZST) void; |
@BratishkaErik , fixed constant semicolon. But need fix casts. try git pull again. Based from clike.rs cbindgen/src/bindgen/language_backend/zig.rs Lines 498 to 503 in fa441d7
|
Same errors ( const std = @import("std");
pub const 10;
pub const ':';
pub const '{';
pub const '\'';
pub const '\t';
pub const '\n';
pub const U'\U00002764';
pub const U'\U00010083';
pub const 3.14;
///
/// A single-line doc comment.
///
pub const 1;
///
/// A
/// multi-line
/// doc
/// comment.
///
pub const -1;
pub const 3;
pub const 1;
pub const ((0 << SHIFT) | XBOOL);
pub const (1 << (SHIFT | XBOOL));
pub const ()'A';
pub const ()()1;
const Foo = extern struct {
x: [FOO]i32,
};
extern fn root(x: Foo) callconv(.C) void;
constant.zig:4:11: error: expected 'an identifier', found 'a number literal'
pub const 10;
^~
|
@BratishkaErik, rebased (semicolon fix) and try again C macros need bypass on zig. |
There's missing equal sign between identifier and value:
...after fixing I get:
const std = @import("std");
pub const FOO = 10;
pub const DELIMITER = ':';
pub const LEFTCURLY = '{';
pub const QUOTE = '\'';
pub const TAB = '\t';
pub const NEWLINE = '\n';
pub const HEART = U'\U00002764';
pub const EQUID = U'\U00010083';
pub const ZOM = 3.14;
///
/// A single-line doc comment.
///
pub const POS_ONE = 1;
///
/// A
/// multi-line
/// doc
/// comment.
///
pub const NEG_ONE = -1;
pub const SHIFT = 3;
pub const XBOOL = 1;
pub const XFALSE = ((0 << SHIFT) | XBOOL);
pub const XTRUE = (1 << (SHIFT | XBOOL));
pub const CAST = ()'A';
pub const DOUBLE_CAST = ()()1;
const Foo = extern struct {
x: [FOO]i32,
};
extern fn root(x: Foo) callconv(.C) void;
|
@BratishkaErik , fixed now (missing '='). But "U'/U" need bypass.
pub const DELIMITER = ':';
pub const LEFTCURLY = '{';
pub const QUOTE = '\'';
pub const TAB = '\t';
pub const NEWLINE = '\n';
pub const HEART = @compileError("macro tokenizing failed: TODO unicode escape sequences");
// constants.c:18:9
pub const EQUID = @compileError("macro tokenizing failed: TODO unicode escape sequences"); cbindgen/src/bindgen/ir/constant.rs Lines 345 to 348 in f664770
|
For u/U I think logic should be similar to this, but IIUC this is wrong place to set this since it breaks other languages: diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs
index 4d11402..68a4aa4 100644
--- a/src/bindgen/ir/constant.rs
+++ b/src/bindgen/ir/constant.rs
@@ -344,7 +344,7 @@ impl Literal {
syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
0..=255 => format!("'{}'", value.value().escape_default()),
- other_code => format!(r"U'\U{:08X}'", other_code),
+ other_code => format!(r"'\u{{{:08X}}}'", other_code),
})),
syn::Lit::Int(ref value) => {
let suffix = match value.suffix() { pub const FOO = 10;
pub const DELIMITER = ':';
pub const LEFTCURLY = '{';
pub const QUOTE = '\'';
pub const TAB = '\t';
pub const NEWLINE = '\n';
pub const HEART = '\u{00002764}';
pub const EQUID = '\u{00010083}';
pub const ZOM = 3.14;
// ...
comptime {
@import("std").testing.refAllDecls(@This());
@compileError(std.fmt.comptimePrint("{u}, {u}", .{ HEART, EQUID }));
}
I have also tried to fix casts myself, but get no results BTW this syntax is also supported, but maybe I got ranges wrong: diff --git a/src/bindgen/ir/constant.rs b/src/bindgen/ir/constant.rs
index 4d11402..2accc0e 100644
--- a/src/bindgen/ir/constant.rs
+++ b/src/bindgen/ir/constant.rs
@@ -342,9 +342,9 @@ impl Literal {
syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
match lit {
syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
- syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
- 0..=255 => format!("'{}'", value.value().escape_default()),
- other_code => format!(r"U'\U{:08X}'", other_code),
+ syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() {
+ '\0' | '\n' | '\r' | '\t' | '\\' | '\'' | '\"' => format!("'{}'", value.value().escape_default()),
+ other_code => format!(r"'{}'", value.value()),
})),
syn::Lit::Int(ref value) => {
let suffix = match value.suffix() { const std = @import("std");
pub const FOO = 10;
pub const DELIMITER = ':';
pub const LEFTCURLY = '{';
pub const QUOTE = '\'';
pub const TAB = '\t';
pub const NEWLINE = '\n';
pub const HEART = '❤';
pub const EQUID = '𐂃';
// ... |
Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
Status: [ WIP ]
c_void
renamed toanyopaque
- see) supportstruct
,enum
,union
with C compatibility- [ ] trycomptime
supportTest Project
Clone and build: https://github.com/kassane/zFFI