ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
35.12k stars 2.56k forks source link

implement emit-h for functions #2173

Open ul opened 5 years ago

ul commented 5 years ago

I started to write a simple LV2 plugin in Zig. Because I'm a novice in both, I do it step by step and try to build regularly to catch compilation errors early. I stuck while trying to export the first function required by LV2 API:

const lv2 = @cImport({
    @cInclude("lv2/lv2plug.in/ns/lv2core/lv2.h");
});

const std = @import("std");
const allocator = std.heap.c_allocator;

const PortIndex = enum {
    Gain   = 0,
    Input  = 1,
    Output = 2,
};

const Amp = struct {
    gain   : *const f32,
    input  : [*]const f32,
    output : [*]f32,
};

export fn instantiate(
    descriptor  : *const lv2.LV2_Descriptor,
    rate        : f64,
    bundle_path : [*]const u8,
    features    : [*]const *const lv2.LV2_Feature
    ) lv2.LV2_Handle {
        return @ptrCast(lv2.LV2_Handle, allocator.create(Amp) catch null);
}

When I try to compile it with zig build-lib src/main.zig --name simple-amplifier --ver-major 0 --ver-minor 0 --ver-patch 1 -dynamic I get compiler crashed with the following error:

TODO implement get_c_type for more types
fish: “zig build-lib src/main.zig --na…” terminated by signal SIGABRT (Abort)

For my inexperienced eye code snippet looks innocent and very familiar to how functions are exported in documentation examples.

Would you mind helping me to get it work?

ul commented 5 years ago

Played with it a little bit and it looks like that problem is with

descriptor  : *const lv2.LV2_Descriptor,

when I make its type just *c_void it compiles fine.

ul commented 5 years ago

Okay, it seems that LV2_Descriptor has function pointer fields and functions are not supported yet https://github.com/ziglang/zig/blob/85edf55b73acda669a2388949d7cf31118d70764/src/codegen.cpp#L8829-L8830

Are there any plans to fix it soon?

andrewrk commented 5 years ago

Thanks for the report. High level, what's going on here, is that although this is one of the central, important use cases for zig, you're actually sort of the first person (that I know of) actually exercising the use case. So you're pioneering the path. I appreciate that you're taking the time to report issues.

This is a problem with Zig's .h file generation code not supporting functions yet. This is actually quite contributor friendly if you are interested in helping decide exactly how zig should generate .h files for libraries. So that's one approach you could take to move forward on this. That particular panic would be fixed if we teach Zig what to put in .h files when exporting a function.

Another approach you can take is to simply disable .h file generation. I tried your example with --disable-gen-h and the panic disappeared.

ul commented 5 years ago

Thank you for the detailed answer! I will go with --disable-gen-h for now as I don't really need a header for this dynamic library.

I'm afraid that I'm not competent in C enough even for such simple task as header codegen, but I'll give it a go one of the weekends.

If you don't mind, one more related question: is it possible to mark a function as C ABI compatible but not export it? For example, instantiate is not supposed to be called by the host by name, but rather by pointer to it which is returned as a part of LV2_Descriptor from the only exported entry point lv2_descriptor.

andrewrk commented 5 years ago

is it possible to mark a function as C ABI compatible but not export it?

Yes, that is generally what the extern keyword does. See:

// this function is guaranteed to have the C ABI but is not exported (unless you use @export)
extern fn foo() void {
    // ...
}
ul commented 5 years ago

I published my simple example just in case someone else will find the idea to build LV2 plugins using Zig exciting as I did and will be interested in some bootstrapping: https://github.com/ul/simple-amplifier

emekoi commented 5 years ago

how would you determine if a type had been exported already?

andrewrk commented 5 years ago

It's a compile error to export the same name twice. So you can't do it on accident. And then have a single canonical place where that thing is exported (or not). Zig's declarative top level declarations should make that easy.

emekoi commented 5 years ago

i mean for the functions, so you can tell if a return or parameter type needs to be exported. or would that be a compile error?

andrewrk commented 1 year ago

Now that Zig has moved to the self-hosted compiler, this issue can be closed when Zig has emit-h tests (I don't believe that there is a test harness for it yet) and coverage is added for functions.

Let's additionally double check that the code in the original post here works before closing.