Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't cast superclass to subclass

I'm currently writing an abstraction layer between my game and the rendering engine. Unfortunately, I came accross a problem: I just can't seem to cast a superclass (The abstract interface) to a subclass (The implementation for a concrete engine). Here is my code:

IInitationSettings.h

class IInitationSettings {};

OxygineInitiationSettings.h

#include "IInitiationSettings.h"
#include "core/oxygine.h"
class OxygineInitiationSettings : public IInitationSettings, public oxygine::core::init_desc {
public:
    OxygineInitiationSettings(const char* title, bool vsync, bool fullscreen, int width, int height);
};

OxygineInitiationSettings.cpp

#include "OxygineInitiationSettings.h"
OxygineInitiationSettings::OxygineInitiationSettings(const char* title, bool vsync, bool fullscreen, int width, int height) : oxygine::core::init_desc() {
    this->title = title;
    this->vsync = vsync;
    this->fullscreen = fullscreen;
    this->w = width;
    this->h = height;
}

The abstract init method:

static void init(IInitiationSettings& initSettings);
void GraphicsFactory::init(IInitiationSettings& initSettings){
#ifdef USE_OXYGINE_RENDERING
    OxygineInitiationSettings settings = initSettings; //Does not work
    oxygine::core::init_desc desc = initSettings; // Does not work
    oxygine::core::init((oxygine::core::init_desc)((OxygineInitiationSettings)initSettings)); //Does not work
#endif
}

How do I cast my abstract interface to the concrete implementation? I want to add a newInitiationSettings-Method too, which will return an IInitiationSettings Object which I will pass into the init method, in order to have a clean code. (I want my ingame-code look like this:

GraphicsFactory::init(GraphicsFactory::newInitiationSettings(args));

)

Any ideas?

like image 656
Fly Avatar asked Dec 20 '22 04:12

Fly


2 Answers

The fundamental mistake here is an attempt to cast the object itself to a different type in your abstract init method. Casting upward (i.e. towards the base class) results in object slicing, as it makes a copy of just the base class's data. That's typically bad, but casting downward is potentially impossible. So the compiler won't let you.

What you really want to do is work at the reference or pointer level. Speaking loosely, a reference is syntactic sugar for a pointer, and a pointer to an object is substitutable for a pointer to one of its base classes. That's why you could pass a derived through a parameter of type base&. But when you try to get your derived back, you have to ask for a derived& or a derived*. In your case this looks more like one of these:

static_cast<OxygineInitiationSettings&>(initSettings) // or
dynamic_cast<OxygineInitiationSettings&>(initSettings)

or, if you need a pointer, perhaps this:

static_cast<OxygineInitiationSettings*>(&initSettings) // or
dynamic_cast<OxygineInitiationSettings*>(&initSettings)

If you know for certain that initSettings will refer to an OxygineInitiationSettings instance, you can and should use static_cast instead of dynamic_cast. If you aren't certain, you should either become certain or use dynamic_cast instead of static_cast. Note that the dynamic reference cast will raise a std::bad_cast exception, and the dynamic pointer cast will return a null pointer if the actual object referenced by initSettings isn't actually an OxygineInitiationSettings.

like image 99
Michael Urman Avatar answered Jan 03 '23 23:01

Michael Urman


Dynamic_cast can be performed from virtual class, that is class which have virtual methods. Simply adding dummy() method like that:

class IInitationSettings {
    virtual void dummy() {}
};

and changing from implicit to dynamic cast:

void GraphicsFactory::init(IInitationSettings& initSettings) {
    OxygineInitiationSettings settings =
        dynamic_cast<OxygineInitiationSettings&>(initSettings); //Does indeed work
}

would resolve problem.

like image 40
Mateusz Kacprzak Avatar answered Jan 04 '23 01:01

Mateusz Kacprzak