With the usual proviso that anything I say about C++ is 20 years out of date.
pragma's are non-standard compiler specific directives so might not work depending on the compiler.
The old way is standard 'C' pre-processor stuff.
'once' is popular because it saves all that boiler plate stuff if the 'ifndef' version if the editor doesn't write that for you.
The .h .vs .hpp as you said Giles seems to be mostly a matter of choice, I don't think I ever used .hpp and only
remember it's there because Boost uses it, and I quite like that with Boost implementing all/most of their stuff in the headers.
It's like its saying it a bit more than a straight forward header.
Remember that C and UNIX grew up together. That C was invented to write UNIX. And UNIX has never been fussed for file extensions, like Microsoft are with Windows. It's always preferred the "magic number" method instead (because, you know, users can change file extensions, but the "magic number" in the file header is in the data itself).
And also remember that they're called "preprocessor directives". That, in the original C toolchain, it wasn't even the compiler that dealt with all this. It was a separate "preprocessor" tool, that processed all these directives first and then sent it onto the C compiler, proper, for actual compilation.
(A touch of the old "UNIX philosophy" there. Do only one job, but do it well. And then chain a bunch of these tools together, with a pipe, to do more complex stuff.)
Now, in a modern compiler, the "preprocessor" probably isn't an actually separate program. It's just a "first stage" that the compiler itself does. But the logical design still remains "run the preprocessor to process the directives and then pass on the results to the next stage". It's logically still decoupled.
And, thus, what the "#include" does is just pull in the specified file at that point. That's it.
C compilers really only process ".c" files, but the preprocessor pulls in the ".h" files and concatenates them together, then passes over the big, long "unwrapped" C file for compilation.
In that sense, the C compiler doesn't really know what ".h" files are. It never sees them. Their file extension certainly doesn't matter, as that information never makes it through to the compiler.
And, similarly, the preprocessor just does text substitution. It doesn't actually comprehend anything in the file, besides the "#" directives. And preprocessor macros are just text macros - you can't do anything that involves understanding what the underlying code says, as preprocessors are just text manipulation and can't / don't read the actual code at all.
The "#include" guards - "#ifndef SYMBOL / #define SYMBOL / #endif" - are really just a way to implement "please only include this once" with such a dumb text manipulation tool. The "#pragma once" a more modern thing - not always supported - to try to tidy up this little "hack" (though, as Dio says, the implementation might just do what the include guards did anyway, so it's no more than a "shorthand" way of writing it).
I'd rather that they just added "#includeonce" to the preprocessor directives. Simply that, while including files, if you've already included that file before, then don't include it again.
As for the ".h" versus ".hpp" thing, it really doesn't matter to the preprocessor / compiler. They don't care either way.
But it's a way to hint, for human readers, that the stuff in the ".hpp" uses C++ stuff.
The big one that I use, which uses the ".hpp" extension, is "#include <glm/glm.hpp>" and, to be fair, in that case, I can see why they opted for the ".hpp" extension, because the GLM maths stuff is all templates and classes and stuff - it's very much C++ to its core, and using ".hpp" tells you that - but it's also because the GLM library is a header library. There are no ".c / .cpp" files, the ".hpp" files contain everything. You include and compile it all into your code, and there is no library to link against.
So, in that case, I can see why they opted for ".hpp". Because it's a header library, then only the header files can supply the hint. With ".h / .cpp", you know it's C++ because of the ".cpp" file. But if it's only ".h" files and nothing else, is that C or is that C++? Can't superficially tell. Therefore, in that case, I can see why they went with ".hpp" to say "it's C++" very clearly in the filename. And because it's all templates and stuff inside of it, it's very much C++ and can't be used with plain C.
Personally, I tend to use ".h" most of the time. But I have sometimes used ".hpp", but this was just, when I was using templates and things, to emphasise "this is C++".
The thing with C++ templates is that you've got to include the code in the header file. It's a limitation of C++ compilers - they all seem to do it - that you've got to put template code in the header, because it's expanded on first use, by the compiler, as it's compiling.
So, for example, I've got a "Vector" template class - "template <typename Type> class Vector" - and the methods have to be inline in the header file, because compilers (due to the way they work) won't let you do "declaration in the .h file, implementation in the .cpp file" with templates.
It's because most compilers are "single pass" compilers, so they need the info at or before the point of usage. Because they don't do forward references.
I mean, if you were doing the design of C / C++ from scratch today, then you'd just have it that the "preprocessor", along with doing expansions and substitutions, could actually read the code and then act as the "first pass" of the compiler to resolve symbols. Then the compiler would actually know about all the symbols when it starts compiling, and forward declarations wouldn't be a problem.
Like, the preprocessor has to run through all the code anyway. So, seeing as we already have that "first pass", it can do a quick scan at the same time. We need to tokenise, right?
They didn't do it back then because it would have been too complicated, and computers didn't have oodles of RAM to devote to such tasks. And "the UNIX philosophy" of just doing one thing well.
But, if you were approaching it today, then I'd say you'd note that, actually, you implicitly already have a "first pass" going on, when the preprocessor runs anyway. If it's got to scan its way through all the text anyway - to do preprocessing - then it can do tokenising and symbol resolution as well, then pass that onto the compiler.
That's how it probably should have been done. But it wasn't. So because compilers are "single pass" - no forward declarations - then C++ templates have to be defined on-the-spot (all inline methods) and that means there's C++ code in the header file. A way to make that clear to other developers on your project is to call it "Vector.hpp" rather than just "Vector.h".
But you don't have to. None of the tools actually give a shit, and you could call all the files ".banana", if you wanted to.
"Words ought to be a little wild - for they are assaults of thought on the unthinking."
- John Maynard Keynes
I've tried. Visual Studio is such a steep learning curve of things that only apply to Visual Studio and Windows development, I decided to learn Vim instead.
If they think you're crude, go technical; if they think you're technical, go crude. I'm a very technical boy.
Little stoned kiwi wrote:I've tried. Visual Studio is such a steep learning curve of things that only apply to Visual Studio and Windows development, I decided to learn Vim instead.
If you think Visual Studio is BAD ( and I see you may have your own reasons ) .. BUT I have to assume you NEVER touched ANDROID STUDIO and tried NATIVE CPP development !!
Andorid Studio is like "Arbre Magique" .. an invention some super-evil-sadist-nihilist-hate-all-human-race created in such a splendid way that APPEARS ( TO SOME ) to be a "useful/nice thing" ..
NOTE : if you want to keep me MILES away from "something" you have to ways .. 1. put some slug in there .. OR 2. put some Arbre Magique .. I CANNOT STAND THOSE THINGS !! ..
There is a theoretical performance advantage to using #pragma once, but it isn't real.
Using include guards a naïve compiler has to feed the entire source file through its preprocessor every time it encounters a file, strip out the block in the #ifdef and then compile what's left. Using the #pragma simply puts the file in a set of files to be ignored in future.
Of course real compilers know what an include guard looks like and treat it in the same way as the #pragma.
These days I just use #pragma once as it takes less typing and there's no risk of copy and paste errors. I haven't encountered a compiler that doesn't understand it for about 15 years.