Sign in to follow this  
Flimflam

Header problems. (solved)

Recommended Posts

Flimflam    665
I've been working with C++ for a few years now, and I'm no pro, but I'm pretty proficent with the language. And as such I'm really stumped with something. I can't really say I've had a problem with the concept of headers since god knows when. I'm starting to wonder if somehow I'm just forgetting important things, to be honest, I'm rather embarassed by this problem. Essentially, I'm getting all sorts of random errors where c++ files aren't able to find other headers that I've mapped out. I was wondering if someone could give this small project a look over (aka compile and see the errors) and tell me what stupid thing I've done this time. Because honestly, I have no clue why this is giving me problems. It's never happened to me before. One of the errors I experiance: \Aeris\aeris\shell.h(10): error C2061: syntax error : identifier 'Panel' This error, despite inside the same file I have: #include "panel.h", which defines the class. However, if inside the function it's complaining about (Add), if I specify "class Panel *pPanel", it seems to compile fine. Then it just gives me another error about being unable to find the base class Control. Which, happens to be included in the file that error complains about... Sigh http://www.sonikstudios.net/junk/Aeris.rar Thank you... [Edited by - Flimflam on November 27, 2005 5:03:20 AM]

Share this post


Link to post
Share on other sites
Basiror    241
try this
i have never seen the use of namespaces ass you used it in the source file
but you could use "using namspeace xyz" add the beginning if you don t like it as in my example

hope that helps

P.S.: #ifndef HEADERNAME #define HEADERNAME #endif for large projects i would add this to each header file to avoid problems with multiple includes of one and the same header


int Aeris::Panel::Add(Aeris::Control *pControl, adword id, astring name) {
if(pControl == NULL) return E_NULLREFERENCE;

// Set values
pControl->SetId(id);
pControl->SetName(name);
pControl->SetParent(this);

// Add item to vector
m_Itemsv.push_back(pControl);

// Add item to map
if(name != "")
m_Itemsm[name] = pControl;

return 0;
}

};

Share this post


Link to post
Share on other sites
DadleFish    150
Well, I haven't got VS.NET around here right now, but looking at it I think you didn't put an additional include directory "aeris". Remember that when you say something like '#include "shell.h"', the compiler will search in the current directory, then in all the additional include directories, then in all the environment include directories.

Share this post


Link to post
Share on other sites
Basiror    241
the file depending on aeris are all in the same folder
the #include directive starts to search for the file relative to the current folder where the #include directive is used

*from the MSDN*

Share this post


Link to post
Share on other sites
Flimflam    665
Well, I dont think it's having any problem finding the files I'm #including. It would give me an error if it couldn't. It's also very picky on the classes it wants to barf up errors for. Stuff that resides in Direct3d.h and all that seem to have no problem being used somewhere. Seems to be only panel.h and control.h that give me problems. The only file that directly referneces the aeris folder is main.cpp, which happens to be outside the folder. Everything else share the folder.

The include directory aeris is really just the folder there. I included the relative to the project directory. From the inside there the files seem to have no problem accessing each other, safe for a select few.

Share this post


Link to post
Share on other sites
Mxz    504
Hi ho Flimflam. Quite a pickle you have got yourself into here. [smile]

Essentially the main problems lie in the needless inclusion of header files in header files. To see what is going on, you need to look at the translation units produced by the preprocessor after it has acted on your source files. The translation unit is the name given to what remains of your source file after the preprocessor has acted.

The first errors come from compiling panel.cpp. All the errors come from compiling the first two lines of code.


//panel.cpp
#include "panel.h"
#include "aeris.h"










So when the preprocessor sees the first line, it will execute the #include directive and start splicing the code from panel.h into your translation unit, probably executing the preprocessor directives from that file as it goes.



//panel.h
#pragma once

#include "control.h"

class Panel : public Control {...};










The first line of panel.h is the non-standard #pragma once. This will prevent panel.h from being included in this translation unit more than once, which is fine.

The next line in panel.h is the include directive for the header control.h. So again, the preprocessor will start splicing the code from there into the translation unit.


//control.h
#pragma once

#include "aeris.h"

class Control {...};









The first line of control.h is again the #pragma once directive, which will prevent this header form being included more than once again. The second line is the troublesome #include "aeris.h". This header file attempts to include quite a number of headers. For the purposes of fixing the errors, imagine the header only contained the following code


//aeris.h
#pragma once
...
#include <vector>
#include "control.h"
#include "panel.h"
#include "shell.h"
..










So again, the preprocessor will get to work here, so far it has only been executing #include and #pragma once directives, it has not yet put any code in to the translation unit. The first line here is the #pragma once again, so aries.h will not be included again in this translation unit. The next line is the inlude directive for vector.h, and at last we get some code in the translation unit. It will look something like


//translation unit
...
template<...>
class vector{...};
...










Next up for the preprocessor is the following two lines in aeries.h


//aeries.h
....
#include "control.h"
#include "panel.h"









It should be clear that, since you have already executed #pragma once in these two files (control.h and panel.h), the preprocessor will do nothing when it sees the include directives for these two files this time. This just leaves us with the the last relevant line in aeries.h, which is #include"shell.h". This is the file where the errors come from when compiling. It contains the following code


//shell.h
#pragma once

#include "aeris.h"
#include "panel.h"

class Shell {
...
typedef std::vector<Panel*> pvector;
...
};











So the preprocessor again gets to work on this file, the pragma once is first, preventing it from being included again, then we have the aeris.h include directive, again it should be obvious that this will again do nothing, as we have already executed the #pragma once directive on the header aeris.h the first time we included it (in control.h). Next we have the panel.h include directive, again this line does nothing, as again we have executed the #pragma once directive on panel.h. Then we have more code to go into our translation unit, the namely the shell class definition.



//translation unit
...
template<...>
class vector{...};
...

class Shell {
typedef std::vector<Panel*> pvector;
...
};










This bring to an end the header file include directives in aeris.h. The preprocessor thus resumes work back in control.h. All that is left in here is to splice the Control class definition into the translation unit.


//translation unit
...
template<...>
class vector{...};
...

class Shell {
typedef std::vector<Panel*> pvector;
...
};

class Control{...};










That brings to an end control.h. Then it resumes work on panel.h. All that is left here is to paste the Panel class definition into the translation unit.


//translation unit
...
template<...>
class vector{...};
...

class Shell {
typedef std::vector<Panel*> pvector;
...
};
class Control{...};
class Panel{...};










That brings to an end the first include directive statement from Panel.cpp, namely the #include "Panel.h" statement. The second include directive from Panel.cpp, namely #include"aeris.h", does nothing, as we have executed the #pragma once directive on this header file when we included it earlier from contorl.h. The preprocessor then splices the remaining code from Panel.cpp into the translation unit, and at last, we have our final translation unit.



//translation unit
...
template<...>
class vector{...};
...

class Shell {
typedef std::vector<Panel*> pvector;
...
};
class Control{...};
class Panel{...};

int Panel::Add(...)
{...}











So now it is the compilers turn to get to work on this translation unit. The first problem it encounters, and the one which gives the error when compiling your code, is the line

typedef std::vector<Panel*> pvector;


The compiler sees and understands what vector is, as the class template has already been defined withing the translation unit. But as you can see from looking at the resulting translation unit, it has no idea what Panel is at this point in the compilation process, as Panel has not yet been defined, it is defined later on in the translation unit. This causes the compiler error.

So after that exhaustive look at what the preprocessor is doing with the code, the problem is very simple, you have a circular inclusion problem. [smile]

I think the problem in the code is essentially aeries.h. It is causing all sorts of circular inclusions because of the fact that it includes most of the files which include it. In the above case, including the panel.h header file in a source file is always going to result in shell.h being included in the same translation unit. Luckily, there is an easy solution to the above error in the case of shell.h, as the typedef declaration only requires that Panel be declared, and not defined. So you can simply remove the #include"panel.h" statement from shell.h, and replace it with the following


//shell.h
#pragma once

#include "aeris.h"

class Panel;

namespace Aeris {

class Shell {
public:
int Add(Panel *pPanel, adword id, astring string);

private:
typedef std::vector<Panel*> pvector;
typedef std::map<astring, Panel*> pmap;

pvector m_Itemsv;
pmap m_Itemsm;
};

};










That fixes all but one of the errors. The other error is another circular inclusion problem which cannot be solved quite as easily. The problem in the case of the last remaining error is that including control.h means that panel.h will also be included, which requires the Control class to be defined before the Panel class is defined, which it wont be, as Panel is defined before Control due to the panel.h header being included by the first include statement in control.h. So to fix the errors, you are going to have to get rid of the header dump that is aeris.h, and include the correct files in the correct headers.

[Edited by - Mxz on November 27, 2005 5:29:22 AM]

Share this post


Link to post
Share on other sites
Flimflam    665
Words cannot express how thankful I am right now :)

I didn't actually know that sort of impact would happen. aeris.h was really the file the end user (namely me) would be using to basicly USE The library. I thought if I was to pretty much include that I could have basic access to things. But now that you've explained it in such detail, I see that was completely throwing it off.

I've since made a new file, _aeris.h, and put all my enums and such in there, and just manually included everything I neeed in the indevidual files, and it compiles without a hitch. Big thanks again :) It's a good thing this happened to me soon, rather than later when I had a large variety of objects to change.

Again, thank you very very much.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this