theVoidZ
theVoidZ

Reputation: 63

Initialize a static variable shared by only a group of objects C++

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.

Upvotes: 3

Views: 648

Answers (1)

m.s.
m.s.

Reputation: 16344

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

Upvotes: 4

Related Questions