Reputation: 63
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
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'
Upvotes: 4