Organizing c++ files of big projects

Started by
5 comments, last by Choff 19 years, 7 months ago
I have been coding for long time, but have rarely done big(multi-files) c++ projects. As im a self-learned I was insane at how header files and compiling and linking process works. When ever I extend my c++ program on many files I get many linking errors. But after reading an article on gamedev.net about organizing c++ files I got better understanding how compiling/linking process works. But still I want to know a standard way to split my objects and code into many files. In case of my programs an object/class needs to access many other classes. Forexample if i have these classes LineShape, CricleShape, RectShape, RenderClass,..,etc. Each with its own pair ".cpp" & ".h" files. And (almost )every one of them needs to have access each other class. So I simply include all header files of each class into on global headerfile. --->globalheader.h #include "line.h" #include "circle.h" #include "rect.h" #include "......." And simple include globalheader.h into all cpp files. like --->Line.cpp #include "globalheader.h" But this kind of setup leads me to many un-understandable compiling & linking errors. So is there a good standard way/ silver bullet for good setup? After looking at project files of big projects, like Ogre. I see that they have a standard header file and a prerequisites header file containing class declerations. But I still cant understand this setup. Any Help?
3D Side-Scroller game demo Project-X2 "playable"Lashkar: A 3D Game & Simulation Project demo @ lashkar.berlios.de
Advertisement
Read this.
I already read that. But it mentions problems and their solutions. But I dont know how to try these techniques because the project is very big.
3D Side-Scroller game demo Project-X2 "playable"Lashkar: A 3D Game & Simulation Project demo @ lashkar.berlios.de
There is no "standard" solution. The expectation is that you will understand the underlying process of preprocessing, compilation and linking and structure your code accordingly.

In any case, your problem (interrelationships between multiple types) has nothing to do with file layout, intrinsically. All you need to do is lookup forward references.

Oh, and maybe see if you can eliminate some of the interrelationships in your case by declaring an abstract base type Shape and making Line, Circle and Rect, etc, derive from that. Then have RenderClass interact with the Shape interface, and let polymorphism do the rest.
Just keep this in mind:

When you type "#include <file.h>", what that is basically saying is "insert file.h in this spot".

There's nothing mystic about it. When you include a header file at the top of your file, it pretty much just dumps that file in that spot. To give an example, say you have a program that needs to create a Shape object, a class that you've defined. That Shape class however relies on another class you've made. A Color class. It might be structured as follows:

main.cpp
#include "color.h"#include "shape.h"int main(){	Shape shape;	return 0;}


color.h
class Color{	float r,g,b;};


shape.h
class Shape{	Color color;};



That's what you see when you're making it. You have these three seperate files. That's not what the compiler sees though. It sees this:

class Color{	float r,g,b;};class Shape{	Color color;};int main(){	Shape shape;	return 0;}


Those #include lines are replaced by the files they referance, and one single file is produced. In this file, it's pretty clear what happens. The Color class is defined. After that, the Shape class is defined, and then the main function is defined, in which a shape object is created. Everything makes sence.


This however is not the ideal way to do it. In this system, you're responsible for including each header file that each other class needs, and you have to do it in the right order. This quickly gets very messy. Instead of you including the color.h file yourself, the shape.h file should include it itself, so your program would now look like this:

main.cpp
#include "shape.h"int main(){	Shape shape;	return 0;}


color.h
class Color{	float r,g,b;};


shape.h
#include "color.h"class Shape{	Color color;};



Note that when this is all shoved together into one file, it produces the exact same file we got with the first structure. Replace all the #include lines with the files they referance, and the result is the same.

This structure creates a problem though. What if I make another class that uses the Color class? Let's add a new class to the mix. Let's make it a Point class. Using the above system, it would be structured as follows:

main.cpp
#include "shape.h"#include "point.h"int main(){	Shape shape;	Point point;	return 0;}


color.h
class Color{	float r,g,b;};


shape.h
#include "color.h"class Shape{	Color color;};


point.h
#include "color.h"class Point{	Color color;};



But this doesn't work. When the files are combined into a single file, we get this:

class Color{	float r,g,b;};class Shape{	Color color;};class Color{	float r,g,b;};class Point{	Color color;};int main(){	Shape shape;	return 0;}



The Color class has just been added to the main file twice. It has been defined twice. This is illegal, and you'll get a compiler error about a type redefinition. To fix this problem, we use include guards. The color.h file with an include guard added looks something like this:

color.h
#ifndef __COLOR_H__#define __COLOR_H__class Color{	float r,g,b;};#endif


This is basically just an if statement. If the constant __COLOR_H__ is not defined, we define it, and go on to define the color class. If it has already been defined, everything between the #ifndef and the #endif is not included when the files get combined. This means that the Color class will not be duplicated like it was before, and this system now works. This ended up a lot longer than I was planning, but I hope it clarified things for you.
Quote:Original post by DirectXXX
After looking at project files of big projects, like Ogre. I see that they have a standard header file and a prerequisites header file containing class declerations.

But I still cant understand this setup. Any Help?


Something to understand is that an engine or library like Ogre is being used by two different sets of people. One set is the people developing it and another set is people using it.

The people developing it want to be able to make a change with a minimal amount of rebuilding as a result. They don't like headers which include lots of other headers.

The people using the library don't care. When they update they just get a new binary, or just rebuild the source once. So they're happy using a header which includes a whole load of things because it's convenient and will only require them to rebuild when they get a new version of the engine, which won't be that often.

So, for developers and the internals of the engine, try and make as few dependencies as possible. For the public api you can supply headers which include whole subsystems or even the whole engine API e.g. #include "MyEngine.h"
One class per h/cpp file will pay dividends as your project grows. Common structs and typdefs can be put into header.

Also try to have each header and cpp file include no more and no less than what it excatly needs to access. Which means don't rely on includes via other includes.

This methodology is non-trivial to organize, but the trade offs are far more positive than negative. You will see the rewords as you add more and more code to the project...

One other side note. If your project is getting bigger you may want to start grouping closely related classes into libraries.
==========================big kid with adult powers==========================

This topic is closed to new replies.

Advertisement