0

I am aware of the technicality that arises when someone uses templates in C++ classes/structs but still wants to follow the accepted (quite established) approach of splitting declarations into header(.hpp) files from definitions into source(.cpp) files.

I have done a mini-research in different blogs/forums, searching for a safe and systematic way to adopt and split the declarations from the definitions.

From this, (see answer 1) I am almost convinced that it is not a trustworthy approach to write a template class library and distribute it with header and lib files to hide the implementation. So, only the safe way to go is to directly write the definitions and the declarations in the same file (.hpp).

However, in another blog here I saw a workaround approach, that suggests doing a kind of circular dependency between the source and header files. By "circular dependency" I mean to include at the bottom of the header file the source code #include "Input.cpp", and as usual, in the source code include the header file #include "Input.hpp" on the top.

This is the example header file:

#ifndef INPUT_H_
#define INPUT_H_

template <class T>
class Input
{
public: // Constructors:
    Input(T whatever);
};

#include "Input.cpp"
#endif

and this is the example source code:

#ifndef INPUT_CPP_
#define INPUT_CPP_
#include "Input.h"

template <class T> 
Input<T>::Input(T whatever) 
{
    /* some code here */
} 
#endif

According to the answers that I found, the only issue in the circular dependency approach is the double-definition issue, which was resolved by adding preprocessor definitions in both files!

Now, my question is: Is it indeed a good practice to adopt, or maybe because of its simplicity (we used a very simple test case) it fails to prove that it is actually a bad approach?

Can someone provide evidence to show if this circular-dependency approach has any limitations?

  • 1
    Usually, you don't use the `.cpp` ending for such files included in headers. Typically, cpp files are to be compiled (== the compiler invoked to compile them), but these included ones are not compiled directly. See https://stackoverflow.com/q/543507/420683 – dyp May 10 '21 at 10:11
  • Why would you even need to include the `Input.h` in `Input.cpp`? – dyp May 10 '21 at 10:12
  • Use a different file-ending, not `.cpp` for the template implementation. Also, you don't need to include the header file in it. – super May 10 '21 at 10:14
  • 3
    This looks like cargo cult cpp files. It is a cpp to match a pattern, not because it matches the reason for the pattern. cpp/h split is for a reason. This mimics the split, but for a different reason. Typically you'd do `.h` and `.inc`, or `private/name.h`, becuase the "cpp" not cpp files. – Yakk - Adam Nevraumont May 10 '21 at 10:16
  • 1
    Okay, so I think you all suggest avoiding naming `.cpp` the file where implementations exist, and rather use something else, like: `.ipp`, or `.inc`. – Andreas Hadjigeorgiou May 10 '21 at 10:24
  • @dyp Why not include `Input.hpp` to the source code? I mean, isn't it the necessary way to declare the class before define its members etc? *do I miss a detail here...?* – Andreas Hadjigeorgiou May 10 '21 at 10:26
  • In the end, you need to compile a file (an "actual" .cpp file), say program.cpp. This program.cpp must include either your Input.hpp or your Input.cpp. But if you remove the `#include "Input.h"` from Input.cpp, you can still include Input.h in program.cpp and it will compile -- because Input.h includes Input.cpp so all your template definitions are still there. – dyp May 10 '21 at 10:30
  • @dyp Yes, I see your point! However, in the case where you are compiling the file `Input.cpp` such `g++ -c Input.cpp` you will need this circular-including... right? – Andreas Hadjigeorgiou May 10 '21 at 10:34
  • 2
    There is no reason to compile Input.cpp directly hence we're all suggesting to change its file suffix. Proper .cpp files are meant to contain actual functions and variables which provide symbols with external linkage for other files to link against. Template definitions are only compiled on-demand, so if you compile Input.cpp, it won't produce any symbols. – dyp May 10 '21 at 10:36
  • Ahaaa, yes you are right, that's the tricky part! Thanks @dyp – Andreas Hadjigeorgiou May 10 '21 at 10:39
  • 2
    Instead of the guard macros of the `.cpp` file, I would check `#ifdef INPUT_H_` (and emit an error otherwise) as `Input.hpp` is the only header which is supposed to `#include "Input.cpp"`. (Btw. I agree with @Yakk-AdamNevraumont that I wouldn't choose `.cpp` for this file.) – Scheff's Cat May 10 '21 at 10:41
  • The second approach is very indirect way to write them into a single file. And the preprocessor output will be the same. you will just give hard time to the maintainer. – Shady Atef May 10 '21 at 11:02
  • Generally speaking, .cpp files should include **code** that gets compiled into an object file. Template functions **are not code**. They are **patterns** for producing code. They do not belong in .cpp files. – Pete Becker May 10 '21 at 12:47

0 Answers0