Skip to content
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

resurrect emit-h #9698

Open
McSinyx opened this issue Sep 7, 2021 · 19 comments
Open

resurrect emit-h #9698

McSinyx opened this issue Sep 7, 2021 · 19 comments
Labels
emit-h This issue is related to generating .h files for C interop enhancement Solving this issue will likely involve adding new logic or components to the codebase. frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Milestone

Comments

@McSinyx
Copy link
Contributor

McSinyx commented Sep 7, 2021

I am converting a codebase from C++ to Zig. So far, I switched the entry point to Zig and recreate the C++ objects in Zig. The rest in the middle are still (extern "C") C++, i.e. we have a Zig sandwich here.

In order for the C++ code to use the Zig struct, there must be a header file and unfortunately it is also translated to Zig. Thus, this shared module is recognized both as originally declared and .cimport[…].struct_[…] and the (stage 1) compiler yields the error: expected type […], found […]. I made a minimal reproducer here hoping it'll help debugging.

I also noticed that stage 1 compiler has disabled C header emission (GH-6753), was this one of the reasons?

@Vexu
Copy link
Member

Vexu commented Sep 7, 2021

All struct types are unique, you cannot mix the automatically translated struct types with handwritten definitions.

@McSinyx

This comment was marked as duplicate.

@McSinyx
Copy link
Contributor Author

McSinyx commented Sep 7, 2021

Wow using emails with GitHub is awful, this is the original message:

I suppose the right way to do it here will be to @cImport the struct
from the C header? What should happen when the header is emitted
by zig build? What I wrote by hand was identical to what -femit-h
gives, except for the C++ compatibility.

@Vexu
Copy link
Member

Vexu commented Sep 7, 2021

I suppose the right way to do it here will be to @cImport the struct
from the C header?

Either that or add casts where needed.

What should happen when the header is emitted
by zig build? What I wrote by hand was identical to what -femit-h
gives, except for the C++ compatibility.

Unless there is a dependency loop, this should be pretty easy to do by chaining a EmitHStep (once such exists), to a TranslateCStep and then using that as a package.

@McSinyx
Copy link
Contributor Author

McSinyx commented Sep 7, 2021

Wouldn't the translated struct from emitted header still be different from the original one? I don't see how that will make the useFoo(makeFoo()) call in Zig legal.

@babelchips
Copy link

I agree. I'm inclined to think we'll have different structs in play even when headers can be emitted.

For example when writing a combination of Zig and C code where you wish to share a Zig defined structure:

  1. Emit a new Zig packed struct (assuming the emit feature exists)
  2. Include the header in your C code
  3. From C, call into an exported Zig function, passing the struct (or a reference to it) as an argument
  4. Surely the compilation will fail with error: expected type […], found […] ... ?

You couldn't even use cImport with cInclude of the exported header back in Zig because the header file would not exist initially (or be out of date). Not that you would want to use imported versions of your own packed struct in the Zig code.

So, outside of using void* and casting either side, what is the ultimate solution here?

@SpexGuy
Copy link
Contributor

SpexGuy commented Sep 12, 2021

You should define your structs in only one place. If you define them on the C side, you can cImport that in exactly one place, and everything will work. If you define them on the Zig side, you will also need to write your own extern function definitions, or post-process the generated .zig file from translate-c to fix up references.

@McSinyx
Copy link
Contributor Author

McSinyx commented Sep 12, 2021

You should define your structs in only one place.

Agreed, but this doesn't account for emitted structs. The only reason the struct was hand-written in the example was because emit-h has been disabled for stage 1 since 0.7. I think that seamless interoperation with C is one of the goals of Zig, and getting structures defined in Zig to work in C should be a supported use case, as it's crucial for writing C ABI libraries in Zig.

@andrewrk
Copy link
Member

getting structures defined in Zig to work in C should be a supported use case, as it's crucial for writing C ABI libraries in Zig.

Completely agreed. The plan is to resurrect the emit-h feature in the self-hosted compiler, which is under active development.

@andrewrk andrewrk added the emit-h This issue is related to generating .h files for C interop label Nov 20, 2021
@andrewrk andrewrk added this to the 0.10.0 milestone Nov 20, 2021
@andrewrk andrewrk changed the title extern struct recognized differently from code translated from C resurrect emit-h Nov 20, 2021
@andrewrk andrewrk added enhancement Solving this issue will likely involve adding new logic or components to the codebase. frontend Tokenization, parsing, AstGen, Sema, and Liveness. labels Nov 20, 2021
@andrewrk andrewrk modified the milestones: 0.14.0, 0.12.0 Jul 23, 2023
andrewrk added a commit to ikskuh/zig that referenced this issue Jul 31, 2023
This branch was not intended to introduce new test coverage on the
emit-h feature.

See ziglang#9698
QusaiHroub pushed a commit to QusaiHroub/zig that referenced this issue Aug 3, 2023
This branch was not intended to introduce new test coverage on the
emit-h feature.

See ziglang#9698
@Renha
Copy link

Renha commented Jan 25, 2024

The "Add a Zig compilation unit to C/C++ projects" point in "⚡ Maintain it with Zig" section of main advertised language features should be removed until the header generation is back in a working state

@mlugg
Copy link
Member

mlugg commented Jan 25, 2024

@Renha, why? That feature works entirely as described - header generation is a completely separate issue.

@Renha
Copy link

Renha commented Jan 27, 2024

So you could add Zig unit to the project, but not use it, and that was the intention?

@abhinav
Copy link
Contributor

abhinav commented Jan 27, 2024

@Renha lack of emit-h doesn't make Zig compilation units unusable.
Right now, it's best to hand-write a C header file for your Zig library if you're exposing a C ABI.
Fixing emit-h would generate the header file for you automatically.
Yeah, that would be more convenient but it's not unusable.

@Renha
Copy link

Renha commented Jan 28, 2024

Thank you, sorry if my tone sounded too harsh, I hope things will improve soon so that I would be able to use Zig in my project.

@mcharytoniuk
Copy link

mcharytoniuk commented Mar 28, 2024

As a workaround, you can usually get good results by asking an LLM to generate C headers from the Zig code. They are good at converting between formats.

@saltzm
Copy link

saltzm commented Apr 4, 2024

@abhinav Do you know of any public examples of handwritten headers for zig libraries that I could look at?

@abhinav
Copy link
Contributor

abhinav commented Apr 4, 2024

@saltzm Yeah, sure! Off the top of my head:

If I had to guess, most projects using Zig 0.11 or newer, and exposing a C API would be hand-writing them right now.

@saltzm
Copy link

saltzm commented Apr 4, 2024

Thanks for the quick response! I'm looking at the first one. So basically it looks like the functions in the header you linked correspond to the exported functions from c_api.zig, and then in the header you linked they're defining some opaque structs where they manually determine the size of each struct in order to allow the user to statically allocate these structs.

The latter bit is something I would want to do since I don't want my struct to be required to be heap allocated, but it definitely seems a bit difficult/convoluted.

I can think of a workaround that would be providing an allocator to a myobj_init function, and doing something like the FixedBufferAllocator pattern to allow people to allocate the object on the stack - they'd just still need to know how large to make the object.

Feel like I'm getting off topic here though, I think I get the gist now so thank you! Currently discussing this in the Zig Discord channel on zig-help by the way.

EDIT: Ah, I see that the Tigerbeetle people are just redefining the struct in C to avoid the opaque struct stuff the libxev person is doing.

@Des-Nerger
Copy link
Contributor

As of 0.13.0-dev.344+b2588de6c, a C header is generable through $ zig test -femit-h src/root.zig, but what about making the same through build.zig? I tried a solution suggested on Discord:

const install_lib = b.addInstallArtifact(lib, .{ .h_dir = .{ .override = .header } });
install_lib.emitted_h = lib.getEmittedH();
b.getInstallStep().dependOn(&install_lib.step);

, but got this: EmittedH_0.13.0-dev.344+b2588de6c.log. Is it a bug?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
emit-h This issue is related to generating .h files for C interop enhancement Solving this issue will likely involve adding new logic or components to the codebase. frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Projects
None yet
Development

No branches or pull requests