views:

2432

answers:

16

You can structure a C++ program so that (almost) all the code resides in Header files. It essentially looks like a C# or Java program. However, you do need at least one .cpp file to pull in all the header files when compiling. Now I know some people would absolutely detest this idea. But I haven't found any convincing downsides of doing this. I can list some advantages:

[1] Faster compile times. All header files only get parsed once, because there is only one .cpp file. Also, one header file cannot be included more than once, otherwise you will get a build break. There are other ways of achieving faster compiles when using the alternate approach, but this is so simple.

[2] It avoids circular dependencies, by making them absolutely clear. If ClassA in ClassA.h has a circular dependency on ClassB in ClassB.h, I have to put a forward reference & it sticks out. (Note that this is unlike C# & Java where the compiler automatically resolves circular dependencies. This encourages bad coding practices IMO). Again, you can avoid circular dependencies if your code was in .cpp files, but in a real-world project, cpp files tend to include random headers until you can't figure out who depends on whom.

Your thoughts?

+8  A: 

The obvious downside to me is that you always have to build all the code at once. With .cpp files, you can have separate compilation, so you're only rebuilding bits that have really changed.

Chris Jester-Young
Ah good catch. So there won't be any "incremental" builds, and in that sense, compile times will be slower. In practice though, I find building pretty fast, since my code is broken up into separate static libs.
That wont change anything. If you have all of your code in your .h file and then you make a modification - every .cpp and .h (technically: .cpp) that includes it has to be rebuilt.
nlaq
@Nelson: Exactly why I disagree with having all code in .h files.
Chris Jester-Young
Maybe he likes Pascal, and wants to emulate that...
Jonathan Leffler
+19  A: 

I disagree with point 1.

Yes, there is only one .cpp and the built time from scratch is faster. But, you rarely build from scratch. You make small changes, and it would need to recompile the whole project each time.

I prefer doing it the other way around:

  • keep shared declarations in .h files
  • keep definition for classes that are only used in one place in .cpp files

So, some of my .cpp files start looking like Java or C# code ;)

But, 'keeping stuff in .h' approach is good while designing the system, because of point 2. you made. I usually do that while I'm building the class hierarchy and later when code architecture becomes stable, I move code to .cpp files.

Milan Babuškov
Disagreement isn't really strong enough. This isn't open to debate: point 1 is flat out wrong for the reasons you mentioned.
Konrad Rudolph
@Konrad: one can argue that having a compiler that has support for pre-compiled headers (MSVC and GCC) doesn't really rebuild all the header files - only the inter-dependent ones - just like .cpp file approach. However, setting that up would meant having a separate PCH file for each .h file.
Milan Babuškov
A: 
pngaz
+9  A: 

You misunderstand how the language was intended to be used. .cpp files are really (or should be with the exception of inline and template code) the only modules of executable code you have in your system. .cpp files are compiled into object files that are then linked together. .h files exist solely for forward declaration of the code implemented in .cpp files.

This results in quicker compile time and smaller executable. It also looks considerably cleaner because you can get a quick overview of your class by looking at its .h declaration.

As for inline and template code - because both of these are used to generate code by the compiler and not the linker - they must always be available to the compiler per .cpp file. Therefore the only solution is to include it in your .h file.

However, I have developed a solution where I have my class declaration in a .h file, all template and inline code in a .inl file and all implementation of non template/inline code in my .cpp file. The .inl file is #included at the bottom of my .h file. This keeps things clean and consistent.

nlaq
The .inl (or .tcc, in GNU libstdc++) is a Best Practice, in my opinion. +1!
Chris Jester-Young
If people only did things the way the language was intended to be used, there'd be no template meta-programming. Oh, hang on, I'm not sure that'd be a bad thing ;-)
Steve Jessop
+13  A: 

You are right to say that your solution works. It may even have no cons for your current project and developing environment.

But...

As others stated, putting all your code in header files forces a full compilation every time you change one line of code. This may not be an issue yet but your project may grow large enough to the point compilation time will be an issue.

Another problem is when sharing code. While you may not be directly concerned yet, it is important to keep as much code as possible hidden from a potential user of your code. By putting your code into the header file, any programmer using your code must look the whole code, while there are just interested in how to use it. Putting your code in the cpp file allows to only deliver a binary component (a static or dynamic library) and its interface as header files, which may be simpler in some environment.

This is a problem if you want to be able to turn your current code into a dynamic library. Because you don't have a proper interface declaration decoupled from the actual code, you won't be able to deliver a compiled dynamic library and its usage interface as readable header files.

You may not have these issues yet, that's why I was telling that your solution may be ok in your current environment. But it is always better to be prepared to any change and some of these issues should be addressed.

PS: About C# or Java, you should keep in mind that these languages are not doing what you say. They are actually compiling files independently (like cpp files) and stores the interface globally for each file. These interfaces (and any other linked interfaces) are then used to link the whole project, that's why they are able to handle circular references. Because C++ does only one compilation pass per file, it is not able to globally store interfaces. That's why you are required to write them explicitely in header files.

Vincent Robert
+3  A: 

You might want to check out Lazy C++. It allows you to place everything in a single file and then it runs prior to compilation and splits the code into .h and .cpp files. This might offer you the best of both worlds.

Slow compile times are usually due to excessive coupling within a system written in C++. Maybe you need to split code into subsystems with external interfaces. These modules could be compiled in separate projects. This way you can minimize the dependency between different modules of the system.

cdv
+2  A: 

I like to think about separation of .h and .cpp files in terms of interfaces and implementations. The .h files contain the interface descriptions to one more classes and the .cpp files contain the implementations. Sometimes there are practical issues or clarity that prevent a completely clean separation, but that's where I start. For example, small accessor functions I typically code inline in the class declaration for clarity. Larger functions are coded in the .cpp file

In any case, don't let compilation time dictate how you will structure your program. Better to have a program that's readable and maintainable over one that compiles in one 1.5 mintues instead of 2 minutes.

mxg
+12  A: 

Reason [1] Faster compile times

Not in my projects: source files (CPP) only include the headers (HPP) they need. So when I need to recompile only one CPP because of a tiny change, I have ten times the same number of files that are not recompiled.

Perhaps you should break down your project in more logical sources/headers: A modification in class A's implementation should NOT need the recompilation of implementations of class B, C, D, E, etc..

Reason[2] It avoids circular dependencies

Circular dependencies in code?

Sorry, but I have yet to have this kind of problem being a real problem: Let's say A depends on B, and B depends on A:

struct A
{
   B * b ;
   void doSomethingWithB() ;
} ;

struct B
{
   A * a ;
   void doSomethingWithA() ;
} ;

void A::doSomethingWithB() { /* etc. */ }
void B::doSomethingWithA() { /* etc. */ }

A good way to resolve the problem would be to break down this source into at least one source/header per class (in a way similar to the Java way, but with one source and one header per class):

// A.hpp

struct B ;

struct A
{
   B * b ;
   void doSomethingWithB() ;
} ;

.

// B.hpp

struct A ;

struct B
{
   A * a ;
   void doSomethingWithA() ;
} ;

.

// A.cpp
#include "A.hpp"
#include "B.hpp"

void A::doSomethingWithB() { /* etc. */ }

.

// B.cpp
#include "B.hpp"
#include "A.hpp"

void B::doSomethingWithA() { /* etc. */ }

Thus, no dependency problem, and still fast compile times.

Did I miss something?

When working on "real-world" projects

in a real-world project, cpp files tend to include random headers until you can't figure out who depends on whom

Of course. But then if you have time to reorganize those files to build your "one CPP" solution, then you have time to clean those headers. My rules for headers are:

  • break down header to make them as modular as possible
  • Never include headers you don't need
  • If you need a symbol, forward-declare it
  • only if the above failed, include the header

Anyway, all headers must be self-sufficient, which means:

  • An header include all needed headers (and only needed headers - see above)
  • an empty CPP file including one header must compile without needing to include anything else

This will remove ordering problems and circular dependencies.

Is compile times an issue? Then...

Should compile time be really an issue, I would consider either:

Conclusion

What you are doing is not putting everything in headers.

You are basically including all your files into one and only one final source.

Perhaps you are winning in terms of full-project compilation.

But when compiling for one small change, you'll always lose.

When coding, I know I compile often small changes (if only to have the compiler validate my code), and then one final time, do a full project change.

I would lose a lot of time if my project was organized your way.

paercebal
+3  A: 

One thing you're giving up that I would have a hard time living without is anonymous-namespaces.

I find that they're incredibly valuable for defining class-specific utility functions that should invisible outside the class's implementation file. They're also great for holding any global data that should be invisible to the rest of the system,like a singleton instance.

+2  A: 

You're going outside of the design scope of the language. While you may have some benefits, it's going to eventually bite you in the butt.

C++ is designed for h files having declarations, and cpp files having implementations. Compilers are built around this design.

Yes, people debate whether that's a good architecture, but it's the design. It's better to spend your time on your problem than reinventing new ways to design C++ file architecture.

Paul Nathan
A: 

Well, as many have pointed out there is a lot of cons to this idea, but to balance out a bit and provide a pro, I would say that having some library code entirely in headers makes sense, since it will make it independent of other settings in the project it is used in.

For example, if one is trying to make use of different Open Source libraries they can be set to use different approaches for linking to your program - some may use the operating system's dynamically loaded library code, others is set to be statically linked; some may be set to use multithreading, while others is not. And it may very well be an overwhelming task for a programmer - especially with a time constraint - to try to sort these incompatible approches out.

All of that is however not an issue when using libraries that are contained entirely in headers. "It just works" for a reasonable well-written library.

Stefan Rådström
+2  A: 

I believe that unless you are using MSVC's pre-compiled headers, and you are using a Makefile or other dependency based build system, having separate source files should compile faster when building iteratively. Since, my development is almost always iterative, I care more about how fast it can recompile the changes I made in file x.cpp than in the twenty other source files that I didn't change. Additionally, I make changes much more frequently to the source files than I do to the APIs so they change less frequently.

Regarding, circular dependencies. I would take paercebal's advice one step farther. He had two classes that had pointers to each other. Instead, I run into the case more frequently where one class requires another class. When this happens, I include the header file for the dependency in the header file of the other class. An example:

// foo.hpp
#ifndef __FOO_HPP__
#define __FOO_HPP__

struct foo
{
   int data ;
} ;

#endif // __FOO_HPP__

.

// bar.hpp
#ifndef __BAR_HPP__
#define __BAR_HPP__

#include "foo.hpp"

struct bar
{
   foo f ;
   void doSomethingWithFoo() ;
} ;
#endif // __BAR_HPP__

.

// bar.cpp
#include "bar.hpp"

void bar::doSomethingWithFoo()
{
  // Initialize f
  f.data = 0;
  // etc.
}

The reason that I include this, which is slightly unrelated to circular dependencies, is that I feel there are alternatives to including header files willy-nilly. In this example the struct bar source file does not include the struct foo header file. This is done in the header file. This has an advantage in that a developer using bar does not have to know about any other files that the developer would need to include to use that header file.

terson
+1  A: 

One problem with code in headers is that it must be inlined, otherwise you will have multiple-definition problems when linking multiple translation units that include that same header.

The original question specified that there was only ever a single cpp in the project, but that is not the case if you're creating a component destined for a reusable library.

Therefore, in the interests of creating the most reusable and maintainable code as possible, only put inlined and inlinable code in header files.

pk
+1  A: 

One disadvantage of your approach is you can't do parallel compilation. You might think you're getting a faster compile now, but if you have multiple .cpp files you can build them in parallel on either multiple core on your own machine or using a distributed build system like distcc or Incredibuild.

Don Neufeld
A: 

One thing nobody has brought up is that compiling large files requires a lot of memory. Compiling your whole project at once would require such a huge memory space that it just isn't feasible even if you could put all code in headers.

Greg Rogers
A: 

If you are using template classes, you have to put the whole implementation in the header anyways...

Compiling the whole project in one go (through a single base .cpp file) should allow something like "Whole Program Optimisation" or "Cross-Module Optimisation", which is only available in a few advanced compilers. This is not really possible with a standard compiler if you are precompiling all your .cpp files into object files, then linking.