?
Solved

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

Posted on 2008-06-25
7
Medium Priority
?
615 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Certified OpenStack Administrator Course

We just refreshed our COA course based on the Newton exam.  With 14 labs, this course goes over the different OpenStack services that are part of the certification: Dashboard, Identity Service, Image Service, Networking, Compute, Object Storage, Block Storage, and Orchestration.

 
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 2000 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

Enroll in August's Course of the Month

August's CompTIA IT Fundamentals course includes 19 hours of basic computer principle modules and prepares you for the certification exam. It's free for Premium Members, Team Accounts, and Qualified Experts!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Many of us here at EE write code. Many of us write exceptional code; just as many of us write exception-prone code. As we all should know, exceptions are a mechanism for handling errors which are typically out of our control. From database errors, t…
For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
The viewer will learn how to use NetBeans IDE 8.0 for Windows to connect to a MySQL database. Open Services Panel: Create a new connection using New Connection Wizard: Create a test database called eetutorial: Create a new test tabel called ee…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
Suggested Courses

771 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