The decision whether to use a monostate or a singleton depends on the problem domain.
You can find a detailed comparison of both here.
HTH,
Pat.
Game Design Help
I've decided to go with a singleton at this point. I'm pretty weak on C++ templates and I'm having a problem getting the code you posted to work with my current method of registering the callbacks in a Vector. Could you post some example code on how to get the callback template to work with a vector?
Ok well I'm at a loss even how to use the code you posted in my current class. Can anyone help?
Changes in the header:
Changes in the implementation:
Sample usage with "free functions":
Sample usage with class members:
HTH,
Pat.
class WindowManager{private: ... // pointers or references must be used, since polymorphism is at work here typedef std::pair<int, ResizeCallback *> ResizeCall; typedef std::vector<ResizeCall> CallbackVector; // the vector now holds a pair of <id, Functor> CallbackVector resizeVector; // find by id functor struct HasId { // ctor. HasId( int id ) : id( id ) { } int id; // function operator, allows a call like "HasId hasId(x); hasId( call );" bool operator( )( ResizeCall const & call ) const { return call.first == id; } }; ...};
Changes in the implementation:
// delete pointers hereWindowManager::~WindowManager(){ glfwTerminate(); for ( size_t i = 0; i < resizeVector.size(); ++i ) delete resizeVector.second; resizeVector.clear();}// note that the windowmanager takes ownership of the pointer and that// the resize functor must be created using "new"int WindowManager::registerResizeFunction(ResizeCallback * resizeFunctor){ ResizeCall newResizeCall; static id = 0; id++; newResizeCall.first = id; newResizeCall.second = resizeFunctor; resizeVector.push_back(newResizeCall); return id;}// find the documentation for "remove_if" in your STL docsvoid WindowManager::unregisterResizeFunction(int id){ // find the callback and remove it by moving it to the end CallbackVector::iterator iter = std::remove_if( resizeVector.begin( ), resizeVector.end( ), HasId( id ) ); // erase it (there can only be one) delete iter->second; resizeVector.erase( iter );}void GLFWCALL WindowManager::windowResize(int width, int height){ CallbackVector::const_iterator iter; wm_width = width; wm_height = height; // Call functions in the vector for (iter = resizeVector.begin(); iter != resizeVector.end(); ++iter) { iter->second.OnResize(width, height); }}
Sample usage with "free functions":
struct MyResizeFunctor : public ResizeCallback { void OnResize( int width, int height ) { // do something }};...WindowManager::getInstance()->registerResizeFunction( new MyResizeFunctor );
Sample usage with class members:
class MyClass { ... void Resized( int, int ) { ... } int resized;public: MyClass( ) { // register with the window manager resized = WindowManager::getInstance()->registerResizeFunction( new ResizeMemberCallback( this, Resized ) ); // if the above doesn't compile for some reason:// resized = WindowManager::getInstance()->registerResizeFunction( new ResizeMemberCallback<MyClass>( this, Resized ) ); } ~MyClass( ) { // unregister WindowManager::getInstance()->unregisterResizeFunction( resized ); }};
HTH,
Pat.
The code you posted doesn't seem to compile. Here are the errors I'm getting
error C2440: '=' : cannot convert from 'ResizeCallback *' to 'ResizeMemberCallback *'
error C2228: left of '.OnResize' must have class/struct/union type
error C2440: '=' : cannot convert from 'ResizeCallback *' to 'ResizeMemberCallback *'
int TestManager::registerResizeFunction(ResizeCallback * resizeFunctor){ ResizeCall newResizeCall; static id = 0; id++; newResizeCall.first = id;--> newResizeCall.second = resizeFunctor; resizeVector.push_back(newResizeCall); return id;}
error C2228: left of '.OnResize' must have class/struct/union type
void GLFWCALL TestManager::windowResize(int width, int height){ CallbackVector::const_iterator iter; // Call functions in the vector for (iter = resizeVector.begin(); iter != resizeVector.end(); ++iter) {--> iter->second.OnResize(width, height); }}
Quote:Original post by ChaosX2
I didn't mean that the way it sounded. So basically what I did was created a monostate singleton which is kind of redundant. So which is the preferred pattern, monostate or singleton? I'm thinking singleton.
I am filled with absolute loathing for the singleton pattern.
I sometimes use the monostate one.
That said, lots of (<opinion>crazy</opinion>) people use singletons. Enough that they're likely more popular. That dosn't necessarily mean best, though.
They both achieve the same basic thing (one set of data), with different "gotchyas" - the singleton pattern enforces a rule of 1 element, and thus is harder to expand when you suddenly need 2+ of something. On the other hand, two seemingly seperate objects sharing the same data (monostate pattern) can be an unexpected/confusing, bug causing gotchya.
Quote:Original post by ChaosX2
The code you posted doesn't seem to compile. Here are the errors I'm getting
error C2440: '=' : cannot convert from 'ResizeCallback *' to 'ResizeMemberCallback *'
*** Source Snippet Removed ***
Are you sure your vector used ResizeCallback* and not the ResizeMemberCallback*?
You have to use the base class for the vector, not the derived class.
Quote:
error C2228: left of '.OnResize' must have class/struct/union type
*** Source Snippet Removed ***
Typo, mea culpa [smile]
This should read "iter->second->OnResize(width, height);", since the second member of the pair is a pointer.
[edit]
I just hacked together a small test app and it works without problems.
Here's the source:
#include <algorithm>#include <cassert>#include <iostream>#include <string>#include <vector>// --------------------------------------------------------------------------------// CALLBACK STUFF// --------------------------------------------------------------------------------// base class, not really necessaryclass Callback {};// resize callbackclass ResizeCallback : public Callback{public: virtual void OnResize( int, int ) { }};// templated resize callback for non-static class memberstemplate<class Callee>class ResizeMemberCallback : public ResizeCallback{ typedef void (Callee::*MethodType)( int, int ); MethodType callback; Callee * callee;public: ResizeMemberCallback( Callee * c, MethodType method ) : callee( c ), callback( method ) { assert( c && method ); } void OnResize( int width, int height ) { // forward the call to the class instance (*callee.*callback)( width, height ); }};// --------------------------------------------------------------------------------// STRIPPED DOWN WINDOW MANAGER - JUST FOR TESTING CALLBACKS// --------------------------------------------------------------------------------class WindowManager{private: // pointers or references must be used, since polymorphism is at work here typedef std::pair<int, ResizeCallback *> ResizeCall; typedef std::vector<ResizeCall> CallbackVector; // the vector now holds a pair of <id, Functor> CallbackVector resizeVector; // find by id functor struct HasId { // ctor. HasId( int id ) : id( id ) { } int id; // function operator, allows a call like "HasId hasId(x); hasId( call );" bool operator( )( ResizeCall const & call ) const { return call.first == id; } }; // singleton stuff WindowManager() { } // hide assignment operator WindowManager const & operator = ( WindowManager const & );public: ~WindowManager(); // register callbacks int registerResizeFunction(ResizeCallback * resizeFunctor); // unregister callbacks void unregisterResizeFunction(int id); // invoke callbacks void windowResize(int width, int height); // singleton stuff static WindowManager * getInstance( ) { static WindowManager instance;; return &instance; }};// delete pointers hereWindowManager::~WindowManager(){ std::clog << "Deleting " << resizeVector.size() << " windowResize callback(s)\n"; for ( size_t i = 0; i < resizeVector.size(); ++i ) delete resizeVector.second; resizeVector.clear();}// note that the windowmanager takes ownership of the pointer and that// the resize functor must be created using "new"int WindowManager::registerResizeFunction(ResizeCallback * resizeFunctor){ ResizeCall newResizeCall; static id = 0; id++; newResizeCall.first = id; newResizeCall.second = resizeFunctor; resizeVector.push_back(newResizeCall); return id;}// find the documentation for "remove_if" in your STL docsvoid WindowManager::unregisterResizeFunction(int id){ // find the callback and remove it by moving it to the end CallbackVector::iterator iter = std::find_if( resizeVector.begin( ), resizeVector.end( ), HasId( id ) ); // erase it (there can only be one) if ( iter != resizeVector.end( ) ) { std::clog << id << " has been removed\n"; delete iter->second; std::swap( resizeVector.back( ), *iter ); resizeVector.pop_back( ); } else std::clog << id << " was not found\n";}// invoke callbacksvoid WindowManager::windowResize(int width, int height){ CallbackVector::const_iterator iter; std::clog << "Invoking " << resizeVector.size() << " windowResize callback(s)\n"; // Call functions in the vector for (iter = resizeVector.begin(); iter != resizeVector.end(); ++iter) { iter->second->OnResize(width, height); }}// wrapped free functionstruct MyResizeFunctor : public ResizeCallback { void OnResize( int width, int height ) { std::clog << "MyResizeFunctor: " << width << "x" << height << "\n"; }};// --------------------------------------------------------------------------------// CALLBACK EXPAMPLES// --------------------------------------------------------------------------------// class with self-registering callbackclass MyClass { void Resized( int w, int h ) { std::clog << "MyClass: " << w << "x" << h << "\n"; } int resized;public: MyClass( ) { resized = WindowManager::getInstance()->registerResizeFunction( new ResizeMemberCallback<MyClass>( this, Resized ) ); } ~MyClass( ) { // unregister WindowManager::getInstance()->unregisterResizeFunction( resized ); }};// --------------------------------------------------------------------------------// DRIVER PROGRAM // --------------------------------------------------------------------------------int main(){ // test self-registering MyClass clazz; // test dynamic register/unregister MyClass * dynamic = new MyClass; // add "free function" WindowManager::getInstance()->registerResizeFunction( new MyResizeFunctor ); // invoke WindowManager::getInstance()->windowResize( 10, 120 ); // unregister delete dynamic; // invoke WindowManager::getInstance()->windowResize( 50, 5 ); // "clazz" should remove itself upon destruction return 0;}
Rip it apart and integrate what you need - it's a gift for you [smile]
Merry christmas!
[/edit]
[Edited by - darookie on December 23, 2005 9:45:18 AM]
Quote:Original post by darookie
Are you sure your vector used ResizeCallback* and not the ResizeMemberCallback*?
You have to use the base class for the vector, not the derived class.
D'oh...
Quote:Original post by darookie
Typo, mea culpa [smile]
This should read "iter->second->OnResize(width, height);", since the second member of the pair is a pointer.
I should have caught that one...
Quote:Original post by darookie
I just hacked together a small test app and it works without problems.
Here's the source:
*** Source Snippet Removed ***
Rip it apart and integrate what you need - it's a gift for you [smile]
Merry christmas!
Thanks so much...
darookie your code works fine on windows but when I compile it on Mac OS X (Xcode) I recieve the following error:
Is this standard C++?
Quote:error: no matching function for call to 'ResizeMemberCallback<MyClass>::ResizeMemberCallback(MyClass* const, <unknown type>)'
note: candidates are: ResizeMemberCallback<T>::ResizeMemberCallback(T*, void (T::*)(int, int)) [with T = MyClass]
Is this standard C++?
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement