[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)