C++ singleton design: using inheritance to call only some implemented methods
I have a singleton that is the main engine container for my game.
I have several abstract classes that make the developer implement the different calls to what that specific object needs.What I want to do is to make my singleton call those methods upon each given object, but avoiding unecessary calls.
Example so you can understand better:
Imagine one object that requires both Render() and Update() methods.class IRender() : public IBase
{
virtual bool Render() = 0;
};
class IUpdate() : public IBase
{
virtual bool Update( long time_delta ) = 0;
};
class Sprite : public IRender, public IUpdate
{
bool Render(){ render stuff; }
bool Update( long time_delta(){ update stuff; }
};
Now I want to add that Sprite object to my singleton engine but I want the engine to call each loop to ONLY whatever that object inherited (there are other things beside Render and Update, like checking for input, etc):
_engine::getInstance()->Add( _sprite );
Thing is, for this to work I have to inherit all the interfaces from a base interface so I can call Add() with whatever objects were created, so the Add() method receives a base interface object.
Now the problem here is the base interface has to at least abstract all the methods that can be inherited like Render(), Update(), CheckInput(), Etc()开发者_如何学编程 and each loop my singleton has to call all the possibilities for every object, even if CheckInput() is empty for the sprite class.
Like so in my singleton:bool loop(){
for every object saved in my container:
CheckInput(); Update(); Render(); etc(); }
Is my design wrong? Is there a way I can avoid this?
I hope you understand what I mean, my engine is in a pretty advanced state and I'm trying to avoid rewriting it all.Thanks in advance.
If you don't need to call the chain CheckInput(), Update(), .... on each object before moving on to the next one, make a call to to the object, and make the object register which actions it supports on the singleton.
class Singleton {
void Add(Base& object) {
object.register(this);
}
void registerForInput(IInput& object) {
addToInputList(object);
}
}
class Some : public Base, public IInput {
void register(Singleton *target) {
target->registerForInput(*this);
}
}
However if you need to call all of them on one object before moving to the next make a method on the object which performs all the calls it supports.
class Singleton {
void loop() {
for all objects:
object.performActions();
}
}
class Some : public Base {
void performActions() {
checkForInput();
render();
}
}
It sounds like early optimization to me - are you sure that you really need to avoid those calls? Typically the overhead for an empty Render() call is going to be so small compared to the work of actually pushing the pixels onto the screen that it's not worth optimizing.
However - if you really want to avoid those calls you could do something like this:
class IBase
{
unsigned int mask;
const unsigned int DONT_CALL_MYFUNCTION = 1;
IBase() : mask(0) { }
virtual void MyFunction() { mask |= DONT_CALL_MYFUNCTION; }
}
...
if((obj->mask & DONT_CALL_MYFUNCTION) == 0)
obj->MyFunction();
You end up calling each base function once - but once the base implementation has realized that it's not overridden then it will stop calling the virtual function.
But again - I doubt it's worth the effort.
Pushing all the methods from the sub-classes to the base-class seems odd to say the least. Why not attempt to dynamic_cast each object to the interface with the method you want to call - if the cast returns a nullptr then skip it:
bool loop()
{
for every object saved in my conatiner:
IRender* render = dynamic_cast<IRender*> (object_ptr);
if (render != 0)
render->Render ();
// etc.
}
Alternatively call all of the actions from a single method in the sub-class - e.g. execute(). You would probably need to use virtual inheritance for your sub-classes to have only a single IBase base.
If you want to write something like
bool loop()
{
foreach IBase _base in _container do
{
_base->CheckInput();
_base->Update();
_base->Render();
...
}
}
all your subclasses must have an implementation of each of the methods. If you don't want the final class (i.e. Sprite
) to implement all the methods, you can implement that methods empty in the subclasses and then use virtual inheritance. But it will get a bit complex. Something like:
bool loop()
{
foreach IBase _base in _container do
{
IChecker * _checker = dynamic_cast<IChecker *>(_base);
if (_checker != 0)
_checker->CheckInput();
IUpdater * _updater = dynamic_cast<IUpdater *>(_base);
if (_updater != 0)
_updater->Update();
IRender * _render = dynamic_cast<IRender *>(_base);
if (_render != 0)
_render->Render();
...
}
}
class IBase
{
virtual bool Render() = 0;
virtual bool CheckInput() = 0;
virtual bool Update() = 0;
virtual bool Render() = 0;
...
}
class IRender : public virtual IBase
{
virtual bool Render()
{
// do stuff
}
virtual bool CheckInput()
{
return false;
}
...
}
It is complex and you repeat code for nothing. Also, I don't know exactly the performance penalty for dynamic_cast
, but there must be. I hope you can use this idea to save your code.
Dirty solution, to begin with :)
Just add a 'tag' to the interfaces. Obviously they are already tied together anyway, so:
class IBase { public: void addTag(Tag t); const TagLists& getTags() const; }
IRender::IRender() : IBase() { addTag(IRender::Tag); }
Then you can just ask for the 'tags' and dispatch with a switch:
const TagList& tags = base->getTags();
for(TagList::const_iterator it = tags.begin(), end = tags.end(); it != end; ++it)
{
switch(*it)
{
case IRender::Tag:
base->render();
break;
case IUpdate::Tag;
base->update();
break;
}
}
Well, as I said, beautiful isn't it ?
The only question I have is: do you really need to do that.
Obviously it's kind of awkward... I mean with all those methods hanging around...
The only question is how you want to proceed:
- For each object, call all the methods in a definite order
- For each method, call it upon all the objects
Object centric
I don't have any idea how to do it properly: the go
method that summarizes them all would end up with a LOT of parameters :/
Then you would need masks, or registrations...
Method centric
One singleton to contain the objects (if it's that necessary), one singleton RenderSingleton
which holds a pointer to each object derived from Render
.
class Render
{
public:
Render() : Base() { RenderSingleton::Add(this); }
~Render() { RenderSingleton::Remove(this); }
};
class RenderSingleton
{
public:
static void Add(Render* i);
static void Remove(Render* i);
static void Render(....); // calls on each obj with specific render parameters
};
The main advantage here is that the parameters are specific to the render
method, no need to pass parameters for check
etc... which promotes decoupling.
Also, note that if you add a new interface, it's independent of the others.
Go for this if you can.
What I usually do is define a listener object with a templated subclass that does dynamic_cast
and passes it on to the "updater", this is then called when object's of the right type are added/removed.
class ObjectListenerBase
{
private:
friend class ObjectManager;
void onAdded(ScriptObject* obj) { doOnAddedBase(obj); }
void onRemoved(ScriptObject* obj) { doOnRemovedBase(obj); }
virtual void doOnAddedBase(ScriptObject* obj) = 0;
virtual void doOnRemovedBase(ScriptObject* obj) = 0;
};
template<class T>
class ObjectListener : public ObjectListenerBase
{
protected:
virtual void doOnAdded(T* obj) = 0;
virtual void doOnRemoved(T* obj) = 0;
private:
void doOnAddedBase(ScriptObject* obj) { if (T* o = dynamic_cast<T*>(obj)) { doOnAdded(o); } }
void doOnRemovedBase(ScriptObject* obj) { if (T* o = dynamic_cast<T*>(obj)) { doOnRemoved(o); } }
};
//// how add object might look like
void ObjectManager::addObject(const std::string& object_name, ScriptObjectPtr s_object)
{
....
for_each(m_listeners, boost::bind(&ObjectListenerBase::onAdded, _1, s_object.get()));
}
//// how a listener might look like
class AIEngine : public ObjectListener<IThinker>
{
public:
AIEngine() { ObjectManager::the().addListener(this); }
void think() { for_each(m_ais, boost::bind(&IThinker::think, _1, ...));
protected:
virtual void doOnAdded(IThinker* obj) { m_ais.push_back(obj) }
virtual void doOnRemoved(IThinker* obj) { m_ais.erase(obj); }
private:
ptr_set<IThinker> m_ais;
}
You can inherit ObjectListener several times for different types if you'd like.
If you have your base interface define too many pure virtual function, every derived class would have to define them.
I would make single pure virtual function in IBase
, lets call it EngineCommunication
that in derived classes would call CheckInput(); Update(); Render() etc.
(class specific)
And your engine would call EngineCommunication
in the loop and let concrete classes decide what to do.
Evolve your hierarchy, use component-based game object system. It was proposed by Scott Bilas in 2002 and implements one of the first OOP principles: prefer composition over inheritance. This approach is very popular nowadays.
精彩评论