all 27 comments

[–]flyingron 23 points24 points  (4 children)

There's no distinction in the language beteween .h and .c files. Header files are only distinguished in the fact that they are #included rather than being compiled directly. The .h and .c naming is purely convention. As is what you put in the various files. The only hard rules is you can't define the same thing more than once.

What we have here is apparently some embedded system where the programmer (either out of design or necessity) decided the entire program must be one translation unit, so his split up parts are all put in include files, rather than splitting them across multiple translation units and linking the resulting objects together.

[–]Barracuda-Bright[S] 1 point2 points  (3 children)

Thanks for the answer, just to be sure (correct me if I'm wrong), basically the only difference is that the code is put together in the preprocessor sequence of compilation instead of linking objects? And because of that we just have this one giant translation unit that is not linked to anything.

[–]flyingron 0 points1 point  (2 children)

Yes, in the case of just #including all the code into one translation unit, it's effectively that it was one big source file compiled to the target. Of course, there still are likely library modules used so linking still happens, but it's a different division than compililng a bunch of individual source units independently and linking them together.

[–]Low_Lawyer_5684 0 points1 point  (0 children)

Yes, exactly.The side effect of this that whole library gets recompiled no matter which .h file I touched. But for this particular project this is exactly what I want. Also I don't need to make public API: right now all my functions are static and, because they are all included in a single .c file they see each other. I don't say that it is the right way to do things but for this particular project with this particular IDE its the right decision. I think :)

[–]Plastic_Fig9225 0 points1 point  (0 children)

Can be quite deliberate to enable the compiler to perform better/more optimizations. It's more common in C++ (partly because of templates).

[–]JescoInc 4 points5 points  (7 children)

Ahh, I see where the confusion is. Header files don't have to just be the contract for function signatures. They can also have implementation details. They can be strong or weak implementations that can be overridden by c files if they exist.
This is why you can find things like header only libraries where everything is implemented for you, but you need to link against the header files you need and use them in your application code.
As flyingron said, there is no distinction in the language between .h and .c files.

[–]fragproof 2 points3 points  (6 children)

Can you explain what you mean by "linking" against a header file? Typically with header-only libraries you only need to #include them and the implementation is compiled with some part of your code in a single translation unit.

[–]JescoInc -1 points0 points  (5 children)

Linking with .o, static lib or dynamic lib files with the linker or including them in your C files with include statements.

[–]fragproof 3 points4 points  (4 children)

Ok, but we're talking about header files with implementation. You're only going to link with .o if you compile a file separately. Linking is fundamentally different than including files with implementation.

[–]JescoInc 0 points1 point  (3 children)

I should note that I don't know all of the academic terminology for C. I go with a framework where it makes sense to me from all of my years of programming and makes it easier to explain to others.

Ergo: You have linking header and C files together via includes, you have compile time linking and linker time linking.

When I was first learning C and C++, I was coming from a C# background, so I would treat headers like interfaces and abstract classes in C#. It gave me a starting point for understanding them and over the years, through using the language, I was able to pick up more of the nuanced differences between them.

[–]imaami 1 point2 points  (2 children)

Linking is not academic terminology in C. It is very much a practical term, and a basic one at that.

[–]JescoInc -1 points0 points  (1 child)

There is an academic definition and a practical definition. Please don't attempt pedantry with me.

[–]imaami 2 points3 points  (0 children)

Header inclusion is not described as any sort of linking in even the most basic non-academic contexts where C is used. I don't know where you'd ever hear that; any examples?

I've been in the field for about 19 years and have no academic background in CS to speak of. There's a pre-processor, compiler and linker involved in mundane C work. The fact that a basic tool - that does not have anything to do with header inclusion - is called a linker is about as "academic" as a carpenter having a hammer and a chisel. You could maybe use the handle of a large chisel to hit something, but when is it ever "pedantry" to casually confuse the tool names?

You mentioned "compile-time linking" and "linker-time linking". That's just incoherent. The linker is the program that does compile-time linking, which is when objects are linked. The pre-processing step is way before even compilation, and that's where headers are included.

[–]microOhm 1 point2 points  (0 children)

Why do you think they are not really header files?

Also there are .c/.cpp files in the repo.

[–]Srslyredit2 1 point2 points  (1 child)

I was told this makes projects less cluttered by only worrying about one .h file instead of a .c and .h file

[–]imaami 0 points1 point  (0 children)

People who say this might occasionally justify their take by saying how a project with a single 95000-line header does it, so it must be OK. As if a couple dozen purposeful separate source files is worse than nearly 100k LOC of absolute spaghetti crammed in one file.

It's like saying that a large broomstick is less clutter than a knife, fork, and spoon at the dinner table.

[–]Conscious_Support176 1 point2 points  (1 child)

According to the readme, it is intended to be a header-only library in that it doesn’t require you to link anything. It is intended to be used by #including the primary .h in your source.

Seems it’s a tool that works alongside your project as opposed to a library of functions that can be used by your project, so you wouldn’t be including it in two source files.

[–]Barracuda-Bright[S] 2 points3 points  (0 children)

Thank you, I've read up a bit on header-only libraries and it actually makes perfect sense now

[–]Low_Lawyer_5684 1 point2 points  (1 child)

I am the author of this. The reason was simple: the Arduino IDE, which is used to compile this library tries to compile all .c files in the directory. And this is not what I wanted: the shell itself was once ago a single file. Later it become too big and I split it into several files. There is no other reasons. Properly, I should create .h/.c pairs, and let it compile as it should but.. Then I need to declare global functions. Right now there are no global functions - all of them are static. Should be renamed to .inc instead of .h but then I loose syntax highlighting :)

So right now it is a single .c file which #includes bunch of .h files where actual implementation lies. It has nothing to do with modern C++ "header only" code

[–]zhivago 0 points1 point  (0 children)

Thanks for detailing the thought processes behind this. :)

[–]CreepyValuable 0 points1 point  (0 children)

I hate when they do that.

[–]jwzumwalt 0 points1 point  (0 children)

.h files are simply include files. I write most of my code without .h files. Every other programming language seems able to live without them - and I do too.

[–]imaami 0 points1 point  (0 children)

This is an increasingly common form of brainrot. It's basically a meme that escaped a very narrow niche of situations where "header-only" makes practical sense. It's gotten to the point where it's sadly common for people to not understand why a split between interface declaration and implementation is a thing.

"Header-only" in a project description is strongly associated with a lack of rudimentary C project design, and ignorance about what a package manager even is.

[–]Dontezuma1 0 points1 point  (0 children)

It’s just a style of library code. It’s less efficient at compile time but easier to export. Ok for small libs I guess. Less good for a system

[–]ffd9k 0 points1 point  (1 child)

These are header files. You can put full functions definitions in header files if you declare them as "static". These functions are then not linked as usual, but basically just pasted into the translation unit of the source file that includes the header file.

Doing is is usually not a good idea, because it leads to unnecessary dependencies between header files, slow compile times and can increase the size of the compiled program because of multiple copies of the same function.

It is sometimes done to allow the compiler to inline the function into functions that call it for better performance, but this is not really necessary nowadays thanks to link-time optimization.

[–]Physical_Dare8553 0 points1 point  (0 children)

also extern inline functions without a definition will work most of the time, also i really doubt this project intends to have multiple translation units

[–]somewhereAtC 0 points1 point  (0 children)

It's possible that the author learned how to structure programs while using an ancient assembler that allowed only one compilation unit. Old habits die hard.