3

I was wondering if is it possible to create a class data member shared only by a group of objects of that same class.

I have a class called Scene, and another called GameObject, The Scene creates GameObjects, and each GameObject created must have a reference to the scene it creates it.

I could achieve this by just declaring:

class GameObject
{
    public:
        Scene* scene;
}

And put whenever a scene creates a gameobject.

void Scene::add_game_object(){
    GameObject* gameobject = new GameObject();
    gameobject->scene = this;
}

But, this would definitely take a lot of memory.

I was thinking about a solution, ( Currently not compiling but, may be we could shape something from that )

class GameObject
{
    public:
        template< Scene* S >
        Scene* get_scene();
}
//
template< Scene* S >
Scene* GameObject::get_scene(){
    static Scene* sc = nullptr;
    if( sc == nullptr ){
        sc = S;
    }
    return sc;
}

void Scene::add_game_object(){
    GameObject* gameobject = new GameObject();
    // link scene and gameobject.
    gameobject->get_scene(this);
}

Using it :

gameobject->get_scene<nullptr>();

Thank you. Cordially.

theVoidZ
  • 53
  • 7
  • How about having the Scene pointer as a static member of the GameObject class? – Vaughn Cato Aug 21 '16 at 21:43
  • In fact, i can have Several scenes at the same time, and each game object can point to a different but only one scene – theVoidZ Aug 21 '16 at 21:44
  • 1
    In your game object, you could have an index into an array of scenes. Using only one byte of memory per game object, that would allow 256 scenes. – Vaughn Cato Aug 21 '16 at 21:47
  • I don't see how the solution you presented is any different from having a static member. Isn't there is still only one `Scene*` for *all* your objects? – Galik Aug 21 '16 at 21:49
  • @VaughnCato Quite simple, i will consider your solution. – theVoidZ Aug 21 '16 at 21:53
  • @Galik It is one Scene but not the same for all of them. Each group of gameobject would have their own (shared variable) Scene. – theVoidZ Aug 21 '16 at 21:53
  • Have you thought about object removal? And 4 or 8 bytes (`sizeof(GameObject*)`) of memory is not that much. – Vyacheslav Napadovsky Aug 21 '16 at 22:08
  • @slavanap Definitely, The scene manages this, However, several scenes are active simultaneously and can hold up to hundreds of gameobjects ( scene graph ). – theVoidZ Aug 21 '16 at 22:51
  • 2
    @Vaughn Cato : your idea is good but I fear that the requested byte may take more than 1 byte of memory. I have no experience of "packed" class ( if possible ) like we used packed struct in plain old C. If for any alignment reason the byte takes 4 or 8 bytes of memory it is not cheaper than a direct pointer to a Scene – NGI Aug 22 '16 at 06:08
  • @theVoidZ: it is not the topic of your question but I think your data member scene should maybe more be private and the "this" pointer of the Scene creating the GameObject be passed as a parameter of the GameObject constructor – NGI Aug 22 '16 at 06:17

1 Answers1

4

You can store the pointer to the Scene object in a static variable of GameObject even if you have multiple Scene objects.

The idea is to have a separate types for each Scene object as well as separate types for their GameObject. This allows to have a separate static variable for each Scene object where the pointer can be stored.

The following code implements this idea. It requires that the maximum number of Scenes is set at compile time in order to populate the function pointer table:

#include <cassert>
#include <array>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

// forward declaration
template <std::size_t N>
struct Scene;

template <std::size_t N>
struct GameObject
{
    GameObject()
    {
        std::cout << "created GameObject<" << N << "> which belongs to '" << scene->name << "'"<< std::endl;
    }
    static Scene<N>* scene;
};

template <std::size_t N>
Scene<N>* GameObject<N>::scene;

struct SceneBase
{
    virtual ~SceneBase() = default;
    virtual void addGameObject() = 0;
};

template <std::size_t N>
struct Scene : SceneBase
{
    using GO = GameObject<N>;
    Scene(const std::string& name) : SceneBase(), name(name)
    {
        GO::scene = this;
    }

    void addGameObject() override
    {
        gameObjects.push_back(std::make_unique<GO>());
    }

    std::vector<std::unique_ptr<GO>> gameObjects;
    std::string name;
};


template <std::size_t N>
struct SceneMaker
{
    static std::unique_ptr<SceneBase> make(const std::string& name)
    {
        std::cout << "making Scene<" << N <<"> with name '" << name << "'" << std::endl;
        return std::make_unique<Scene<N>>(name);
    }
};

using FunctionPtr = std::unique_ptr<SceneBase> (*)(const std::string&);

// based on http://stackoverflow.com/a/20408889/678093
template <std::size_t N, std::size_t... Rest>
struct FunctionTable
{
    static constexpr auto& value = FunctionTable<N - 1, N, Rest...>::value;
};

template <std::size_t... Rest>
struct FunctionTable<0, Rest...>
{
    static constexpr std::array<FunctionPtr,sizeof...(Rest)+1> value = {SceneMaker<0>::make,SceneMaker<Rest>::make...};
};

template <std::size_t... Rest>
constexpr std::array<FunctionPtr, sizeof...(Rest)+1> FunctionTable<0, Rest...>::value;


struct SceneFactory
{
    auto makeScene(const std::string& name)
    {
        assert(sceneIndex < maxScenes);
        return fTable[sceneIndex++](name);
    }

    static constexpr std::size_t maxScenes = 10;
    static constexpr auto& fTable = FunctionTable<maxScenes>::value;

    std::size_t sceneIndex = 0;
};


int main()
{
    SceneFactory factory;
    auto scene1 = factory.makeScene("first scene");
    scene1->addGameObject();
    scene1->addGameObject();
    auto scene2 = factory.makeScene("second scene");
    scene2->addGameObject();
    scene2->addGameObject();
}

output:

making Scene<0> with name 'first scene'
created GameObject<0> which belongs to 'first scene'
created GameObject<0> which belongs to 'first scene'
making Scene<1> with name 'second scene'
created GameObject<1> which belongs to 'second scene'
created GameObject<1> which belongs to 'second scene'

live example

m.s.
  • 16,063
  • 7
  • 53
  • 88
  • Hello, It does perfectly fit to the problem, i like it. However, i will adapt it without the SceneFactory. Thanks a lot, thank you all. – theVoidZ Aug 22 '16 at 13:32