LuaJIT has a pretty great FFi which was later ported to standard Lua; Python’s ctypes provides a decent FFI.
Nim, Zig and Rust do too, but they have semantics much closer to C, so it’s almost free (especially when they can all use llvm directly for code generation, and Nim’s preferred way is to compile through C in the first place)
I hear about D often on HN but I never see it being used in the wild so to speak. When I looked at it, it seemed, like C++, a hodgepodge of features built up over time rather than being a more deliberately focused language.
I hate to compare and contrast but Rust comes to mind as one of the latter, probably because it has a somewhat more intensive and extensive RFC process for adding new features.
D has made its mistakes (there's no way to evolve a language over 20 years and not make a mistake here and there), but we also have a deprecation process to remove them.
This is even more impressive than LuaJIT's FFI in my opinion, which I previously thought was state of the art. You basically pass in a preprocessed header, and LuaJIT does the rest.
All this to say, I feel that any FFI that requires you to manually write bindings is rubbish.
We lived with manual bindings to interface with C for years. Building in direct support for C itself is a huge leap forward in usability. In several ways it's even better than C++'s support for C.
How does it deal with the macro-in-macro-in-macro soup that a non trivial amount of headers are? c2nim does a decent job but struggles with some of those types. gcc -E on the header works around it, so I wonder if c2nim could be made to operate similarly to what you’re describing.