Skip to main content
GameDev.net gamedev.net
🔒 Locked

Include order not working (c++)

Started by suliman Jun 5, 2018 at 8:47 AM 13 replies 2.7k views
Original Post
suliman
suliman

Hi!

I get this error. And im pretty sure it has to do with some include order or circular include.


Error    1    error C2079: 'cRegion::a' uses undefined class 'army'

This is from the world.h file that starts like this:


#pragma once

#include "unit.h"
#include "main.h"
#include "cWalker.h"
#include "pathMaster.h"

#include "player.h"


class army;
class armyUnit;
class unit;

class cRegion{
public:

	polygon2D shape;

	army a;

};

The class "army" is defined in unit.h which starts like this:


#pragma once

#include "main.h"
#include "world.h"
#include "cWalker.h"
#include "pathUnit.h"

So yes they both include each other. But how can I not do that? They both need to know how each other look since they use the members of each other (the classes "army" and "cRegion")

Kylotan
Kylotan

You will need to refactor your dependencies so that there are no circular includes. Note that it's In C++ it's not possible for Class A to contain an instance of Class B, if Class B contains an instance of Class A. When you think about it, this would mean each one was of infinite size, because an A contains a B which contains an A which contains another B and so on. So, if army contains a cRegion and cRegion contains an army, it can never work.

You might fix this by having one object refer to the other via a pointer, smart pointer, or reference - these allow you to forward declare the class (as you have with army, armyunit, and unit already) and create a pointer to them without needing to #include their header file. (You will still need to #include it in your non-header files in order to work with those objects, however.)


suliman
suliman

That is not the case, i misspoke (they do not include each other in that way)

I could rearrange some stuff and can now remove "include world.h" from unit.h

BUT
cRegion still complains that army is an undefined class even though the file (world.h) includes unit.h. How can I find where the error lies now?

Gnollrunner
Gnollrunner

Well .... world.h has to include unit.h because army is actually included in cRegion (not just a pointer to it). First question, did you mean to do this or was this meant to be a pointer to army? In any case I can't see the rest of the code but hopefully at most army (or some other class in unit.h) only points to cRegion. You can do this, but you have to remove the include to world.h and simply add a forward declaration of cRegion in unit.h (i.e. class cRegion;). By doing this you can still have a pointer to cRegion in unit.h if you want. However you can't actually use any of it's members there, you can only pass the pointer around. This is where the cpp file comes in. You want to put a any members in army that reference cRegion in unit.cpp, and you can put include "cRegion.h" at the top of it. The down side is the compiler typically can't inline those functions but it's probably not a big deal unless you are trying to squeeze every ounce of speed from your code. Unfortunately C++ programmers have to deal with this all the time.

BTW you don't need the forward declaration of army in world.h or any forwards that you have include files for. You typically only need to use them if you can't use an include, as I outlined above.

Bregma
Bregma

Dependencies need to be a directed acyclic graph (ie. a tree with no circles). This is true for classes and for header files.

Perhaps start by refactoring each class into its own header file, and then only include those header files you need where you need them.

If that fails to work because you have logical circular dependencies, try using members by reference (which confusingly usually means by pointer type as described above) instead of by value.

Stephen M. Webb
Professional Free Software Developer
h8CplusplusGuru
h8CplusplusGuru

shared_ptr can be used when the definition is not available in header space

an example solution:

a.h




#include <memory>

#pragma once

class B;


class A {

	std::shared_ptr mB;

};

b.h


#include "a.h"


#pragma once



class B{

	A mA;
};


suliman
suliman

Thank you all for helping out!
I still dont get it to work. In unit.h I have no reference to stuff from world.h so i dont understand how it can be circular. Below are the full files (they are somewhat long but most of it is very simple members so quick to scroll through).

world.h

Spoiler

#pragma once

#include "unit.h"
#include "main.h"
#include "cWalker.h"
#include "pathMaster.h"

#include "player.h"


class army;
class armyUnit;
class unit;

class cRegion{
public:

polygon2D shape;

army a;

};

class gameMap{
public:

bool startSave;

char name[20];
char tile[MAP_SIZE][MAP_SIZE];

int2 startPos[MAX_PLAYERS];
int2 size;

cRegion region[REGION_MAX];

bool endSave;

gameMap(){

}

bool withinMap(int x,int y){if(x>=0 && x=0 && y };


class gameObject{
public:
int type;
int subType,team;
float2 pos;
float life,rot;
float2 target;
bool ranged;

std::list::iterator curIt;

};

class world
{
public:

hgeSprite * lightSprite;
HTARGET lightTexture;
HTEXTURE mapTexture;

int attackSoundMass[30];
int hitSoundMass[30];
float2 pan;
int2 sector;
bool editorOn;

float noUnitDelay;
pathMaster pathSolver;

std::list objectList;
std::list armyList;
std::list unitList;

unit * selectedUnit;
army * theNewArmy;

gameMap map;
HTEXTURE minimapTexture;
hgeSprite * minimap;

char roadBit[MAP_SIZE][MAP_SIZE];
char tileVariation[MAP_SIZE][MAP_SIZE];
float moveCost[10][30];

// battle stuff
char battleTerrain[BATTLE_X][BATTLE_Y];
char battleTerrainCurrent[BATTLE_X][BATTLE_Y];
army * bArmy1;
army * bArmy2;
bool battleOn;

float aiWait;
bool battleTurnLeft;

//_________________________________________________________________________________
bool startSave;

float timeMatch,timeCurrentRound;
int nextArmyId,nextTownId;

int turnNumber;
float gameTime;
bool showUnitRange;
int infoClass,infoType;
char townBaseIncome[10];
int gameScreen;

//settings
bool explorationOn;
int difficulty,baseIncome;
int startingPoints;
int gameMode;

bool mapLoaded;

int popupArmyID;
int regionID;

bool endSave;
//_________________________________________________________________________________


char goodsName[10][20];
char incomeName[10][20];

char regionRatingName[20][20];
char unitClassName[10][20];


void loadTextData();
void loadFileData();

void controlPan();
void controlGame();
void setupWorld();
void drawWorld();
void drawPanel();
void doWorldTurn();

void selectArmyFromMap();
void createArmy(int team,int2 pos,int type,int level=0);
void deleteArmy(army * u);
void drawSystemInfo();

//battle functions
void setupBattle(army * a1, army * a2);
void doBattle();

void reset(){
regionID = 0;
battleOn = false;
popupArmyID=-1;

gameScreen=0;
mapLoaded=false;
infoClass=0;
infoType=0;
startingPoints=20;
difficulty=2;
baseIncome=0;
selectedUnit=0;
editorOn=false;
explorationOn=true;
pan.x=0;
pan.y=0;

sprintf(goodsName[0],"Funds");
sprintf(goodsName[1],"Rare earth");
sprintf(goodsName[2],"Tech");
sprintf(goodsName[3],"Power");
sprintf(goodsName[4],"NOT_USED_GOODS");

sprintf(goodsName[5],"NOT_USED_GOODS");
sprintf(goodsName[6],"NOT_USED_GOODS");
sprintf(goodsName[7],"NOT_USED_GOODS");
sprintf(goodsName[8],"NOT_USED_GOODS");
sprintf(goodsName[9],"NOT_USED_GOODS");

sprintf(incomeName[0],"Taxes");
sprintf(incomeName[1],"Trade");
sprintf(incomeName[2],"Mining");
sprintf(incomeName[3],"Agriculture");
sprintf(incomeName[4], "Drilling");
sprintf(incomeName[6], "Rare earth");
sprintf(incomeName[7], "Tech");

for (int i = 0; i < 20; i++)
regionRatingName[0] = 0;

sprintf(regionRatingName[1], "Growth");
sprintf(regionRatingName[2], "Mining");
sprintf(regionRatingName[3], "Agriculture");
sprintf(regionRatingName[4], "Drilling");
sprintf(regionRatingName[6], "Rare earth");

sprintf(regionRatingName[10], "Solar");
sprintf(regionRatingName[11], "Biomass");
sprintf(regionRatingName[12], "Hydro");
sprintf(regionRatingName[13], "Geothermal");


sprintf(unitClassName[0], "Soft");
sprintf(unitClassName[4], "Mixed");
sprintf(unitClassName[1], "Hard");
sprintf(unitClassName[2], "Rotor");
sprintf(unitClassName[3], "Plane");

};

world(){
reset();
};

~world(void);
void editMap();
bool loadMap(char * name);
void saveMap(char * name);
void setVisible(int2 pos,int range);
void explore(int2 pos,int range);
void updateMinimap(int2 pos,bool careAboutExplored=true);
void drawMinimap(int2 pos,bool simple,int size);
void effectMinimap( int type );
void resetMap(int clearType=0);
bool withinMap( int x,int y );
void findNextUnit(int type=0);
void setPan( float2 pos );
void createObject( int type,int team,float2 pos,int data,float rot=0,float2 target=float2(-1,-1),bool ranged=false);
std::list::iterator deleteObject(gameObject * o);
void updateObjects();
void drawCost( int x,int y,short cost[10],int time=0,char * name=0,int nameGap=0,int workers=0,int background=0);
void setMap( int x,int y,int type ,bool updateMini=true);
void drawInfo();
int getBuildingIcon(int bType);
void drawSingleCost( int x,int y,float cost,int costType,char * name=0,int nameGap=0,int decimals=0,DWORD color=white, float secondValue=-1000,char * helpTextOverride=0, int endingStyle=0, bool forcePlus=false);
int2 findFreeTile( int2 pos, int moveClass );
void calcControl( int teamID, bool draw );
void doArmyTurn(army * a);
void setRoadBit( int x,int y );
int10 getTerrainIncome( int x,int y,bool support=false, bool drawTiles=false );
bool buildPosOk( int x,int y,bool support );
void toggleInfo( int classIn, int typeIn );
void setMapTiles(int2 singleTile=int2(-1,-1));
void toggleGamescreen( int type);
void drawGamescreen();
float getSoundVol(int2 absPos,int team=0);
void gainGoods(int team, int2 absPos,int type, int mass, int playSound=-1);
int2 getBattlePos(float x, float y, bool leftSide);
int getSeason();
void newGame();
army * getArmyFromId(int id);
void doGame();
int drawProgressBoxes(int x, int y, char * name, char * helpText, int value, int maxValue, int nameGap=90);
void drawArmyUnit(int x, int y, armyUnit * u, int uType = 0);
bool drawBuilding(int2 pos, int bType = 0, int damage = 0, char * helpText=0);
void analyzeMap();
void setPathSolver(bool battle);
void drawBattle();
bool withinMapCombat(int x, int y);
bool enemyAdjacent(int2 pos, int team);
unit * getUnit(int2 pos);
};

unit.h

Spoiler

#pragma once

#include "main.h"
#include "player.h"
#include "cWalker.h"
#include "pathUnit.h"

class armyUnit{
public:
int type, xp, level;
float hp;

armyUnit(){
hp = 1;
type=0;
xp=0;
level=0;
};
void draw( int x,int y,float scale,bool flipped =0, int bg =0, bool highLight=0);
};


class unit : public pathUnit, public armyUnit{
public:

float effectHit, effectAttack;
bool leftTeam;
int team;

float2 projectilePos;
float2 projectileGoal;
float projectileRot;
bool isAttacking;

unit(){
isAttacking = false;
effectAttack = 0;
effectHit = 0;
};

void newTurn();
void draw(int x, int y, float scale, bool flipped =0, int bg =0, bool highLight=0);
void doAI();
int2 findClosestFreeTile(int2 p);
void endTurn();
void attack(unit * victim);
void doAttack();
void hitUnit(unit * param1);
};

class cBuilding{
public:
short type, damage;

cBuilding(){
type=0;
damage=0;
}
};


class army : public pathUnit{
public:

army * target;
army * defendingThis;

bool startSave; //--------------

int type, xp, level;
int stance;

float winDelay;
int winState;

int2 rallyPoint;
float dir;
int radius;

int2 offset;
int team, commandedByLord, id;
float morale, hp, flanking;

armyUnit un[MAX_UNITS];

float attackDelay,dmgPoints;
int lastDamagedBy;
int battlefield;

float strength[5];
short count[5];

int2 lastExploredPos;

int inBattleTime;

int inBattle; //id of ongoing battle (if engaged in any)
int2 placePos; //to place next unit in battle
int samePlacePosTime;

float effectAttack,effectHit;

float armyUpkeep[2];

//town (map settings
char name[30];
int regionRating[20]; // regional resources, power ratings etc

//town
float goods[10];
int goodsIncome[10];
int incomeFromField[10]; // mining, refineries etc
int buildingIncome[4]; //direct income
float incomeModifier[10];

gameTrainingCue townCue[4]; // 0 construction 1 recruit 2 factories 3 dockyards
bool coastal;

float recruitsNow[50];
int recruitsMax[50];

int prodGenerated[4]; // how much is generated from factories and shipyards
int prodPoints[4]; //for the cues: how much is done already

//variable state
int busyPop;
float trade,wealth;
float order,growth,corruption,pop;
int maxWorkerPerField,maxBuilders,maxCarts;
float trainingSpeed,musterSpeed;
float upgradeProgress;
float defense;
int recoverMaxMen,defenseMaxMen;
float growthBuildings, corruptionBuildings, orderBuildings;
int limitedBuildings;

//settings
int taxSetting,ruleSetting;

//buildings
cBuilding b[BUILDING_MAX];

int takenBy;
float takenDelay;

bool endSave; //--------------

pathUnit savedData;

army(){
sprintf(name, "London");
movesLeft=1;
armyUpkeep[0]=0;
armyUpkeep[1]=0;

for (int i = 0; i < 4; i++){
townCue.maxItems = 5;
prodPoints = 0;
prodGenerated = 1; // use fixed speed for all now
}
//prodGenerated[0] = 1; //construction

for (int i = 0; i < 4; i++)
buildingIncome = 0;

limitedBuildings = 0;
takenBy=-1;
takenDelay=0;
defendingThis=0;
recoverMaxMen=0;
defense=0;
defenseMaxMen=0;
orderBuildings = 0;
growthBuildings = 0;
corruptionBuildings = 0;

inBattle=-1;
dmgPoints=0;
battlefield=2; //open terrain

for(int i=0;i<10;i++){
goods=0;
goodsIncome=0;
}


for(int i=0;i<50;i++){
recruitsNow=0;
recruitsMax=0;
}

for(int i=0;i<10;i++)
incomeModifier=1.0;

pop=430;
growth=2.5;
order=85;
taxSetting=2;
ruleSetting=0;

samePlacePosTime=0;
lastDamagedBy=-1;
target=0;
upgradeProgress=-1;

level=-1;

busyPop=0;
trade=130;
wealth=50;

order=100;
growth=0.0;
corruption=0;

for(int i=0;i<10;i++){
incomeFromField=0;
}

effectAttack=0;
effectHit=0;

attackDelay=0;
retryPathCount=0;
retryPathDelay=0;

stance=0;
radius=40;
id=-1;
commandedByLord=-1;
hp=1;
morale=1;
xp=0;
}

void addUnit( int type,float mass=1 );
int getValue();
void doTownTurn( int draw=-1 );
void drawAdd( int x, int y, int addType );
bool armyAvailable( int type );
bool buildingAvailable( int bType );
void buildingEffect( int bType, bool added );
int2 findWalkerGoal( int walkerType );
bool hasBuilding(int bType);
void draw(int x, int y, float scale, bool bg=true);
void placeUnit(int unitID, int lane=-1, int intelligence=0);
void nextPlacePos();
float getTownData(int dataType, bool draw=false);
void gtd(char * title,float mass=0, int decimals=0, bool multiply=false);
float getPopFactor(int fType);
void spawnUnit(short uType);
void addBuilding(int bType,bool forceAllow, int removePos=-1);
void gtd_(int c);
void setTownDetails(bool updatePlayer=false);
void takeTown(int style);
void damageBuildings(int mass);
void doExplore();
void setArmyDetails();
void removeFromCue(int cueId, int cueIndex);
void addToCue(int addType, bool isBuilding);
void setupTown();
float getRecruitsAdded(int recruitsMax);
bool unitAvailable(int i);
float getDistanceFactor(int type);
float getCityCountFactor(int type);
int getGarrisonOrder();
};

Gnollrunner
Gnollrunner
2 minutes ago, suliman said:

Thank you all for helping out!
I still dont get it to work. In unit.h I have no reference to stuff from world.h so i dont understand how it can be circular.

Reveal hidden contents


Reveal hidden contents



Basically, no matter what, you shouldn't have two files include each other.

suliman
suliman

But as far as I understand I dont. Did you look at the files?

Gnollrunner
Gnollrunner

Unless I'm missing something world.h includes unit.h and visa versa. This is what you showed I your first post.

Kylotan
Kylotan

It's not just about those 2 files, it's about any files they include. If A.h includes B.h, B.h includes C.h, and C.h includes A.h, that's circular, even though no 2 files directly include each other.

Probably the best way to fix this is to do what Bregma said, refactor your headers so that each one has only 1 class in it, and #includes as few other headers as possible. This makes it easier to only include what you need, and easier to go through them and look for cycles. Those cycles can be fixed with forward declarations and pointers or other ways of referencing that don't require the circular dependency.


Alberth
Alberth

Another way to find the problem is to draw a dependency tree. Each include file is a square on paper. From each square, draw an arrow to other squares it directly includes (ie there is an #include line in the file at the back of the arrow).

You should not be able to follow the arrows and end up in a square where you have already been (ie cycles are not allowed)


suliman
suliman

Yeah there must be something like that going on. I started picking apart this very old project but it become to big of an effort so i just declared them in global scope. Not so pretty but it works well.

I will certainly avoid this in new projects I start:)

Thanks!

ChaosEngine
ChaosEngine
On 6/6/2018 at 2:28 AM, suliman said:

I started picking apart this very old project but it become to big of an effort so i just declared them in global scope. Not so pretty but it works well.

I wouldn't do that unless you absolutely have to (deadlines, etc).

This is a really good opportunity to pick this apart and learn how to restructure your code. It's a very useful skill to have because you will almost certainly encounter code like this again (and probably written by someone else).

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.