digitalmars.D - ImportC: On the issue of using a type from two different C files
- TheGag96 (27/30) Aug 26 2022 I've been trying out ImportC with libgit2 lately, and it's
- Mike Parker (7/16) Aug 26 2022 This has been discussed a couple of times in our Foundation
- Paul Backus (4/10) Aug 26 2022 This pretty much requires DMD to incorporate a C preprocessor,
- Dave P. (9/26) Aug 26 2022 IMO, the best solution is to implement some variation of C23’s
- Adam D Ruppe (56/62) Aug 27 2022 This is why I said "be an alias" in the chat thread. The `import
- TheGag96 (5/7) Aug 26 2022 So they're satisfied, at least for the moment, with how things
I've been trying out ImportC with libgit2 lately, and it's honestly a bit like magic!! Kudos to Walter for his work so far - this has been really cool. However, you may have seen Adam's [blog post](http://dpldocs.info/this-week-in-d/Blog.Posted_2022_05_16.html#importc-an -module-namespaces) about the issue that D's module namespaces create, where a type imported from two different .c files aren't compatible with each other. There's a [Bugzilla issue](https://issues.dlang.org/show_bug.cgi?id=22674) about it too, which Walter initially closed as WONTFIX because it seemed like it needed too difficult a fix. Zig advertises a similarly "magic" C importing feature. I made [an example](https://github.com/TheGag96/zigwithc) trying a similar scenario to what Adam described, and it seems we're actually in "good company": ``` <proj path>/src/main.zig:11:16: error: expected type '.media.<username>.4ea9c2fa-455c-480d-adbd-b533afd47647.home.<username>.Coding.Zig.zigwithc.zig-cache.o.8cf07da78935aad995196c635ddb6192 cimport.struct_tm', found '.media.<username>.4ea9c2fa-455c-480d-adbd-b533afd47647.home.<username>.Coding.Zig.zigwithc.zig-cache.o.46100012acc16f9e9848385e1fd2122e.cimport.struct_tm' clibB.doIt(thing); ^~~~~ <proj path>/zig-cache/o/46100012acc16f9e9848385e1fd2122e/cimport.zig:121:30: note: struct declared here pub const struct_tm = extern struct { ~~~~~~~^~~~~~ <proj path>/zig-cache/o/8cf07da78935aad995196c635ddb6192/cimport.zig:121:30: note: struct declared here pub const struct_tm = extern struct { ~~~~~~~^~~~~~ ``` I'd like to know how they plan to solve this issue themselves... Maybe we can help each other out in this regard. I asked Adam about it on the Discord, and his suggestion was:but there is a pretty easy solution to this in any case: make everything from importC be an alias back into an implicitly-created global namespaceWhat do you think? This at least seems like a sound idea.
Aug 26 2022
On Saturday, 27 August 2022 at 02:34:39 UTC, TheGag96 wrote:What do you think?This has been discussed a couple of times in our Foundation meetings. The most recent was in May. See Walter's section in the sumamry: https://forum.dlang.org/thread/uhgndrcnekedjqtarnwl forum.dlang.org Iain suggested the following:ImportC symbols, rather than going into a module space, should go into a global ImportC module. Each imported C file or header file is then appended to that module.Martin disagreed, and proposed an alternative:Martin said that one problem with that approach is that a D module would have access to symbols it didn't import. He suggested the ideal solution would be for each header to have its own module that doesn't change from one invocation of the compiler to the next.
Aug 26 2022
On Saturday, 27 August 2022 at 03:07:25 UTC, Mike Parker wrote:Martin disagreed, and proposed an alternative:This pretty much requires DMD to incorporate a C preprocessor, right? Since that's the only way it can reliably determine which headers are included.Martin said that one problem with that approach is that a D module would have access to symbols it didn't import. He suggested the ideal solution would be for each header to have its own module that doesn't change from one invocation of the compiler to the next.
Aug 26 2022
On Saturday, 27 August 2022 at 03:07:25 UTC, Mike Parker wrote:On Saturday, 27 August 2022 at 02:34:39 UTC, TheGag96 wrote:IMO, the best solution is to implement some variation of C23’s rules for tag compatibility where identical types with identical tags are treated as equivalent. Importing into a global C namespace means you can’t use two C headers with conflicting definitions of a type. Ideally, using C from D should be better than using C from C and allow you to resolve this situation via the module system while still allowing you to mix compatible types between C modules.What do you think?This has been discussed a couple of times in our Foundation meetings. The most recent was in May. See Walter's section in the sumamry: https://forum.dlang.org/thread/uhgndrcnekedjqtarnwl forum.dlang.org Iain suggested the following:ImportC symbols, rather than going into a module space, should go into a global ImportC module. Each imported C file or header file is then appended to that module.Martin disagreed, and proposed an alternative:Martin said that one problem with that approach is that a D module would have access to symbols it didn't import. He suggested the ideal solution would be for each header to have its own module that doesn't change from one invocation of the compiler to the next.
Aug 26 2022
On Saturday, 27 August 2022 at 03:07:25 UTC, Mike Parker wrote:Martin disagreed, and proposed an alternative:This is why I said "be an alias" in the chat thread. The `import c` module essentially selectively imports just the things actually declared from the magic module, giving the same behavior, but since the canonical name+definition is elsewhere and merged in there, the aliases won't cause type conflicts. The implementation might be easier said than done, since you'd have a pseudo-module being mutated through the import process. I expect a high probability of forward reference bugs cropping up. But the concept is something we can test by hand. Taking the same example from my blog that fails with importC on dmd master an doing instead: --- module __magic_importC; // representing all the C definitions imported struct FILE; extern(C) int printf(const char*, ...); extern(C) int fclose(FILE*); extern(C) void saySomethingToAFile(FILE*); extern(C) FILE* openAFile(); --- then --- module b; // it does a public selective import public import magic : FILE, printf, fclose, openAFile; --- and --- module b2; // again public selective import public import magic : FILE, printf, fclose, saySomethingToAFile; --- And now the test program compiles successfully: --- import b; import b2; void main() { auto fp = openAFile(); scope(exit) fclose(fp); saySomethingToAFile(fp); printf("Hello\n"); } --- and if you remove an import, you correctly get: d.d(7): Error: undefined identifier `saySomethingToAFile` thanks to the selective import mechanism. This scheme is compatible with both C and D declaration rules, doesn't have a big namespace surprise (the compiler should also just forbid importing the magic internal implementation module so people don't try to poke that directly), and.... might be doable. Again, possibility of bugs with the extraordinary magic module being mutated through the process, but since that's all inside dmd thanks to the selective import hiding anything you can't see in the right order anyway and the mutations are strictly additive we ought to be able to cover it up and make it work.Martin said that one problem with that approach is that a D module would have access to symbols it didn't import. He suggested the ideal solution would be for each header to have its own module that doesn't change from one invocation of the compiler to the next.
Aug 27 2022
I asked the Zig Discord about their plans. Andrew Kelley himself actually responded:projects should try to have only 1 c import for all C interfacingSo they're satisfied, at least for the moment, with how things work now. Hopefully D can solve it the most satisfyingly. ImportC is a great feature for D, I think.
Aug 26 2022