-1

I'm working on a little C++ WinAPI wrapper. I'd like to create my own window and append my own controls to it. I created 2 classes : Window and Control. The problem is that I struggle to deal with the multiple header files, because the class Control needs the class Window and vice versa. When I compile my code, I get many errors..

Here are my files :

globals.h :

// globals.h
// I need this file to define a few constants and to include the main headers needed by my classes

#ifndef GLOBALS_H
#define GLOBALS_H

#include <windows.h>
#include <iostream>
#include <vector>

#include "window.h"

#define SOME_GLOBAL_CONSTANT    1

#endif

window.h :

// window.h
#ifndef WINDOW_H
#define WINDOW_H

#include "globals.h"
#include "control.h"

class Window
{
    public:
        Window(RECT windowRect);
        virtual ~Window();

        void appendChild(Control* child);

    private:
        RECT m_windowRect;
        Control m_staticBackground;
        std::vector<Control*> m_children;
};

#endif

window.cpp :

// window.cpp
#include "window.h"

Window::Window(RECT windowRect) : m_windowRect(windowRect)
{
    std::cout << SOME_GLOBAL_CONSTANT << std::endl;
}

Window::~Window()
{
    m_children.clear();
}

void Window::appendChild(Control* child)
{
    m_children.push_back(child);
}

control.h :

// control.h
#ifndef CONTROL_H
#define CONTROL_H

#include "globals.h"

class Control
{
    public:
        Control(Window* parentWindow);
        virtual ~Control();

    private:
        RECT m_controlRect;
        Window* m_parentWindow;
};

#endif

control.cpp :

// control.cpp
#include "control.h"

Control::Control(Window* parentWindow) : m_controlRect({}), m_parentWindow(parentWindow)
{
    std::cout << SOME_GLOBAL_CONSTANT << std::endl;
}

Control::~Control()
{}

And finally, main.cpp :

#include "globals.h"


class MyCustomControl : public Control
{
    public:
        MyCustomControl(Window* parentWindow) : Control(parentWindow)
        {}
        ~MyCustomControl()
        {}
};

class MyWindow : public Window
{
    public:
        MyWindow(RECT windowRect) : Window(windowRect)
        {
            kid1 = new MyCustomControl(this);
            appendChild(kid1);
        }
        ~MyWindow()
        {
            delete kid1;
        }

    private:
        MyCustomControl* kid1;
};

int main()
{
    MyWindow appWindow;

    std::cout << SOME_GLOBAL_CONSTANT << std::endl;

    return 0;
}

Here are all the compilation errors I get :

1>------ Build started: Project: include, Configuration: Debug Win32 ------
1>window.cpp
1>d:\visual studio 2017\projects\include\include\control.h(9): error C2061: syntax error: identifier 'Window'
1>d:\visual studio 2017\projects\include\include\control.h(14): error C2143: syntax error: missing ';' before '*'
1>d:\visual studio 2017\projects\include\include\control.h(14): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\visual studio 2017\projects\include\include\control.h(14): error C2238: unexpected token(s) preceding ';'
1>main.cpp
1>d:\visual studio 2017\projects\include\include\control.h(9): error C2061: syntax error: identifier 'Window'
1>d:\visual studio 2017\projects\include\include\control.h(14): error C2143: syntax error: missing ';' before '*'
1>d:\visual studio 2017\projects\include\include\control.h(14): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\visual studio 2017\projects\include\include\control.h(14): error C2238: unexpected token(s) preceding ';'
1>d:\visual studio 2017\projects\include\include\main.cpp(8): error C2664: 'Control::Control(const Control &)': cannot convert argument 1 from 'Window *' to 'const Control &'
1>d:\visual studio 2017\projects\include\include\main.cpp(8): note: Reason: cannot convert from 'Window *' to 'const Control'
1>d:\visual studio 2017\projects\include\include\main.cpp(8): note: No constructor could take the source type, or constructor overload resolution was ambiguous
1>d:\visual studio 2017\projects\include\include\main.cpp(32): error C2512: 'MyWindow': no appropriate default constructor available
1>d:\visual studio 2017\projects\include\include\main.cpp(13): note: see declaration of 'MyWindow'
1>control.cpp
1>d:\visual studio 2017\projects\include\include\window.h(13): error C2061: syntax error: identifier 'Control'
1>d:\visual studio 2017\projects\include\include\window.h(17): error C3646: 'm_staticBackground': unknown override specifier
1>d:\visual studio 2017\projects\include\include\window.h(17): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>d:\visual studio 2017\projects\include\include\window.h(18): error C2065: 'Control': undeclared identifier
1>d:\visual studio 2017\projects\include\include\window.h(18): error C2059: syntax error: '>'
1>d:\visual studio 2017\projects\include\include\window.h(18): error C2976: 'std::vector': too few template arguments
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.10.25017\include\vector(700): note: see declaration of 'std::vector'
1>Generating Code...
1>Done building project "include.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Any help would be greatly appreciated.

Thank you in advance,

winapiwrapper.

Edit: I have also read this thread, but I couldn't solve my problem.

winapiwrapper
  • 125
  • 1
  • 12
  • Possible duplicate of [When can I use a forward declaration?](https://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration) – Dai Jun 10 '17 at 00:01
  • You're looking for Forward Declaration. Read this QA: https://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration – Dai Jun 10 '17 at 00:01
  • It means that the compiler can't find the 'Window' class, and I already tried to put a forward declaration of the class Window, but the compiler kept yelling at me (it said something like 'invalid use of incomplete type' or so). – winapiwrapper Jun 10 '17 at 00:05
  • I have also read [this](http://forums.codeguru.com/showthread.php?462973-Mutually-dependent-classes-and-inheritance) thread, but I couldn't solve my problem. – winapiwrapper Jun 10 '17 at 00:07
  • Groovy. You got step 1. That exposes that you have a [circular dependency](https://stackoverflow.com/questions/625799/resolve-header-include-circular-dependencies) causing the incomplete type error. – user4581301 Jun 10 '17 at 00:07
  • Thank your for the answer, but even when I put a forward declaration before the class 'Control' I get many errors. – winapiwrapper Jun 10 '17 at 00:20
  • Not that this will help you fix your current issue with your design, but consider that controls and windows are conceptually identical or almost identical. It would certainly simplify your issue if one inherited from the other. – Retired Ninja Jun 10 '17 at 01:42

2 Answers2

1

Move "window.h" from globals.h to controls.cpp. And put class Window; before class Controls { in controls.h. This is called forward declaration.

You even do not need window.h in controls.cpp, you can move it directly to your main.cpp.

Yuki
  • 3,857
  • 5
  • 25
  • 43
  • Recommend explaining why all this is necessary. – user4581301 Jun 10 '17 at 00:16
  • Thank you for your help. I did move "window.h", then removed it and I put class Window; before class Control{}; but now I get these errors : window.cpp(4): error C2512: 'Control': no appropriate default constructor available control.h(9): note: see declaration of 'Control' – winapiwrapper Jun 10 '17 at 00:16
  • main.cpp(5): error C2504: 'Control': base class undefined 1>d:\visual studio 2017\projects\include\include\main.cpp(7): error C2061: syntax error: identifier 'Window' – winapiwrapper Jun 10 '17 at 00:17
  • main.cpp(7): error C2065: 'parentWindow': undeclared identifier main.cpp(8): error C2614: 'MyCustomControl': illegal member initialization: 'Control' is not a base or member main.cpp(14): error C2504: 'Window': base class undefined main.cpp(17): error C2614: 'MyWindow': illegal member initialization: 'Window' is not a base or member main.cpp(18): error C2664: 'MyCustomControl::MyCustomControl(const MyCustomControl &)': cannot convert argument 1 from 'MyWindow *const ' to 'const MyCustomControl &' – winapiwrapper Jun 10 '17 at 00:17
  • main.cpp(18): note: Reason: cannot convert from 'MyWindow *const ' to 'const MyCustomControl' – winapiwrapper Jun 10 '17 at 00:17
  • main.cpp(18): note: No constructor could take the source type, or constructor overload resolution was ambiguous main.cpp(19): error C3861: 'appendChild': identifier not found main.cpp(32): error C2512: 'MyWindow': no appropriate default constructor available main.cpp(13): note: see declaration of 'MyWindow' – winapiwrapper Jun 10 '17 at 00:17
  • @winapiwrapper If you didn't read the link above on circular dependencies, do it now: https://stackoverflow.com/questions/625799/resolve-header-include-circular-dependencies – user4581301 Jun 10 '17 at 00:28
  • @winapiwrapper it is just another issues, that you do not have a default constructor for your `Control` class. – Yuki Jun 10 '17 at 00:57
1

Welcome to programming. Strike down one error message and two shall take its place.

Well... Not really. What's happening is as you resolve one error, this reveals the errors that the precious errors were hiding.

First a PSA:

Don't write much code without compiling and testing. This way you have a smaller amount of code you need to check when something goes wrong. Start with a main function. Make sure it builds. Add the headers you need. Make sure it builds. Write a function for main to call. Make sure it builds. Repeat until program is finished. Trying to add too much all at once results in cascading storms of errors like you have experienced here.

Global include header files are usually a sucker bet. They often lead you into problems like the circular dependency you have here and make files include everything even when they don't have to. This can slow down build times.

Next, read the following link before continuing: Resolve header include circular dependencies

Now on with the answer:

window.h includes control.h. control .h includes window.h though global.h. This results in the Chicken and egg problem discussed in the above link. One of the headers is going to be included before the other and not be able to find the contents of the other file. Fortunately control.h only needs a reference to Window, not the whole thing, and this can be satisfied with a forward declaration and removing the global include file.

I'm not going to demonstrate cleaning this up. The process is well documented in the link.

This exposes hydra head number 2: Window contains Control m_staticBackground and does not explicitly initialize it. This results in the compiler hunting around for a default constructor for Control, something that does not exist.

Solution: Explicitly initialize m_staticBackground

Window::Window(RECT windowRect) : m_windowRect(windowRect),
                                  m_staticBackground(this)
{
    std::cout << SOME_GLOBAL_CONSTANT << std::endl;
}

BIG Mother Freaking note here: m_staticBackground(this) is dodgy as hell. this has not been fully constructed yet, so if you do more than simply store it in Control::Control (which is all you are currently doing) very bad, unpredictable things can happen. Do not use parentWindow or m_parentWindow inside the body of the Control constructor. If possible find a better, safer, way to do this. If not possible, document it with DO NOT USE messages to remind your future self or anyone else looking at the code not to use them.

Once this has been fixed you get to

MyWindow appWindow;

over in main. MyWindow's constructor requires a RECT you don't currently have a RECT to provide, so I'll stop here.

user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Thank you for your help. I forgot about the RECT in the MyWindow's contructor. I solved my problem like this : I put 2 forward declarations : class Control; before class Window {} and vice versa. Then I got 2 or 3 errors, which I solved by storing a pointer to 'm_staticBackground' in the Window class instead of the object itself. This link about circular dependencies helped me a lot. Thank you again! – winapiwrapper Jun 10 '17 at 15:22
  • A little update: I didn't even need to put a forward declaration of Window before the class GraphicElement. – winapiwrapper Jun 10 '17 at 15:36