Jump to content
  • Advertisement
Sign in to follow this  
trentfaris242

Lots of syntax errors when using abstract classes

This topic is 1158 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey all,

 

I recently started developing my own game engine (just for fun), but I've run into a really weird error when implementing abstract classes. To help you all understand the issue I'm having, I've attached a simply UML diagram of how my game engine currently works (subject to change).

 

[spoiler]KjRSpre.png[/spoiler]

 

This is a pretty simple design. My engine initializes and keeps track of several different modules or components called managers. Each individual manager needs access to some of the same types of information, so I thought it'd be easiest to create an abstract class that they all derive from. The abstract class contains a constructor, an init() function (this is the abstract function that all managers should override), a reference to the manager, and a reference to the engine.

 

I'm using Visual Studio 2013, and in the viewport I see no errors of any kind. I'm pretty sure I've implemented this correctly, but when I try to compile, I get a slew of different kinds of errors (mostly syntax) that reference "blank" spots in the code.

 

Here's the full list of errors:

[spoiler]JbNjK7K.png[/spoiler]

 

If you reference these areas of the code, they all point to the beginning of a line, and their description makes little sense in the context. The 3 main headers that are receiving these errors are BuddyEngine.h, Manager.h, and RenderManager.h. I'll post the code for you to see. These are all pretty simple implementations.

 

BuddyEngine.h

[spoiler]

#pragma once
#ifndef BUDDYENGINE_H
#define BUDDYENGINE_H

#include "RenderManager.h"
//#include "InputComponent.h"
//#include "SoundComponent.h"

class BuddyEngine {
public:
	BuddyEngine(int argc, char **argv);

	RenderManager& getRenderManager();

	static BuddyEngine& getInstance();

private:
	RenderManager* renderManager;

	static BuddyEngine* instance;
};

#endif

[/spoiler]

 

Manager.h

[spoiler]

#pragma once
#ifndef MANAGER_H
#define MANAGER_H

#include "BuddyEngine.h"

class Manager {
public:
	Manager(BuddyEngine* engine, int argc, char **argv);

	BuddyEngine& getBuddyEngine();

	static Manager& getInstance();

private:
	BuddyEngine* buddyEngine;

	static Manager* instance;

	virtual bool init(int argc, char **argv) = 0;
};

#endif

[/spoiler]

 

RenderManager.h

[spoiler]

#pragma once
#ifndef RENDERMANAGER_H
#define RENDERMANAGER_H

#include "Manager.h"
#include "WorldRender.h"

class RenderManager : public Manager {
public:
	RenderManager(BuddyEngine* engine, int argc, char **argv) : Manager(engine, argc, argv) {}

	GLFWwindow& getWindow();

	WorldRender& getWorldRender();

private:
	GLFWwindow* window;

	WorldRender* worldRender;

	virtual bool init(int argc, char **argv);

	static void render();
};

#endif

[/spoiler]

 

I hate having to post lots of code for help, but I'm afraid I've hit a brick wall with this ghost of an issue. If anyone has any ideas of what could be causing these errors, please let me know. If you need me to post the implementations of these functions (.cpp files), I can do that as well. Thanks for reading!

Edited by trentfaris242

Share this post


Link to post
Share on other sites
Advertisement
You've got circular dependencies. Your header files are referencing each other. You can probably break some of this by forward declaring BuddyEngine in Manager.hpp instead of including the BuddyEngine header.

[edit]
I'm on my phone right now, so not easy to go into more detail.

Share this post


Link to post
Share on other sites

You've got circular dependencies. Your header files are referencing each other. You can probably break some of this by forward declaring BuddyEngine in Manager.hpp instead of including the BuddyEngine header.

[edit]
I'm on my phone right now, so not easy to go into more detail.

I literally just discovered this and came back here to update it. I'm new to C++ and this is something I'm having to get used to. Dependencies don't seem to flow as nicely, and as "OOP-y", as imports do in Java, which is what I'm used to.

 

Thanks for the verification!

Share this post


Link to post
Share on other sites
You can have class declarations without content if you never use its internals.
 
class B;

class A {
public:
    A(B *b);
    ~A();

    B *b;
};
I don't know exactly how that works with references (you'h have to look that up), but this is a useful technique to avoid recursive includes.

Another option is to move most of the #include into the .cpp files, but that makes .h file dependent on #include-ing other .h files before it without clear specification what you need where.


I am also somewhat wondering why you return a reference like GLFWwindow& getWindow(); while internally you have a pointer GLFWwindow* window; Unlike Java, C++ does not have nullptr references.
From that perspective, making window a contained data member may make more sense? Another option is of course to return a pointer at the public interface as well.
(your solution is an option too, if you get the pointer after construction, but you do always get it, so you never have to return a nullptr.

Last but not least, please be aware that you cannot call derived methods until you have arrived in the constructor of the derived class. That means here that you cannot call 'init' from the constructor of the base class.
Since it's a private method, I am not sure who is going to call it from the base class, in this code.

Share this post


Link to post
Share on other sites

You can have class declarations without content if you never use its internals.
[...] 
I don't know exactly how that works with references (you'h have to look that up), but this is a useful technique to avoid recursive includes.


Never using its internals is one part of the coin. The other side is that you cannot declare a variable (local, global or member) of that type while it is incomplete (that is, if the full body is not known at that point) nor instantiate the type via any flavor of dynamic allocation. Pointers and references to the type are no problem since the compiler does not need to know anything about the type except that it exists.
Using an incomplete type as a template parameter will depend on the template. It might refuse to compile at all (like std::unique_ptr which needs more knowledge about the type) or work fine (like std::shared_ptr or std::vector), possibly with some limitation regarding what member functions you can call while the type is incomplete.

Share this post


Link to post
Share on other sites

Using an incomplete type as a template parameter will depend on the template. It might refuse to compile (like std::unique_ptr which needs more knowledge about the type) or work fine (like std::shared_ptr or std::vector), possibly with some limitation regarding what member functions you can call while the type is incomplete.

It's true that there are some templates that require a fully-defined type, but std::unique_ptr<> is certainly not one of them.  The only place you need to have a fully defined type for that template class is where its destructor is invoked, at that should if at all possible be in an implementation file, not a header file.

 

A good general rukle of thumb is to forward declare classes if you can, and only include their definition (header) files if you must.  That rule speeds up compile times, breaks circular dependencies, and minimizes the surface for unintentionally exposing the wrong interface.

Share this post


Link to post
Share on other sites
Mhm. I remember having had considerable amounts of stress with std::unique_ptr and incomplete types. According to the documentation the only issue should be with std::default_deleter for some operations, but I remember my problems being more annoying. Maybe because it was early in C++11s life cycle or I had a secondary issue?

Share this post


Link to post
Share on other sites

So here would be a modified version of your code (if you haven't fixed it already).

 

BuddyEngine.h

[source]

#pragma once

#ifndef BUDDYENGINE_H

#define BUDDYENGINE_H

 

class RenderManager;

 

class BuddyEngine {

public:

    BuddyEngine(int argc, char **argv);

 

    RenderManager& getRenderManager();

 

    static BuddyEngine& getInstance();

 

private:

    RenderManager* renderManager;

 

    static BuddyEngine* instance;

};

 

#endif

[/source]

 

Manager.h

[source]

#pragma once

#ifndef MANAGER_H

#define MANAGER_H

 

class BuddyEngine;

 

class Manager {

 

public:

    Manager(BuddyEngine* engine, int argc, char **argv);

 

    BuddyEngine& getBuddyEngine();

 

    static Manager& getInstance();

 

private:

    BuddyEngine* buddyEngine;

 

    static Manager* instance;

 

    virtual bool init(int argc, char **argv) = 0;

};

 

#endif

[/source]

 

RenderManager.h

[source]

#pragma once

#ifndef RENDERMANAGER_H

#define RENDERMANAGER_H

 

#include "Manager.h"

 

class WorldRender;

 

class RenderManager : public Manager {

 

public:

    RenderManager(BuddyEngine* engine, int argc, char **argv) : Manager(engine, argc, argv) {}

 

    GLFWwindow& getWindow();

 

    WorldRender& getWorldRender();

 

private:

 

    GLFWwindow* window;

 

    WorldRender* worldRender;

 

    virtual bool init(int argc, char **argv);

 

    static void render();

};

 

#endif

[/source]

 

Then in your implementation files (.cpp) you can include the appropriate headers.

 

[edit]

Theoretically fixed the inheritance issues mentioned below

Edited by Rattrap

Share this post


Link to post
Share on other sites

That will still not work, because, making a class you want to derive from a singleton.

Which of the 3 derived classes should it return? How can the base class know its derived classes anyway?

Just cut out anything remotely related to a singleton, global or static variable. You have a broken copy of the singleton pattern anyway, cause it allows contructing more than a single one.

While you are at it, don't name anything engine or manager which signals "messy agglomeration of code violating the SRP", choose good names representing the single responsibility of each class.

My guess is, the code inside the cpp files might just be collapsed into about a dozen lines doing calls to GLFW to init, open a window, clean up and about twice as much to implement the needed callbacks for input and there is nothing which actually needs a manager?

Edited by wintertime

Share this post


Link to post
Share on other sites


That will still not work, because, making a class you want to derive from a singleton.

 

Your correct.  I completely missed the inheritance there.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!