Modern compilers have error messages that show you the whole chain of #includes, and on top is the filename and line number, whether it ends with ".c" or ".h" or anything else.
Exe size -- Sean usually includes a macro that lets you include the implementation only once (so you can use it as a pure header in multiple places, then as a definition just once).
Compile times -- if you use that macro, the implementation will be skipped by the preprocesser which is really fast.
Even if you build the whole code multiple times in your project, C compilers are also very very fast these days.
Many people follow Sean's style but use C++. That can be much slower to compile if you use fancy features. But if you avoid templates it's usually fine.
It's interesting that many big C++ projects are also header-only, but for totally the opposite reason -- they use templates everywhere so nothing can be compiled separately. That approach definitely does slow down compilation and increase your code size.
This issue was also on the mind of the C++ standards committee when they began work on the new module system. I'm not sure how the current spec behaves with respect to template instantiation, though.
Problem with template instantiation is that the mechanism is too general to be amendable to some kind of meaningful precomputation.
You need the package definition for the source-code-read time of whatever you're compiling, and the macros for its macro-expansion time. Practically speaking, these are tied together.
As a result, there do not need to be multiple specializations of the List type for strings and buffers. No matter how many different parameterizations there are of a given generic class, there is only one implementation. I think this is probably a good thing most of the time, since on average the performance gains of this form of specialization are insignificant compared to the costs of code size. You wouldn't want 100 or more different implementations of List clogging memory and needing to be JIT-compiled.
One of Java's constraints is also that all generic types are reference types. So one reason why it wouldn't be too useful to instantiate separate List<String> and List<Buffer> is because these are basically just List<ReferenceToObject> anyway. C++ templates can provide both value and reference semantics, and can work on primitive types such as `int` which may reside on the stack, whereas Java generics only operate on full-fledged garbage collected objects that are dynamically allocated on the heap.
The disadvantage of this approach is that it precludes some constructs that are possible in other languages. For example, in C++ a template method could take some type `T` and construct a new instance of it. In C++ you could do this, but in Java it's not legal:
<T> T broken() { return new T(); }
In C++ you could also create a new local variable of type `T` that will be allocated on the stack. This `T` can represent anything from a primitive `int` to a complex struct or class, or a point or reference to a dyanmically allocated object. The C++ code could then dynamically allocate a new instance of `T`.This makes it more difficult to use implement generic programming patterns analogous to things like std::allocator in C++, and to deliberately specify different implementations of parameterized types and algorithms. Fortunately these are not too significant of practical downsides in Java.
Instantiation at static link time would be feasible in C++, but runtime instantiation would require an heavy runtime which is frowned upon in C++.
as the C preprocessor? Are you kidding?
Wow. I am speechless. Going to dive in another day.
It's weird because it does feel like it violates the separation of interface and implementation. On the other hand, it's really efficient. It has the advantages of doing generics with the C preprocessor (efficiency and de-duplication of code, if nothing else), minus many of the disadvantages of doing generics with the C preprocessor.
Putting non-template code or data in headers is something I've seen people do for expediency, but it's more trouble than it's worth IMO. As soon as two files in the same project include it, you risk a future headache.
/Pedantic
(Templates get de-duplicated).
Template functions are implicitly inline.
Inline in C has a similar but subtly different definition.
What we call them doesn't matter. There is nothing wrong with putting code in one. You can call them source files if you prefer.
So glad you asked: The C Programming language, 2nd edition, page 82, chapter "FUNCTIONS AND PROGRAM STRUCTURE", as follows:
"There is one more thing to worry about--the definitions and declarations shared among the files. As much as possible, we want to centralize this, so that there is only one copy to get right and keep right as the program evolves. Accordingly, we will place this common material in a header file, calc.h, which will be included as necessary."
But if you need anything more than that it's all there. e.g. loading from memory, loading via callbacks, support for HDR images, support for custom allocators, preprocessor flags let you exclude code for unused image formats, etc etc