[HN Gopher] Zig / C++ Interop
___________________________________________________________________
Zig / C++ Interop
Author : simonklee
Score : 96 points
Date : 2025-11-11 08:09 UTC (14 hours ago)
(HTM) web link (tuple.app)
(TXT) w3m dump (tuple.app)
| swiftcoder wrote:
| This has reawakened the nightmares about Objective-C++
| kccqzy wrote:
| How so? Having written Objective-C and C++ separately but never
| written Objective-C++ before, I don't understand what's the
| hate it. Some of my favorite Mac apps from a decade ago were
| written in Objective-C++. I think Chrome still has some parts
| in Objective-C++.
| swiftcoder wrote:
| I don't think anyone who could help it wrote software in
| Objective-C++ directly.
|
| It was an unholy welding of the two languages that mostly let
| Objective-C apps adopt some libraries from the broader C++
| ecosystem. Plus the occasional cross-platform C++ codebase
| used it as a thin shim to provide a Cocoa-based UI...
| nly wrote:
| Objective C++ was great.
|
| Rename a file to .mm and start using Objective C APIs. Very
| good interop
| enricozb wrote:
| This idea about communicating size/alignment is actually
| something we're doing on the port of RediSearch to Rust [0]. We
| have an "opaque sized type" which is declared on the Rust-side,
| and has its size & alignment communicated to the C-side via
| cbindgen. The C-side has no visibility into the fields, but it
| can still allocate it on the stack.
|
| It's a bit ugly due to cbindgen not supporting const-generic
| expressions and macro-expansion being nightly-only. It seems like
| this will be a generally useful mechanism to be able to use
| values which are not traditionally FFI-safe across FFI
| boundaries.
|
| [0]:
| https://github.com/RediSearch/RediSearch/blob/cfd364fa2a47eb...
| jcelerier wrote:
| > When you want to embed a type, you need its definition, but you
| don't actually need the full definition. You just need the
| size/alignment.
|
| Aren't there ABI cases where e.g. struct foo {
| float X, Y; }
|
| would be passed in e.g. fp registers whereas
| struct { char[8]; }
|
| would not?
| threeducks wrote:
| Yes. For example consider this function to add two 2D points,
| which accepts and returns all variables entirely in xmm
| registers: https://gcc.godbolt.org/z/hPGKrh6W4 (surprisingly,
| gcc generates some fairly odd assembly code here)
| anematode wrote:
| It's fixed if you pass -fno-trapping-math. There could be
| junk in the upper half of the registers that causes a
| floating-point exception.
| threeducks wrote:
| Oh, that explains the seemingly useless movs. Thank you! I
| simply thought that it was a bug, since clang did not emit
| them.
| marler8997 wrote:
| Yeah this is correct. You don't want to pass these values
| around "by value" but, you should be able to "embed them" and
| pass "pointers to them". It's a middle-ground between a
| completely opaque type which you would also pass around by
| address, but, with the added benefit that you allocate your own
| storage for it.
|
| I sort of mentioned this in the blog but this is good
| clarification.
|
| > if you want to pass a shared_ptr to Zig, you need to pass a
| pointer to the shared pointer
|
| For lore, I believe this GitHub thread is where I first learned
| about the how types of the same size/alignment can still have
| different ABIs :)
| https://github.com/microsoft/win32metadata/issues/623#issuec...
| pyrolistical wrote:
| It's just both using c abi right?
| swiftcoder wrote:
| Seems like it. And the sizes are all hard-coded, which means
| you are probably wedded very tightly to a particular C++
| compiler.
| Jeaye wrote:
| Yeah, this isn't quite C++ interop on its own. It's C++ interop
| _via C_ , which is an incredibly pertinent qualifier. Since we
| go through C, opaque pointers are needed for everything, we
| can't stack allocate C++ values, we need to write extern C
| wrappers for everything we want to do (like calling member
| fns), and we don't get any compile-time type/safety checking,
| due to the opaque pointers.
|
| Direct C++ interop is doable, by embedding Clang into Zig and
| using its AST, but this is significantly more work and it needs
| to be done in the Zig compiler. As a Zig user, going through C
| is about as good as you can do, probably.
| marler8997 wrote:
| It's a bit more than your typical "interop via C". With a
| "sized opaque" type you actually can stack allocate C++
| values in Zig (and vice versa stack allocate Zig values in
| C++), i.e.
|
| fn stackExample() void { var some_cpp_type:
| c.SomeCppType = undefined;
| c.some_cpp_type_ctor(&some_cpp_type); defer
| c.some_cpp_type_dtor(&some_cpp_type); // ...
| }
___________________________________________________________________
(page generated 2025-11-11 23:01 UTC)