forward declaration/include problem

Hi!

I'm having some problems using my global elements i've created. Simply because they contain a class I created.

Read some about headers and projects, but so far I've been unsuccessful to solve it.

So the Class I use is called Block
and resides in MapGenPart.hpp (with it's more advance member functions declared in MapGenPart.cpp)

in MapGenGen.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//MapGenGen.hpp
#ifndef MAPGENGEN_H
#define MAPGENGEN_H
#include <vector>
#include <MapGenPart.hpp>
#include <string>
#include <cstdlib>
#include <iostream>
using namespace std;

class Block;

const int X_COUNT = 100;
const int Y_COUNT = 50;
const double WATER = 0.7;
Block MAP[X_COUNT][Y_COUNT];
vector<Block*> activeSeeds;


//further down

//goes until it finds an empty spot
void randomPlace(){
    Block* p = MAP[rand() % X_COUNT, rand() % Y_COUNT];
    if(p->setTerrain("land", 1)){
        randomPlace();
    }
    else{
        activeSeeds.push_back(p);
        return;
    }
}


and the compiler complains both about the Block MAP[X_COUNT][Y_COUNT]: elements of array have incomplete type, storage size of MAP isn't known.

and then about all the functions that use Block in any way.

for example randomPlace

invalid use of incomplete type 'class Block'.

So. Can some1 give me a run through of linkers and what to include and what to not and so on...

Forward declaring a class let's you use the name of that class in a "generic" way. IE: you can use it:

1) As a return type to a function
2) You can use it as a function parameter
3) You can create a pointer or reference to it


However you cannot use a forward declared class as a full object, because in order to do that the compiler needs to have the class fully defined. If you are doing any of the below, you will need to #include the class's header:

1) Creating an object/instance of the class (such as your 'MAP' array)
2) Implementing a function which returns an object, or takes an object as a parameter
3) Dereferencing a pointer or reference
4) Doing a sizeof() or anything else that requires the size/contents of the class to be known.



Can some1 give me a run through of linkers and what to include and what to not and so on...


This outlines it pretty well:

http://cplusplus.com/forum/articles/10627/
But that class's header is included:
#include <MapGenPart.hpp>

// sorry didn't have time to read the article atm. Have to go, but will read it all when I get back.
Last edited on
whoops, I missed that.



Can you post MapGenPart.hpp? The only thing I can think of that's going wrong is a header guard mistake (IE: accidentally using the same header guard ID for both of these headers).
I've read through some of that article before just couldn't find it again, thanks!

Entire MapGenPart.hpp: Take not that some function may be in prototype mode and non functioning.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//MapGenPart.hpp
#ifndef MAPGENPART_H
#define MAPGENPART_H
#include <string>
#include <cstdlib>
#include <vector>
#include <map>
#include <MapGenGen.hpp>
using namespace std;

class Block{
    private:
        map<string, double> m_Terrain;
        double m_Fertility, m_Moisture;
        const pair<int,int> M_ID;
        vector<Block*> m_Neighbours;
    public:
        //if sorting is needed
        //bool operator< (const Block& that)const {return m_ID < that.m_ID;}

        double getTerrain(string type){double temp = m_Terrain[type]; return temp;}
        //returns false if it wasn't there from the beginning
        bool setTerrain(string, double);

        double getFertility(){double temp = m_Fertility; return temp;}
        double getMoisture(){double temp = m_Moisture; return temp;}
        void setFertility(double fertile){m_Fertility = fertile; return;}
        void setMoisture(double moist){m_Moisture = moist; return;}

        vector<Block*>& getNeighbours(){vector<Block*>& temp = m_Neighbours; return temp;}
        void addNeighbour(int x, int y){Block * p = MAP[x,y]; m_Neighbours.push_back(p); return;}
        void setAllNeighbours();

        void setID(int x , int y){m_ID.first = x;m_ID.second = y; return;}
        pair<int,int> getID(){pair<int,int> temp = m_ID; return temp;}

};


//vector<vector<Corner>> River vector ordered by elevation set?
class Corner{
    private:
        double m_Elevation;
        vector<Corner*> m_Neighbours;

    public:
        //< orders by elevation
        bool operator< (const Corner& that)const {return m_Elevation <= that.m_Elevation;}

        double getElevation(){double temp = m_Elevation; return temp;};
        //returns the previous value
        double setElevation(double newElev){double temp = m_Elevation; m_Elevation = newElev; return temp;};

        vector<Corner*>& getNeighbours(vector<Corner>& temp = m_Neighbours; return temp;);

        void insertNeighbour(Corner* temp){m_Neighbours.push_back(temp); return;};

};

#endif 



extra note: if I remove the forward declared Block then everything that uses Block and it's functions start complaining. Not sure what's going wrong here.
Last edited on
Okay... this is a circular dependency.

mapgenpart.h is including mapgengen.h
and mapgengen.h is including mapgenpart.h

This doesn't work. See subsection 6 in that article I linked... it explains why this fails.

Suggested solution:

Step 1) mapgenpart.h should not include mapgengen.h

Step 2) any function in mapgenpart.h that uses something defined in mapgengen.h should be moved to a cpp file and should not be put in a header file.

Step 3) Any cpp file can #include both headers. You just can't have the headers include each other.


You might have additional problems due to the use of your global MAP array (globals are yucky... break out of that habit)... but that's another topic.
I would love to lose the globals but have nothing to substitute them with. Any help?

I'm off to check if it works. Will edit when done, noticed it was a circular dependency almost at the same time i got the response due to my slow reading speeds :P.
Ok, most of it is fixed, except when I thought I fixed the last problem I got 19 errors all saying "multiple definition of 'bla bla' "

brb, gonna google some.

Hmm... not finding any good answers when it's functions that is the problem


Compiler log:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z6setIDSv':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|18|multiple definition of `setIDS()'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|18|first defined here|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z11randomPlacev':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|30|multiple definition of `randomPlace()'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|30|first defined here|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z5seedsij':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|42|multiple definition of `seeds(int, unsigned int)'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|42|first defined here|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z10randomTypev':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|70|multiple definition of `randomType()'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|70|first defined here|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z11displayTypeii':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|93|multiple definition of `displayType(int, int)'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|93|first defined here|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z15printCurrentMAPv':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|109|multiple definition of `printCurrentMAP()'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|109|first defined here|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z11mapxModulosii':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|118|multiple definition of `mapxModulos(int, int)'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|118|first defined here|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z11mapyModulosii':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|125|multiple definition of `mapyModulos(int, int)'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|125|first defined here|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGenPart.o||In function `Z6setIDSv':|
C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|18|multiple definition of `MAP'|
obj\Debug\Desktop\MMOSTS\trunk\MapGen\MapGen.o:C:\Users\Zonaz\Documents\Game\MapGen\..\..\..\Desktop\MMOSTS\trunk\MapGen\MapGenGen.hpp|18|first defined here|
||=== Build finished: 18 errors, 0 warnings (0 minutes, 0 seconds) ===|
Last edited on
I would love to lose the globals but have nothing to substitute them with. Any help?


There are 2 common approaches.

1) Have the map owned by something with a very broad scope (maybe main()), and pass it into other functions as needed:

1
2
3
4
5
6
7
8
9
10
11
12
13
// where 'MapType' is the class/struct which contains your map data

void DoSomething( MapType& map )
{
    // use 'map' here
}

int main()
{
    MapType theOneAndOnlyMap;  // have it local to main

    DoSomething( theOneAndOnlyMap );  // when other functions need it, pass it in
}


2) Put your overall game in a 'Game' class and have the map as a member

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Game
{
private:
    MapType theMap;

public:
    void run();
};

int main()
{
    Game theGame;
    theGame.run();
}



19 errors all saying "multiple definition of 'bla bla' "


The issue with the 'MAP' error is because it's global. A global variable can only be defined in one source file... so if you put it in a header, it will be defined in every source file which #includes it (ie: defining it multiple times, which confuses the linker).

You have a similar problem with all the other errors.

Put function prototypes in header files.
Put function bodies in cpp files.

1
2
3
// in the header

void randomPlace();  // just prototype it 

1
2
3
4
5
6
// in a cpp file

void randomPlace()
{
   // put the actual body here
}
so no inline declarations like this?

double getFertility(){double temp = m_Fertility; return temp;}

I use cpp files for functions that I can't write on one line or similar.

the longest i go is something like this:

bool terrainExists(string temp){if(m_Terrain.find(temp) == m_Terrain.end()){return false;}else{return true;}}

Could you supply an example were you rewrite this function for example:
Declaring the MAP and activeSeeds in the main() and passing it's reference around.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//goes until it finds an empty spot
void randomPlace(){
    Block* p = MAP[rand() % X_COUNT, rand() % Y_COUNT];
    if(p->terrainExists("land")){
        randomPlace();
    }
    else{
        activeSeeds.push_back(p);
        return;
    }
    return;
}

//places seeds at random places
void seeds(int nr, unsigned int seed){
    srand(seed);
    for(int i = 0; i < nr; i++){
        randomPlace();
        }
}


Will edit once I moved all the functions around, will take a while.

And also I have a problem in Code::blocks where it freezes for a couple of seconds 4-5 times per minute if I'm trying to edit a file that is included in a project any ideas what that might be the cause?
Last edited on
so no inline declarations like this?


If they're in a class, yes that's fine.

If they're global... you have to move them to a cpp file or declare them as inline with the inline keyword:

inline double yourFunction() { /* do stuff. OK to be in a header file now */ }

Still I don't recommend you go overboard with function inlining.


On a side note....
 
double getFertility(){double temp = m_Fertility; return temp;}


Why are you running that through a temp variable? Why not just do this?

 
double getFertility() { return m_Fertility; }


???


problem in Code::blocks


No idea.
if I return a member value can't it be directly modifed through that?

or have I gotten this entire thing wrong?

and the example?
if I return a member value can't it be directly modifed through that?


No. Returned values, like parameters, are copies.

The only way it could be modified is if you return a reference

and the example?


??? huh?
Could you supply an example were you rewrite this function for example:
Declaring the MAP and activeSeeds in the main() and passing it's reference around.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//goes until it finds an empty spot
void randomPlace(){
    Block* p = MAP[rand() % X_COUNT, rand() % Y_COUNT];
    if(p->terrainExists("land")){
        randomPlace();
    }
    else{
        activeSeeds.push_back(p);
        return;
    }
    return;
}

//places seeds at random places
void seeds(int nr, unsigned int seed){
    srand(seed);
    for(int i = 0; i < nr; i++){
        randomPlace();
        }
}



And also: is there anyway you might help me in the future as well? your answers have been very pedagogical and helpful making sure I'll not repeat the same mistake twice.
Last edited on

Example without using a class. I don't recommend this because using a 2D array makes this syntax really awkward.

Also I didn't see activeSeeds until after I typed this example and I didn't want to retype it. But hopefully you get the idea.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
typedef Block MapArray[X_COUNT][Y_COUNT];
MapArray MAP;  // same as doing 'Block MAP[X_COUNT][Y_COUNT]'
   //  here, 'MapArray' can be used a typename.. like "int", but it refers
   //  to an array.  This will make passing as a function parameter easier

void randomPlace(MapArray& map){
    // note:  you were using a comma here, which was wrong.  Corrected to use
    //   brackets
    Block* p = map[rand() % X_COUNT ][ rand() % Y_COUNT];
    if(p->terrainExists("land")){
        randomPlace();
    }
    else{
        activeSeeds.push_back(p);
        return;
    }
    return;
}

//places seeds at random places
void seeds(int nr, unsigned int seed, MapArray& map){
    srand(seed);
    for(int i = 0; i < nr; i++){
        randomPlace(map);
        }
}

// to call it:
seeds( foo, bar, MAP );



Though really, a better approach would be to create a Map class, and keep all this stuff in there. That way you don't have to deal with the ugly typedef or passing around array nonsense:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Map
{
public:
    void seeds(int nr, unsigned int seed){
        srand(seed);
        for(int i = 0; i < nr; i++){
            randomPlace();
        }
    }

private:
    void randomPlace(){
        Block* p = blocks[rand() % X_COUNT ][ rand() % Y_COUNT];
        if(p->terrainExists("land")){
            randomPlace();
        }
        else{
            activeSeeds.push_back(p);
            return;
        }
        return;
    }

    Block blocks[X_COUNT][Y_COUNT];
    std::vector<Block*> activeSeeds;
};

//  Then instead of create MAP and activeSeeds separately... you just have a Map object
Map theMap;

// and to call seeds:

theMap.seeds( foo, bar );



And also: is there anyway you might help me in the future as well? your answers have been very pedagogical and helpful making sure I'll not repeat the same mistake twice.


I'm always happy to help, but sometimes I'm limited on time and/or interest in a specific problem.

The best thing to do is post your Qs on the board. If I see them and am interested, I'll chime in with a response. If I don't, and you really want a response from me, feel free to send me a PM with a link to the thread asking for input. I won't answer Qs in PM, but it helps draw my attention to a specific thread.
I will.

This will probably take most of today since I'm not comfortable with references yet but I'm confident that this will make it work.

Thanks a lot!
Topic archived. No new replies allowed.