C Header File conventions

Programming, for all ages and all languages.
User avatar
lortega
Posts: 2
Joined: Tue Jun 14, 2022 1:37 am
Libera.chat IRC: lortega

C Header File conventions

Post by lortega »

I'm following along the meaty skeleton tutorial and had a few questions about the header file conventions used. Unfortunately, seems the account that created it is no longer active, so I'm hoping others are aware of the conventions being used.

I see three distinct conventions used on the include guards in this tutorial, they are:
  • all caps version of the filename, including extension
  • all caps name including preceding underscore and library name (e.g. "_KERNEL_TTY_H")
  • the second convention, but with the define having a "1" after the guard name
For anyone who's viewing this thread in hopes of learning about the conventions, here's an explanation of the first two. The first rule is fairly standard, by naming your preprocessor guard like this, you avoid naming conflicts with other headers. The second rule is used since that particular header is being compiled as part of libc, and convention holds that global headers have this preceding underscore to further avoid conflict with project specific headers. If anyone has any corrections or clarifications on this, please let me know.

My question is mostly about the third rule I listed. Why is the "1" following the define? The author of that page said most elements of their code is intentional, even the weirder ones, as they either directly serve a purpose for the tutorial, or help to simplify future work. I tried searching for this convention but could not find anything about it.
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: C Header File conventions

Post by max »

There is no further intention behind this. #ifndef will only check if the symbol is (not) defined, the value does not matter.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: C Header File conventions

Post by Solar »

lortega wrote:The second rule is used since that particular header is being compiled as part of libc, and convention holds that global headers have this preceding underscore to further avoid conflict with project specific headers. If anyone has any corrections or clarifications on this, please let me know.
Any kind of identifier that begins with two underscores, or one underscore and an uppercase letter, is "reserved for the implementation", i.e. not legal in application code. (There are more reserved identifiers, like E_*, but they are reserved for different purposes.)

"The implementation" used to be the combination of compiler and library. Since compiler and library could conceivably come from two different upstreams, you have to "hope" that they don't use the same identifier for different things. Prefixing identifiers with _MYCLIB_* or _MYCOMPILER_* reduces that probability to next-to-zero.
lortega wrote:My question is mostly about the third rule I listed. Why is the "1" following the define?
Safeguard against being lazy. It makes no difference for #ifdef / #ifndef, but if you just wrote #if, having the identifier defined to 1 will give you a true result, while having it defined to empty will give you a somewhat funky error message.

There's yet another convention, that is to define the header guard to itself.

Code: Select all

#ifndef MYAPP_MAIN_H
#define MYAPP_MAIN_H MYAPP_MAIN_H
In the (admittedly far-fetched) case that someone wanted to use MYAPP_MAIN_H as a variable or function name, both "#define MYAPP_MAIN_H" and "#define MYAPP_MAIN_H 1" would cause a compilation error, while the above construct doesn't.
Every good solution is obvious once you've found it.
User avatar
iansjack
Member
Member
Posts: 4685
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: C Header File conventions

Post by iansjack »

Excellent explanation.

But it would be more consistent to stick to one convention (use 1 everywhere or use 1 nowhere) rather than mixing styles.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: C Header File conventions

Post by kzinti »

Here is another convention:

Code: Select all

#pragma once
Although 'non-standard", it is effectively supported by all compilers.
nullplan
Member
Member
Posts: 1766
Joined: Wed Aug 30, 2017 8:24 am

Re: C Header File conventions

Post by nullplan »

kzinti wrote:Although 'non-standard", it is effectively supported by all compilers.
I would disagree. It is not supported by at least one compiler I use regularly, and that is one more than has an issue with normal include guards. Clang, GCC, and MSVC make up a large chunk of the compiler market, but they are not all of it.
Carpe diem!
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: C Header File conventions

Post by kzinti »

Not many people get to work on OS-9. Lucky you.
User avatar
lortega
Posts: 2
Joined: Tue Jun 14, 2022 1:37 am
Libera.chat IRC: lortega

Re: C Header File conventions

Post by lortega »

Wow thanks everyone so many quick responses! Glad to see its just a separate convention and not some alternate functionality I was missing. In that case, I'll do what makes the most sense to me and was suggested in this thread, and go along with using the same style (so no "1" for me). I understand it helps if someone uses #if rather than #ifdef, but thats not something I would personally use nor any collaborators I can think of, so seems to make more sense just to stick to a programming style that makes the most sense for me.
nullplan
Member
Member
Posts: 1766
Joined: Wed Aug 30, 2017 8:24 am

Re: C Header File conventions

Post by nullplan »

kzinti wrote:Not many people get to work on OS-9. Lucky you.
I feel so privileged.

...he said, bitterly, as he waited another five minutes for the bloody prelinker to finish.
Carpe diem!
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: C Header File conventions

Post by Solar »

kzinti wrote:Here is another convention:

Code: Select all

#pragma once
Although 'non-standard", it is effectively supported by all compilers.
And I've run into problems with that at least twice (once in the context of softlinked header files, once in the context of a header file being copied into several locations of the source tree). I don't really get why anyone would go for a #pragma of any kind for something that can be done perfectly well without.
Every good solution is obvious once you've found it.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: C Header File conventions

Post by kzinti »

Solar wrote:once in the context of softlinked header files
Yes I can see how that could be a problem... As I understand it GCC and clang both handle them properly.
Solar wrote:once in the context of a header file being copied into several locations of the source tree).
Your problem here is that you copied the same header file in several locations, not that you used #pragma once. You could also run into issues with #ifdef/#define if you modified one of these file and forgot to update the other locations. Did you end up copying the file because of the softlink issue above?
Solar wrote:I don't really get why anyone would go for a #pragma of any kind for something that can be done perfectly well without.
- Can reduce build time as the compiler doesn't need to open and read the same file again after the first #include of the file in the translation unit.
- Doesn't pollute the global namespace with header guards
- Less error prone
- Less maintenance on the programmer's part

In my experience, I've run into problems with header guards a few times where I've only hit one issue with #pragma once: a compiler that didn't support it in a cross-platform project.

More info here: https://en.wikipedia.org/wiki/Pragma_once
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: C Header File conventions

Post by Solar »

kzinti wrote:
Solar wrote:once in the context of a header file being copied into several locations of the source tree).
Your problem here is that you copied the same header file in several locations, not that you used #pragma once. You could also run into issues with #ifdef/#define if you modified one of these file and forgot to update the other locations. Did you end up copying the file because of the softlink issue above?
The issue was that the developer thought that "same file name, same contents" would make the compiler consider it the same file and thus avoid redeclaration / redefinition issues. Whereas the compiler considered those separate files.

You're losing a level of control with #pragma once.
kzinti wrote:
Solar wrote:I don't really get why anyone would go for a #pragma of any kind for something that can be done perfectly well without.
- Can reduce build time as the compiler doesn't need to open and read the same file again after the first #include of the file in the translation unit.
GCC does not re-read header files since at least version 3.0...
kzinti wrote: - Less error prone
To the contrary, which was the reason for my comment. I despise "automagic" solutions to non-existing problems, because when those go awry, finding out what your actual problem is can be orders of magnitude more annoying than e.g. a mistyped header guard.

(Have you ever had a hash collision in a Git repository? I have. No fun.)
kzinti wrote:In my experience, I've run into problems with header guards a few times...
All of which boil down to developer error, not compiler quirk...
kzinti wrote:...where I've only hit one issue with #pragma once: a compiler that didn't support it in a cross-platform project.
Well, cross-platform is pretty much all I am doing, and in some cases with some really old compiler. (Dice C on the Amiga.)
kzinti wrote:More info here: https://en.wikipedia.org/wiki/Pragma_once
Gee, look at the "Caveat" section! Pretty much exactly what I wrote above! :D
Every good solution is obvious once you've found it.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: C Header File conventions

Post by kzinti »

You got me thinking and now I don't think one method is (much) better than the other. They both have pros and cons. I just wanted to bring it up as another option since this was the subject of this thread.
thewrongchristian
Member
Member
Posts: 424
Joined: Tue Apr 03, 2018 2:44 am

Re: C Header File conventions

Post by thewrongchristian »

I use makeheaders, from the same guy that wrote SQLite.

Makes header management a breeze. Each source file has a single header (plus any compiler/system headers required), and that single head is generated from the sources.

If one source file references a function defined in another source file, that function is prototyped and inserted into the source's header file. It makes my C source really easy to manage.
User avatar
eekee
Member
Member
Posts: 872
Joined: Mon May 22, 2017 5:56 am
Location: Kerbin
Discord: eekee
Contact:

Re: C Header File conventions

Post by eekee »

Some years ago I read that slow compilation was considered one of C++'s bigger problems, and that re-including header files was part of the reason it was so slow. Guards don't really help with this; the preprocessor still has to parse the entire file every time its loaded to find the correct #endif. I assume #pragma once is an attempt to deal with this speed issue. That's the only positive thing I have to say about it, anyway.
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
Post Reply