Solved

C++: Base class undefined.  Possible #include hell?

Posted on 2008-06-25
7
594 Views
Last Modified: 2013-12-14
I am writing a DLL that will serve to wrap the frequently used parts of the Win32 API in a set of C++ classes.  My problem is that I keep getting compilation errors that some base class or another is undefined.  I suspect this is a #include problem because a lot of my classes all depend on each other and there are a couple of circular dependencies that I'm resolving with the #pragma once directive.  I've read that problems can be caused this way, but I'm not sure of that's what's going on here.

Here are the classes I currently have:

FzaLib.h - The main header to be #included from projects that use this dll.  Only contains dllmain() and some #defines.
Controller.h - Contains the window procedure and code to register/unregister GuiObjects in a vectored list.
Exception.h - Derives from std::exception.  Needed so I can throw them when API calls fail in constructors.
Easel.h - Container for DC, RECT, brush, and color for GDI functions.
GuiObject.h - Base class for Windows GUI elements.  Basically a container for HWND with some functions.
Window.h - Derives from GuiObject.  A generic application window.
....h - Other class types that will derive from GuiObject.

Here's how their relationships work:
FzaLib.h needs to #include Controller.h.  That's where the window procedure is.
Controller.h - Needs GuiObject.h, Exception.h, and the system headers <windows.h> and <gdiplus.h>.
Exception.h needs <exception> and <windows.h> again for the GetLastError() and FormatMessage() API calls.
Easel.h #includes <windows.h> and <gdiplus.h> again.
Here's where it gets sticky.  GuiObject.h needs Controller.h because controller is a friend.  Circular dependency #1.
Window.h and other derived classes need GuiObject.h.  Circular dep #2 because GuiObject #includes controller.h.
Controller.h needs to #include all of GuiObject's derived classes or else they can't be instantiated unless the program using the library #includes each one manually.  Circular dependency #3.

The point where the compiler barfs is when it tries to compile Window.cpp.  It swears that GuiObject base class is undefined.  Coincidentally (or maybe not), this is the only point where inheritance is used thus far (I haven't written any of the other derived classes yet, but I'm sure the problem will be the same).

How do I #include this to work?  Maybe I am wrong in assuming this is a #include problem?  Is there something special you have to do to use inheritance within a DLL?
0
Comment
Question by:cuziyq
  • 4
  • 3
7 Comments
 
LVL 53

Expert Comment

by:Infinity08
ID: 21870077
>> and there are a couple of circular dependencies that I'm resolving with the #pragma once directive.

You do not solve circular dependencies with #pragma once. You solve them by using forward declarations, like in :

        class Test2;      // <--- forward declaration

        class Test {
            Test2 *t;
        };
       
        class Test2 {
            Test *t;
        };


Can you show all of your header files ?
0
 
LVL 14

Author Comment

by:cuziyq
ID: 21870188
Forward declaration doesn't work in this case.

class FZALIB_API GuiObject;  <- This does not work.

class FZALIB_API Window :public GuiObject
{
     ...
};

Here's a copy of what I've got so far.  The program is mostly just stubs at this point, so if something doesn't make sense, it's probably because I've been fiddling around with this issue and not paying attention to the actual implementation just yet.  But ask me about it anyway :-)

Note, I had to rename the files to .txt so EE would let me upload them.  They're all .h files.
Controller.txt
Easel.txt
Exception.txt
FzaLib.txt
GuiObject.txt
Window.txt
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 21870249
>> class FZALIB_API GuiObject;  <- This does not work.

Afaics, the problem is not on that level, but rather in the controller.h file (indirectly included in window.h). Instead of the includes there, use forward references, so :

        class Exception;
        class GuiObject;
        class Window;

instead of :

        #include "Exception.h"
        #include "GuiObject.h"
        #include "Window.h"

The exact errors you get would also be helpful.

Note that you don't necessarily need to include a whole header file, especially if you're only using a pointer to the class from that header file (just a forward declaration is enough, and avoids problems with multiple includes).
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 14

Author Comment

by:cuziyq
ID: 21870333
Hmmm.  Unfortunately, with Exception.h and GuiObject.h, the implementation in Controller.cpp calls methods from both, so it needs to actually #include these files.

As far as Window.h is concerned, the Controller class doesn't use it directly, but it needs to be #included somewhere or else it won't be visible outside the DLL unless every project that uses it #includes Window.h manually -- which will be a real pain if I get more than a few of these.  I don't really care where it gets included from.  Putting it in Controller.h, however, produces the fewest complaints from the compiler that GuiObject base class is undefined (6 times if I put it in GuiObject.h vs just 1 time if I put it in Controller.h).

The only gripe I get from the compiler is as follows:
error C2504: 'GuiObject' : base class undefined      window.h:       line 6

Although I can get that a dozen or more times depending on where I #include window.h
0
 
LVL 53

Accepted Solution

by:
Infinity08 earned 500 total points
ID: 21870485
>> the implementation in Controller.cpp calls methods from both, so it needs to actually #include these files.

Yes, the controller.cpp needs those includes, but NOT the controller.h file.


>> As far as Window.h is concerned, the Controller class doesn't use it directly, but it needs to be #included somewhere or else it won't be visible outside the DLL unless every project that uses it #includes Window.h manually

You can leave the include for window.h in the controller.h file then - it shouldn't impact anything.
0
 
LVL 14

Author Comment

by:cuziyq
ID: 21874375
Hah.  You know I had never even considered putting it in the implementation file.  In fact, I remember when I was in school, my CS instructor used to say he would dock us points if he found a #include in a .cpp file other than the header that goes with it.  Since then I've always put includes in the headers and have never had a problem or even thought to do it differently.

So the big question is . . . why does that work?  Does the compiler not parse the header and implementation together?

Also, I plan to make Controller.h my precompiled header.  Will having the include in the cpp file still include it in the resulting PCH file?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 21875028
>> my CS instructor used to say he would dock us points if he found a #include in a .cpp file other than the header that goes with it.

That's not a good idea. Generally, you want to limit the number of includes in the header file to the absolute necessary. The problem is that all these includes will be included in any file that includes that header file, and that can cause problems like you are experiencing now.


>> So the big question is . . . why does that work?  Does the compiler not parse the header and implementation together?

The problem is that since you have a circular inclusion, and protected all your headers with pragma once, the headers can only be present once in each file.

For your specific case (originally), the Window.cpp would look like this :

        contents of Exception.h
                                                  // <--- note here
        contents of Controller.h
        contents of Easel.h
        contents of GuiObject.h
        contents of Window.h

        contents of the Window.cpp file

Now, the location where I put the comment is where these includes were supposed to be :

        #include "GuiObject.h"
        #include "Window.h"

but since they had already been included, and the pragma once allows only one inclusion, these two includes are ignored.

So, you end up with the Controller class being defined without having the GuiObject and Window classes defined yet ... That causes the error you saw.


When putting only the absolutely necessary includes in each header file (and using forward references where possible/needed), you avoid this problem.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
This article describes relatively difficult and non-obvious issues that are likely to arise when creating COM class in Visual Studio and deploying it by professional MSI-authoring tools. It is assumed that the reader is already familiar with the cla…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now