3

I am currently programming something where I hit this problem:

I have a Scene class and a MainScene class:

class Scene{
    Scene();
}

class MainScene : public Scene{
    MainScene();
}

And what I want to do is keep track of a list of scenes like this:

std::map<std::string, Scene*> scenes;

And I add a scene to it like this:

MainScene* mainscene; //I do not make it a new because i want my scenemanager to handle that
scenes.emplace("mainscene", mainscene); // add it to the list

and I have a function like this:

template <class T>
void SceneManager::recreateScene(T* &scene)
{
    scene = new T();
}

So that when I want to use a function loadscene, I can grab the scene from the list and delete the current scene and create the new scene with the function recreateScene. But the map gives me Scene. So when I use recreateScene it calls the constructor of Scene() instead of MainScene(). But I need it to know that the scene in the list is a MainScene so it creates a new MainScene() instead of a new Scene().

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • Sounds like you need to have a virtual function in your Scene class and override it in every 'extending' class. – Zach P Oct 26 '16 at 13:24
  • Mixing templates and runtime-polymorphism is always hard. While polymorphism has the intention of "forgetting" the special type templates need them. – Hayt Oct 26 '16 at 13:26

4 Answers4

4

One way to do that is to store the creator alongside the pointer. Something like this:

std::map<std::string, std::pair<Scene*, std::function<Scene*()>> scenes;

scenes.emplace("mainscene", {nullptr, []() { return new MainScene(); }});

Then, modify recreateScene:

template <class T>
void SceneManager::recreateScene(Scene* &scene, std::function<Scene*()> creator)
{
  scene = creator();
}

// called as:
auto& s = scenes["mainscene"];
recreateScene(s.first, s.second);

Side note: If these pointers own the Scene object, they should not be raw pointers, but std::unique_ptr<Scene> instead. DTTO for the return type of the std::function.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
1

A simple and efficient way to answer your needs is to implement a "self-rebuild" function in the following way :

class Scene {
    virtual ~Scene();
    virtual void self_rebuild() = 0;
};

class MainScene : public Scene {
    ~MainScene();
    void self_rebuild() {
        this->~MainScene();    // destroy scene (without deallocation)
        new(this) MainScene(); // rebuild a MainScene object in place (without reallocation)
    }
};

The main advantage of this method is to cleanup/rebuild the object without deallocation/reallocation. It is perfectly safe, as long as the object is the most derived object of class MainScene.

Reference to the standard (3.8/7) :

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied, and
  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
  • the original object was a most derived object (1.8) of type T and the new object is a most derived object of type T (that is, they are not base class subobjects).
Community
  • 1
  • 1
shrike
  • 4,449
  • 2
  • 22
  • 38
0

How about a pure virtual function on your Scene, called "clone" which returns a Scene*? MainScene can then have something like Scene* clone() {return new MainScene(*this);}

UKMonkey
  • 6,941
  • 3
  • 21
  • 30
  • this only works though if there was a `Scene*` to clone in the first place. – Hayt Oct 26 '16 at 13:24
  • `"void SceneManager::recreateScene(T* &scene)"` The question states there is – UKMonkey Oct 26 '16 at 13:25
  • `Scene* scene = nullptr; recreateScene(scene)` could also be valid in the question. – Hayt Oct 26 '16 at 13:27
  • Sure - although that concept would break any design. It would be like saying "I want to clone a class but don't know what class" – UKMonkey Oct 26 '16 at 13:32
  • well that is what happens... storing an uninitialized `MainScene` pointer in a map of `Scene` pointers. I'm not saying your solution is bad. But with the information from what I see in the question this maybe has some limitations. – Hayt Oct 26 '16 at 13:33
0

I would suggest letting the objects handle their own creation in a virtual function. That way you won't need to track the type.

class Scene{
    Scene();
    virtual ~Scene();

    virtual Scene* ReCreate() = 0;
}

class MainScene : public Scene{
    MainScene();
    virtual ~MainScene();

    virtual Scene *ReCreate(){
        return new MainScene();
    }
}

template <class T>
void SceneManager::recreateScene(T* &scene){
    Scene *temp = scene;
    scene = scene->ReCreate();
    delete temp;
}
Xiver
  • 71
  • 1
  • 7