0

There are lots of similar answers to this question such as this one:

  1. Include .cpp instead of header(.h)
  2. https://stackoverflow.com/a/1001723/637142 (this answer really says it is bad)

Stack overflow will prbably say I have a duplicate question but I still do not understand after looking into all the similar questions why people discourage this code:

#ifndef _FOO
#define _FOO

// my comment that I only have to write once
int some_method()
{
    return 1;
}

#endif

Call this foo.cpp. Why will it be bad practice to do something like this where foo.h is excluded? Compile time will be slower but how much slower? I believe it will be milliseconds slower. Why is being lazy a bad thing (https://stackoverflow.com/a/9253825/637142). I just want to write something that works, is well documented etc..

Benefits I see when using this technique are:

  1. I do not have to write comments twice. If I decide to change the comments of a function I will also have to change the comments on the header file.

  2. If I change the signature of a method then I will also have to change it on the header file.

  3. I use F12 shortcut on visual studio a lot to go to the definition of a method for example. Visual studio by default will go to the header file. I want to go to the actual implementation. Seeing code plus comments is better for me than just seeing comments.

The only negative side I see is https://stackoverflow.com/a/1001667/637142 . But that is less work than having to write everything twice. Just a few includes and thats it.

Edit

I know I am probably wrong and I a missing something. Since I am a C# developer its hard for me to understand it. I just do not understand why this file will be included multiple times. Yes it will be included multiple times but only the first time will be compiled. The other times it will not be compiled correct?

Tono Nam
  • 34,064
  • 78
  • 298
  • 470
  • 4
    This doesn't actually work because if you `#include` a source file in more than 1 translation unit you will still have multiple definitions. It will only work for very simple examples. – François Andrieux Sep 02 '20 at 17:03
  • 1
    (1) Simply don't write API describing comments twice. They belong in a header only. (2) You can define your functions in a way that will only be valid given a matching prior declaration. (3) VS has more shortcuts beyond F12, and can go to definitions as well. – StoryTeller - Unslander Monica Sep 02 '20 at 17:06
  • So for simple examples it will be fine? – Tono Nam Sep 02 '20 at 17:06
  • `#include` tells the preprocessor to replace the `#include` statement with the contents of the specified file. This has two immediate downsides: You have to compile more code more often. Make a change in one of those header-fied cpp files and you'll have to rebuild EVERYTHING. EVERYTIME. Not so much of a problem with 2-3 files, but ten or a hundred or a few thousand files in the project and there goes your afternoon. Problem 2, if multiple files include the cpp file, you have multiple definitions of everything in the cpp file. – user4581301 Sep 02 '20 at 17:07
  • But because of the `#ifndef` it will not be compiled again @user4581301. It will be slower to compile, but how much slower? – Tono Nam Sep 02 '20 at 17:08
  • 3
    @TonoNam - You seem to be missing the fact that an include guard only prevents multiple inclusion in a **single** translation. When including in multiple **different** translation units, each one will have a *separate* definition of the function defined within. – StoryTeller - Unslander Monica Sep 02 '20 at 17:10
  • 2
    With a good build tool, once you compile a cpp file it stays compiled unless you request otherwise. You don't have to build it again. If you have hundreds or thousands of files, you suffer a long build once. and then after that only what you change is recompiled. Except for the stuff that's included. Everything that includes a changed file must be recompiled. By separating the implementation from the interface, when you change an implementation detail, you compile one file. When you change the interface, you compile everything that uses the interface. – user4581301 Sep 02 '20 at 17:20
  • 1
    If you combine interface and implementation and include the cpp file, you make an implementation change, you compile everything that uses the cpp file. This adds up as the project grows and eventually you make a change, rebuild everything, and sit around for hours before you can test that change. For a few months in around 1998, I worked for a primarily-hardware company that mandated full builds of everything every time. That company's been dead for over 20 years. Their productivity couldn't keep up as their software component grew. – user4581301 Sep 02 '20 at 17:24
  • Side note: Some build tools are smart and automate a lot of the build management . They see you have a cpp file in the project folder and assume they should compile and link it. This guarantees multiple definition. – user4581301 Sep 02 '20 at 17:33
  • Some handy reading: [How does the compilation/linking process work?](https://stackoverflow.com/questions/6264249/how-does-the-compilation-linking-process-work) [How does #include work in C++?](https://stackoverflow.com/questions/35720656/how-does-include-work-in-c) [The One Definition Rule](https://en.cppreference.com/w/cpp/language/definition#One_Definition_Rule) – user4581301 Sep 02 '20 at 17:36
  • This doesn’t address the question, but names that begin with an underscore followed by a capital letter (`_FOO`) and names that contain two consecutive underscores are reserved for use by the implementation. Don’t use them in your code. – Pete Becker Sep 02 '20 at 19:55

2 Answers2

3

include .cpp with #ifndef instead of .h considered bad practice?

Yes, including .cpp files is considered a bad practice.

int some_method()
{
    return 1;
}

Header files are quite often included into multiple translation units. If you include that file into multiple TU, then you have defined some_method in multiple TU. This violates the One Definition Rule and your program would be ill-formed.

Even if you pinky promise to include that file into exactly one other TU, .cpp files are conventionally compiled - possibly automatically depending on build system - in which case foo.cpp file will be one TU and the including file will be another TU and you end up with having multiple definitions.

  1. I do not have to write comments twice.

You do not need to write comments twice even if you do not include .cpp files.

  1. If I change the signature of a method then I will also have to change it on the header file.

This is a trivial change that can be automated.


#define _FOO

That identifier is reserved to the language implementation. You should use another macro as a header guard.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • But how is it included twice if I have the `#ifndef` directive? – Tono Nam Sep 02 '20 at 17:10
  • 1
    @TonoNam Because macros defined in one TU do not affect other TUs. – eerorika Sep 02 '20 at 17:11
  • I do not even know what TUs are. That is how newbie I am with c++ :/ . EDIT. I see its the translation unit. But I am just researching about that as I type this message. – Tono Nam Sep 02 '20 at 17:15
  • 1
    @TonoNam TU is short for translation unit. When you compile (compilation is also called translation) a file, that source file, combined with all of the included headers constitute a translation unit. Programs typically consist of multiple translation units that are linked together. – eerorika Sep 02 '20 at 17:17
  • Thanks for the help. Last question. Can I write the actual code on the header file without having the cpp file? – Tono Nam Sep 02 '20 at 17:25
  • Many companies I've worked for in the past had tools that parse the implementation files and automatically generate the header files. These are built into the build system so you-the-coder just write the implementation files and the build system takes care of the rest. – user4581301 Sep 02 '20 at 17:27
  • 2
    @TonoNam You can write inline functions in header files. But writing all your functions in header files will have compilation time problems in projects that aren't small. – eerorika Sep 02 '20 at 17:27
3

You should never #include a .cpp file, because the .cpp extension tells people that a file is a separately compiled translation unit. There is nothing technically wrong with it, but it is misleading to other programmers and will make your code confusing and difficult to maintain.

If you change the file extension of the file in your question to .h or .hpp then it's fine to include the entire definition of a function in a header file as long as you mark it inline.

That is, if you have a file named "foo.hpp" with the following contents then that's totally fine.

#ifndef FOO_HPP_
#define FOO_HPP_

// my comment that I only have to write once
inline int some_method()
{
    return 1;
}

#endif

This will slow down your compile times by a not-insignificant amount in a large project though. Compiling all (or most) of you code as one huge compilation unit will take less time overall than compiling 200 individual compilation units, but it will require re-compiling it all every time you make a change anywhere. That's the problem that separate compilation units exist to solve. If you make a change in one part of your code you want to avoid re-compiling unrelated code. In a large project this can save quite a bit of time. I've worked on projects that take over an hour to fully re-compile, and having to do that every time I make a change would be really painful.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52