Organising Code Files in C and C++

Started by
7 comments, last by PaulCunningham 20 years, 3 months ago
Hi - I'm in the processes of doing a long overdue C++ code clean up and need clarification on one of the points in this excellent article. Organising Code Files in C and C++ - by Ben 'Kylotan' Sizer In the section titled, Fixing Problem 1:
quote:The key here, is to explicitly #include any header files that you need for a given source file to compile. You should never rely on header files indirectly including extra headers for you, as that may change. The 'extra' #includes also serve as documentation, by demonstrating what other code this file is dependent on. So don't try and leave them out if you know you need that header included somehow.
. Does that also apply to derived classes and how they include parent headers? 1)

//in file derived.cpp

#include "parent.h"
#include "derived.h"
Instead of 2)
   
//in file derived.h

#include "parent.h"

//in file derived.cpp

#include "derived.h"
The article seems to be recommending approach 1. Cheers, Paul Cunningham Pumpkin Games EDIT: Hopefully made example clearer [edited by - PaulCunningham on January 22, 2004 8:59:48 AM]
Cheers,Paul CunninghamPumpkin Games
Advertisement
I''m not sure I understand the differnce between your two examples, it looks like you just added a comment.
Mr. Sizer is suggesting the first approach because it is self-documenting without relying on implications.
Does the approach differ if the engine was in a seperate library.

Let''s say I wanted to derive a game specific object from a generic library object that has a data member that is a class.

So...

*in file Generic.h*class Generic {    private:        SomeClass some_class;      }*in file generic.cpp*#include "Generic.h"#include "SomeClass.h"


How would I handle the game side of things. Would I include both headers in my new GameSpecific.CPP?

(Currently I have one big Engine.h which includes Generic.h and SomeClass.h and then a game specific include file called Game.h which includes Engine.h. Each game specific cpp file then includes Game.h - yuk!)
Cheers,Paul CunninghamPumpkin Games
quote:Original post by PaulCunningham
Does that also apply to derived classes and how they include parent headers?

Hmm. Good question. But my answer is no.

The reason for this is that parent.h is not required by derived.cpp. In an ideal world, everything in derived.cpp is an implementation of the interface you define in derived.h. Of course, in the real world, sometimes you end up calling methods of the base class and so on (although it''s often best to avoid that by using template methods... but that''s a whole new can of worms I''m not opening here).

Let me put this another way; the original advice was basically designed to say "don''t rely on a header file to do extra work for you beyond that which it''s supposed to do". This is because the first header file might change one day, and your code will break as a result of the second header file no longer being implicitly included.

For example, if graphics.h includes windows.h so that you can use a Windows-specific type, you might later change graphics.h so that you no longer have a Windows dependency. However, any other Windows-specific files that relied on graphics.h to include windows.h will now break, and you''ll have to go through them all and add #include "windows.h" in everywhere.

However, child.h will always include parent.h. That is part of what being a child is, and therefore by definition child.cpp can assume it will have access to anything that Parent defines by way of Child. Therefore child.cpp does not directly depend on parent.h as such; it depends on various properties of Parents that just happen to be defined in parent.h, but which is child.h''s problem.

Clear? Unclear? It''s a lot of words for what is instinctively a simple issue once you get to the bottom of it.

As for when it''s in a separate library and your example:

  • generic.h would have to include SomeClass.h given that type of implementation. generic.cpp therefore has no need to include it, but you might choose to do so to prevent things breaking if this implementation changes (ie. to a reference or pointer).

  • GameSpecific.CPP would never include SomeClass.h. Why would it, when the SomeClass is a private member that GameSpecific will never access? GameSpecific is working with a ''Generic'' - the details of what makes up a Generic are irrelevant; such is the benefit of encapsulation and abstraction.

  • By the same rule, Engine.h shouldn''t include SomeClass.h.



Let a class be responsible for its own contents. No other file should ever care about what comprises it. In your example, the only files that should include SomeClass.h are generic.h and generic.cpp. Everything else will only manipulate it via public methods of Generic, which are exposed in generic.h.

[ MSVC Fixes | STL Docs | SDL | Game AI | Sockets | C++ Faq Lite | Boost
Asking Questions | Organising code files | My stuff | Tiny XML | STLPort]
Wow - thanks for the reply - initially it was unclear but the more I read the more (some of) it sinks in. You have raised some more concerns though

quote:Original post by Kylotan
For example, if graphics.h includes windows.h so that you can use a Windows-specific type, you might later change graphics.h so that you no longer have a Windows dependency. However, any other Windows-specific files that relied on graphics.h to include windows.h will now break, and you''ll have to go through them all and add #include "windows.h" in everywhere.


This is something that I think will need explaining a little further.

So the the code breaks! The compiler has told you, so you fix it. In my small game project I don''t think this would be an issue. Is this a major problem in large, commercial applications and something that should be stamped out now?

Is this situation not better than having a CPP file including a header that it no longer needs? I guess the programmer should remove the include but I know I would forget Does it merely boil down to a larger binary versus code maintenance?

quote:Original post by Kylotan
However, child.h will always include parent.h. That is part of what being a child is, and therefore by definition child.cpp can assume it will have access to anything that Parent defines by way of Child. Therefore child.cpp does not directly depend on parent.h as such; it depends on various properties of Parents that just happen to be defined in parent.h, but which is child.h''s problem.


Phew - that''s what I was hoping you would say!

quote:Original post by Kylotan
generic.h would have to include SomeClass.h given that type of implementation. generic.cpp therefore has no need to include it, but you might choose to do so to prevent things breaking if this implementation changes (ie. to a reference or pointer).


So it''s quite acceptable and common for a required header to be included by both the (child) header and (child) implementation?

Can you recommend any reliable resources that I can look at that explain things further. I''m wary of just googling ''cause you can get all kinds of bad advice returned - I wouldn''t know the ''wheat from the chaff'' as they say. When I come here I know certain individuals who know their stuff so I know that their advice is sound.

PS - I recently purchased Accelerated C++ but haven''t had the time to look at it yet (blushes). I''m a VB developer by day and know enough C++ (dipped in and out over the years) to get done what I want. However, every now and again I get a little ''bitten'' and it''s time to do some more reading!

Thank again.





Cheers,
Paul Cunningham

Pumpkin Games
Cheers,Paul CunninghamPumpkin Games
quote:Original post by PaulCunningham
So the the code breaks! The compiler has told you, so you fix it. In my small game project I don''t think this would be an issue. Is this a major problem in large, commercial applications and something that should be stamped out now?

You''re right, it''s not a big issue. But there might be rare situations where it breaks in a more subtle way. Such as when headers say "if function ''x'' isn''t already defined, let''s define it". Or if they provide a new overload for a function. In this case, one of your functions might still exist but throw up an odd error. Or may continue to work, but yield a slightly different result.

All my examples usually just involve 3 files. But in a large project you might be looking at files included to a ''depth'' of 4 or 5 levels, which may make it more awkward to find what needs to be fixed.

Also, as flangazor said, code should help document itself as far as possible. An explicit #include is a signal to anyone else who may read that source file that it uses a certain set of functions or relies on certain types. The cost to you is about 5 seconds of typing, compared to 5 minutes of fixing when it breaks, or 50 minutes when it breaks for someone else using your code and who doesn''t understand the intricacies.

quote:Is this situation not better than having a CPP file including a header that it no longer needs? I guess the programmer should remove the include but I know I would forget Does it merely boil down to a larger binary versus code maintenance?

The only penalty you''ll be likely to pay here is marginally slower compilation times, as excess code will get stripped out of the final executable at link-time (at least for release builds using most compilers). As for the ''risk'' of not removing the include... it''s quite simple really... when you remove the dependency you search your files for any relevant includes.

quote:So it''s quite acceptable and common for a required header to be included by both the (child) header and (child) implementation?


Yes. But as I said, it differs depending on whether ''required header'' is a parent class (in which case the implementation can assume it''ll be present) or is just a class used in composition (in which case, you can''t assume it''ll be included in the header as there''s often no need for it to be). This is where your first and second examples differ; the first is an example of inheritance and the second is an example of composition, which work in very different ways in C++. It''s also an example of why composition should be preferred to inheritance because inheritance is fragile. But these are things that will probably only affect you in big projects.

Remember that these things often come down to rules of thumb. My article generally addresses common problems people encounter. If your system is compiling just fine, and you can understand it, then you don''t need to worry so much. An extra #include here or there won''t derail your project.

quote:Can you recommend any reliable resources that I can look at that explain things further.


Sadly not; I''m self-taught in these matters. I believe there''s some sort of ''Large Scale Design'' book by someone called Lakos but I don''t have the time to search for it at the moment. I would expect that it''s overkill for what you''re trying to do though.

[ MSVC Fixes | STL Docs | SDL | Game AI | Sockets | C++ Faq Lite | Boost
Asking Questions | Organising code files | My stuff | Tiny XML | STLPort]
I believe this is a matter of exact situation. It probably depends on how you are inheriting, exactly. I would probably recommend only including the derived class''s header, because what if it changes its parents somewhere in the future? I include the STL hash_map all the time, and it depends on many others, but i''m not about to include all of those because it could easily not depend on them in the future, adding wasted compiling time for those extra headers.
(http://www.ironfroggy.com/)(http://www.ironfroggy.com/pinch)
My rule is that a header should include whatever headers are needed in order to use what is declared in that header.

For example, if it is impossible to use "graphics.h" without including "windows.h", then "graphics.h" should include "windows.h".

But I would also say a file that includes "graphics.h" shouldn''t use anything that isn''t declared in "graphics.h", or is absolutely necessary for using "graphics.h". That''s because, as mentioned before, if you change "graphics.h" so that it doesn''t need "windows.h" anymore, some files that use "graphics.h" might break.

One possible, although slightly unpleasant, way to ensure that you don''t accidentally use features that aren''t actually declared in the headers you''ve included could be to #include them into a namespace and then using the names you need to make available:

namespace _win32{#include <windows.h>};using _win32::CreateWindow;using _win32::GetWindowRect; 


I realise that''s ugly, and may be more work than you want to put in. It also means that you must always access the members of "windows.h" via _win32::, since #including windows.h again will just be ignored.

It''s perfectly safe to declare the same namespace twice, so it''s okay if "graphics.h" and "network.h" both #include "windows.h" into the _win32 namespace.

Note that, under this scheme, files should never access the members of _win32 directly unless they''ve also declared the namespace. Otherwise, you have the same problem you had before: if a header stops providing _win32, the including file breaks.

CoV
CoV

This topic is closed to new replies.

Advertisement