Include order not working (c++)

Started by
13 comments, last by ChaosEngine 5 years, 11 months ago

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")

Advertisement

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.)

 

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?

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.

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

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<B> mB;

};

b.h


#include "a.h"


#pragma once



class B{

	A mA;
};

 

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<MAP_SIZE && y>=0 && y<MAP_SIZE) return true; return false; };
};


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

    std::list<gameObject*>::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<gameObject *> objectList;
    std::list<army> armyList;
    std::list<unit> 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<gameObject *>::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();
};

 

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. 

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

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

This topic is closed to new replies.

Advertisement