The module equivalent would be different versions of the same module under the same import name. Can the C++ module system actually deal with that?
D has a simple and effective module system. It even treats unmodified .c and .cpp files as modules (though the C and C++ files are unaware of that!). D's builtin C compiler can even import other C files as modules.
Both C and C++ should have just copied it.
modern languages dont adopt them because they are terrible. a header import just dumps all the declarations into the global scope, so you have no idea where function or type usages comes from.
> There’s no rule that they have to only contain declarations
Same thing in Python, you can write statements at the top level of a module, and it will be executed whenever someone first import that module...
> or that they have to appear at the top of source files
Same thing in Python, you can `import` anywhere, and the context/order matters and overwrites existing scope declarations.
> or that they have to be .h files
Same thing in Python, the extension doesn't matter if you use import lib, only if you use the `import` keyword does the name/path matters.
no, its not. just off the top, I can say that D, Go, Nim, Rust and Zig dont do this.
also this isn't really an argument. instead of actually disagreeing with the original premise "headers are terrible" (which you cant), you've started a new argument, which is "other languages use headers", which no one was contesting.
static const float FIR_FILTER={
#include "fir_coefs.csv"
};
In modules, the implementation completely dominates, and it's impossible to figure out what the module API looks like from looking at the source. You need an IDE to generate an outline for you, which frankly sucks.
For C at least, headers are pretty much perfect because they are supposed to only contain public API declarations. It's just C++ that messed everything up with the inline and template keywords, which required implementation code to move into header files.
See: `cargo doc` that generates nice HTML documentation. See also language servers that provide completion using the same documentation comments.
Meanwhile headers, on top of the forced duplication of signatures between header and implementation, and the associated cost, is also implemented on terms of the preprocessor and textual inclusion. This compilation model makes code analysis much harder than necessary, because it becomes hard to know which piece of the preprocessed compile unit comes from which header.
This makes it hard to implement e.g. "remove unnecessary import" or "import symbol automatically" functionalities that are the bread and butter of module-based languages. Also impacts automatic refactoring negatively
Textual inclusion also allows a number of things modules do not.
The main disadvantage that they have, that they're stateful, is also true of module systems in many languages (e.g. python).
Maybe in a clean, smaller codebase with minimal dependencies.
But the textual inclusion means that including one header can suddenly break previously compiling code. Unless that header is completely inclusive, that can mean a while of chasing down transitive headers till you find the problematic one.
There are much more things going on in the module system that you'd have to know about if you care about what's actually going on.
So again, maybe if you have a smaller, cleaner or dependency free codebase, then it’s not an issue.
But in any codebase of major scale or legacy, headers can introduce several issues that aren’t possible with the module interface.
Headers are literal copy paste, and are entirely different. Anyone who has ever had to define WIN32_LEAN_AND_MEAN knows that there's _way_ more going on here.
Modules are a nice barrier to guard against side effects.
Surely you want your code to be portable to begin with?
One great thing - it's nice to have an interface[1] which already has tooling to let other languages use your private, hidden and compiled implementation.
If those C++ libraries that almost all of Python scientific work is built on had used modules[2] (or something similar) instead of headers, interfacing Python to those libraries would have been an immense effort.
Even with modules existing now, if you're writing something core and foundational in C++, it's not getting adopted like Numpy, BLAS, etc unless you also provide header files.
And that's just Python. Think of all the other languages that rely on performance critical code written in C++, and used with a C-only interface.
[1] Of course, your interface can't be a C++ interface either, so there's that.
[2] If modules were around at the time.
(I just realized that I don't completely understand how exactly the new modules work, so I have some further reading to do! )
Well, they're simpler, they're text, and the tooling already exists (I use swig all the time to automatically make C functions and symbols available to Java and Python. Some Lisps as well).
The reason the tooling exists, is because they are simple, and they are text. Looking at the C++ module definition, it's unclear to me[1] that similar tooling for C++ modules will ever be possible.
As long as the most popular languages (Python, Java, etc) rely on a C header file for inter-call interoperability, header files aren't going away. No one wants to manually write the interface into a C++ library by hand.
[1] I'm no C++ expert though - the problems I see with modules includes things like lack of a standard to grab the interface from the compiled module, complexity of C++ to determine the interface from the actual source code, etc.
Perhaps there's something specific about C++'s module design that makes this difficult, but Rust has modules and has similar tooling to enable you to automatically generate bindings to other languages (including Python and Java).
Rather, the language spec aspect of modules is about symbol visibility and collisions. In terms that interop cares about, moving a declaration into a module just adjusts its name mangling, without changing much else about e.g. its presence in the object file.
Well the one hurdle is that C++ is a lot more complex to process than C.
You'll note in my original comment upthread, I pointed out that the C++ libraries provide a C interface in their headers, and that is what tools like swig work with.
They don't (and can't, because of symbol mangling) read C++-only headers. Since they can't do it for headers (because the resulting symbols differ from compiler to compiler), why would they be able to do it for modules?
I'm of the understanding that C++ modules are unable to provide a C interface (`extern "C"` or similar), so it's kinda pointless reading them and generating a list of symbols, because then the generated stubs only work when the library is compiled with that specific compiler.
I think that it would be nice if the compiler can be instructed to output header files for the modules it reads. You'll still have the problem that the header now has mangled symbols in it (because modules cannot have `extern "C"`), but at least tools can generate stubs for that specific instance of the compiled library.
(PS. I'm happy to be corrected about the `extern "C"` thing - if I am wrong about that then, sure, tooling that scans C++ modules for `extern "C"` can in fact be made quite easily).
Further, swig does seem to support quite a bit of C++. IIUC it even avoids dealing with name mangling by generating wrappers that provide a C interface.
See, I did not know that :-)
> And either way the complexity of C++ and its name mangling is independent of headers vs modules, no?
You got me self-doubting here: C++ implementations are necessarily more complex than their interfaces.
The full complexity of C++ (not restricted to only name-mangling) is not usually found in headers intended for consumption by a C-interface compatible consumer.
The intention of headers is to serve as an interface alone, but absolutely nothing enforces that - nothing stops the developer from dumping the entire implementation into the header (and this is a popular route for some libraries that are header-only).
Modules are not intended to serve as an interface alone, so it is more likely that devs (myself included) will simply throw the entire implementation into a module, because it seems like a better idea to do so with modules.
IOW, common practice for headers is to only have the interface necessary to use the implementation, but I think that common practice for modules will be to have the implementation and interface included in a single module.
> Further, swig does seem to support quite a bit of C++. IIUC it even avoids dealing with name mangling by generating wrappers that provide a C interface.
Swig supports much of C++ in a smart way, by default. For things like demangling swig generates `extern "C"` wrappers (for functions, definitely - not so sure about class typenames and enum typenames).
But, to generate those wrappers from modules requires the original source code for those modules, and to use generated C++ wrappers, it needs to be compiled with the same compiler: Not a large hurdle, but it is definitely an additional concern compared to using the headers.
It will be interesting to see how this plays out: how long it takes until swig support for modules, or swig-like tooling for modules, comes along. It's still early days for modules, after all.
While I'm sure some people will do this (just like they do with header-only libraries, and are forced to do with C++ features like templates) modules do support the same interface/implementation split as headers.
I suspect that a lot of code will stick with that split for a couple of reasons. First, they already have it with headers, so migrating over to modules will be less work if they just leave things that way. And second, downstream translation units that import the module can't (with today's compilers) even start building until their dependencies' interfaces have finished building, so keeping the split may also turn out to improve build times.
Really though, I think the ideal here is not for library authors to have to maintain C-ish interface headers as a sort of conflation with that interface/implementation split. That should be generated by a tool- either something like swig (which knows more about the other side of the binding) or at least a C++ tool like you mentioned upthread. This is also how, for example, Carbon plans to do its C++ interop.
> But, to generate those wrappers from modules requires the original source code for those modules
This part I don't think is actually going to be an issue, because pre-compiled module interfaces are not really any more usable in C++-land than pre-compiled libraries. They also have to be compiled by the same compiler, with the same configuration, so people will have to provide the module interface sources just like they provide headers.
Modules on the other hand are more strict and would likely have made the job of writing language bindings easier and more predictable.
I’m not sure I agree with your logic about NumPy etc. You seem to be conflating technology limitations with policy ones. They could most certainly use modules too , since they exist in the compiled space anyway. They won’t because they make certain compiler compatibility guarantees but that’s a conflict of interests not of technical quality.
In general your argument feels a bit “older is better due to legacy support”. That would preclude almost any advancement in the space as well, because any new language feature (even just a keyword) would break all your examples above equally too.
There's two ways for tooling like swig to work with modules:
1. Read the compiled module and generate the stubs to call into it.
2. Read the source code for the module and generate the stubs for it.
Which of those, do you think, is easier than reading C (not C++) header files?
Parsing code to define an API is horribly fragile. A single macro or unknown keyword can break it. Hence why you had to specify C not C++ headers.
Having a statically defined metadata like C++ modules do would have greatly simplified binding generation
I'm having a hard time seeing the downside of the "fancy tools".
Edit: Ok I get it, disagreement is an encouraged reason for downvotes on HN. If a reason for an opinion is given, a refuting response is preferred, but in this case, no reason was given.
Initially I got the most downvotes I think I'd ever seen†, and then Thomas realised oops, yeah, he's not saying what I thought he's saying and it shot back up over the course of an hour or so. IIRC the auto-transcripts for example transcribe PKIX (an RFC explaining how we shall use the X509 certificate system for the Internet, and the regime resulting from that RFC) as "Peacocks". Hilarious. But, not a criticism of the person on the show (Ryan Sleevi I believe, who I rather like although we've never met in person).
I have written things I don't feel good about, especially trolling groups who believe something I don't, and deserved to be downvoted even if they were strictly true. But those mostly get -1 or -2 or something. Thomas' fans just keep hitting that down arrow apparently.
Other than disagreeing with Thomas, the main way I got sizeable negative voting was being wrong and then going to sleep without correcting it. Say 2+2 = 3 on a Thursday evening, but correct it to "1+2 = 3" a quarter hour later? Probably get one or two downvotes. Leave it that way overnight? Wake up to 10 downvotes at least. People will kick you if you don't react.
I remember picking an erroneous example in one of the endless Rust v C++ threads, and then going to bed, and yup, very clear I was wrong when I woke up.
A comment should be evaluated on its own, not based on who it was written by.
I try to avoid looking at the names until after the discussion is over. When I remember the fact I'm posting among giants, I find it difficult to post at all. HN is a place where the person who created an algorithm shows up to explain it to you. The fact such people post here is the number one reason to come here.
For example, if a comment claims an article is wrong about the development and use of the Nagle algorithm, I am very much not well-equipped to evaluate the comment on its own since that is not an area I know much about. If I see that comment was written by Animats, though, I can be reasonably confident I should trust the comment over the article, since he invented the algorithm.
If someone makes the statement that something in an article is incorrect, and fails to takes into account some minutiae, it is probably because the person is knowledgeable about the topic and believes it to be the case.
There is no need to research who the author is, or to mod the person down because he appears to be a contrarian that doesn't follow the hivemind. If anything, that's just an interesting subject to look into.
It is, but intent is distinct from correctness. I can believe that everyone is commenting in good faith and stating their honest beliefs, but that is little help when commenters take contrary positions and I would like to determine who is correct despite not being knowledgeable myself.
Under those circumstances, authorship is a useful signal. It doesn't need to be the sole determining factor, of course, but it's better than nothing.
I think it best to make your own opinion based on the arguments presented to you. Sure, someone may convincingly say wrong things, and you might end up believing them, but then it's up to you to develop your own ability to see through bullshit (of which there is a lot in the tech space, especially when you get close to VCs).
I'd hope most people would agree with you, since fame is not the same as expertise. A well-known author who happens to talk about a subject they are knowledgeable about can be worth paying attention to, but the same author talking about a completely different field may (should?) warrant more skepticism. Authorship can be a useful signal, but that doesn't mean it is always a useful signal.
> but then it's up to you to develop your own ability to see through bullshit
I mean, yes, that is ideal, but unfortunately but not all BS is equally detectable. Sometimes it really does take intimate knowledge to know someone is incorrect, and achieving that for everything just is not realistic for most, if not all, people.
And to be fair, there was no reason givin for the dislike of headers, so all it contributes to the discussion is that there is an alternative view.
I like modules, rust has been my daily driver at work for a couple years now, with some cuda for things that are too slow, and usually prototyping algorithms in matlab.
I can see the viewpoint though, that headers made for an easy way to convey an api before c++ added all the inline stuff.
The surest way to get massively downvoted here is to complain about downvoted, though. People seem to relish in it.