Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

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

Posted on 2008-06-25
7
Medium Priority
?
620 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
Visualize your virtual and backup environments

Create well-organized and polished visualizations of your virtual and backup environments when planning VMware vSphere, Microsoft Hyper-V or Veeam deployments. It helps you to gain better visibility and valuable business insights.

 
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

Visualize your virtual and backup environments

Create well-organized and polished visualizations of your virtual and backup environments when planning VMware vSphere, Microsoft Hyper-V or Veeam deployments. It helps you to gain better visibility and valuable business insights.

Question has a verified solution.

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

In my previous two articles we discussed Binary Serialization (http://www.experts-exchange.com/A_4362.html) and XML Serialization (http://www.experts-exchange.com/A_4425.html). In this article we will try to know more about SOAP (Simple Object Acces…
Article by: evilrix
Looking for a way to avoid searching through large data sets for data that doesn't exist? A Bloom Filter might be what you need. This data structure is a probabilistic filter that allows you to avoid unnecessary searches when you know the data defin…
THe viewer will learn how to use NetBeans IDE 8.0 for Windows to perform CRUD operations on a MySql database.
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.

609 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