Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Singleton - CordayUK & otros

Iniciado por ethernet, 27 de Abril de 2003, 05:32:27 PM

« anterior - próximo »

ethernet



    Este es un código de la semana diferente, CordayUK me envio el codigo que pondre a continuacion como ejemplo de singleton, sin embargo leyendo un poco acerca de esa implemetacion en los foros de gamedev y otros sites no me parecio la adecuada por razones que espero podamos discutir en próximos días. Por ello he decidido postear otra implementación de singleton que se discutió en el irc y que espero podamos sacar conclusiones aquí también.

    Si teneis alguna implementación de Singleton que creais que podemos comentar o simplemente diferente a las planteadas no dudeis en postearlas.


    Esta es la implementación que me envió CordayUK


    /*

    CSingleton.h   (basado en el Game Programming Gems I)



    Define una clase singleton. Deriva tus clases a partir de esta y podras llamarlas desde cualquier punto de tu juego.



    ejemplo:



    en tu clase CJuego tienes un objeto CTimer, el cual esta implementado en CTimer.h asi:



      Class CTimer : public CSingleton <CTimer>



    desde cualquier otra parte de tu juego que incluya CTimer.h puedes llamar a CTimer::GetSingletonPtr () y obtener un

    puntero al Timer. Por supuesto solo puedes tener un objeto CTimer en todo el juego.



    */







    #pragma once

    #pragma warning( disable : 4311 4312 )   //4311,4312 : unsafe cast from int to class pointer





    #include <cassert>





    #define SINGLETON_ERROR_MSG   "Algo va mal con el singleton!! :("



    template <typename T> class CSingleton

    {



      //puntero al singleton

      static T* ms_ptr;                                    



    public:



      CSingleton (void)

      {

         assert (!ms_ptr && SINGLETON_ERROR_MSG);



         //averigua la direccion relativa de la instancia derivada

         spI offset = (spI)(T*)1-(spI)(spSingleton <T>*)(T*)1;



         //graba el resultado en el puntero del singleton

         ms_ptr = (T*)((int)this+offset);                    

      }



      virtual ~CSingleton (void)

      {

         assert(ms_ptr && SINGLETON_ERROR_MSG);

         ms_ptr = 0;

      }



      //devuelve la referencia al singleton

      inline static T& GetSingleton (void)

      {

         assert (ms_ptr && SINGLETON_ERROR_MSG);

         return (*ms_ptr);

      }



      inline static T* GetSingletonPtr (void)

      {

         spAssert (ms_ptr && SINGLETON_ERROR_MSG);

         return (ms_ptr);

      }

    };



    //inicializa el puntero a nulo

    template <typename T> T* CSingleton <T>::ms_ptr = 0;





    Esta es la implementación que yo propongo





    template <class T> class Singleton : public T

    {

    private:



        Singleton() {     }

        Singleton(const Singleton& fact) {     }

        Singleton& operator = (const Singleton& fact) {     }

    public:

        ~Singleton() {     }

    public:

        static Singleton& Instance() {

            static Singleton s_instance;

            return s_instance;

        }

    };





    Como veis el destructor esta como public, teóricamente se debería poner como private pero  para mantener la implementación original , que esta así debido a un bug del msvc++, he decidido no cambiarla.

    Espero que el autor no se me enfade por postearla sin su permiso ;P


    Zaelsius

    Yo también leí el thread de FlipCode hace tiempo, cuando estaba buscando una correcta implementación del estereotipo . El código que yo uso es prácticamente idéntico al de Ethernet, con las siguientes diferencias:

    1º) No derivo de T la clase Singleton.(¿Cómo influye ésto?)

    2º) Declaro (static T* m_pInstance;) como miembro de la clase,
      y en Instance() tengo


    if(!m_pInstance)m_pInstance = new T;

      return m_pInstance;


       Que es lo mismo que hace Ethernet con



     // Crea la instancia de clase al ser requerida por primera vez

    static Singleton s_instance;



    3º) Tambien tengo una pequeña macro que llamo desde los .CPP de la clase


    #define DECLARE_AS_SINGLETON(T) T* T::m_pInstance=NULL



       Luego hago DECLARE_AS_SINGLETON(CTimer); p.ej.

    El 3º punto es consecuencia directa del 2º. Con la implementación que propone ethi no hace falta...

    Por cierto, el constructor de CordayUK me parece demasiado hardcore... ¿Funciona/deberia funcionar en cualquier compilador de C?[/code]

    ethernet

    Umh, cuidado, porq declarar un static T *instace; en la clase no es lo mismo q tener un static en un metodo. Fijate q si tienes un static como miembro lo tienes q declarar y lo tienes q hacer en un cpp con la putada q conlleva ese tema unido a templates. Con la implementacion que yo he posteado (q no es mia) no tienes q preocuparte de ese problema.

    Por lo demas estoy pensando el porq de derivar de la clase q queremos singletonizar (xDD parece sincrorzzrzzrr)

    saludos

    ethernet

    ah, ya veo, parece ser q es para llamar al destructor (de la base claro :)una vez terminada la ejecucion, asi no tienes q preocuparte de hacer un delete con el singleton q propone zaelsius. Eso es lo unico q se me ocurre

    saludos

    ethernet

    por cierto:


      #define DECLARE_AS_SINGLETON(T) T* T::m_pInstance=NULL


    no seria ? :


    #define DECLARE_AS_SINGLETON(T) T* Singleton<T>::m_pInstance=NULL


    CoLSoN2

                                   
    CitarPor cierto, el constructor de CordayUK me parece demasiado hardcore...
    yo ni lo entiendo xDDDD

    lo de ::instance() { static singleton inst; return inst; } me parece mejor que usar un miembro static.

    yo también uso unas macros de este rollo:

    #define singleton Singleton::getInstance()

    evidentemente para la clase base no sirve de nada, pero si para algo tipo game, npcManager, sceneManager, etc.                                
    Manuel F. Lara
    Descargar juegos indie  - blog sobre juegos indie y casual
    El Desarrollo Personal.com  - blog sobre productividad, motivación y espíritu emprendedor

    Zaelsius

    Tengo un par de problemillas, y no sé si es cosa mia:

    - El compilador(VS.NET) se queja de que hay que definir T antes de Singleton, pero se supone que es al revés ¿?
    Vamos, que no me deja derivar Singleton de T.

    - Si tengo, una clase p.ej. Singleton, no veo manera de llamar a funciones miembro no estáticas de MyClass.
    Sólo puedo llamar a funciones declaradas static...el caso es que con mi Singleton yo hacía algo parecido a esto MyClass::Instance()->Foo() y ahora no puedorrl!!
    Esto último es sin poder derivar Singleton de MyClass(¿está relacionado?, o Singleton Sux.. :-D)

    Bueno, propongo a los del ISO C++ que incorporen al lenguaje una puñetera plantilla SIngleton estándar...
    #include rulez!! :-D

    Tambien he investigado un poco por ahí y he llegado a ver implementaciones académicas de Singleton de 3 páginas...haciendo movidas impresionantes

    ethernet

    XD, yo no he probado el codigo, pero se de buena fuente q funciona. No deberia fiarme pero bah! xD.

    zaelsius, donde has visto esas implementaciones de singleton?

    A mi no me gusta usar singletones, prefiero usar un pattern façade si no quiero emborronar mucho el codigo y lo de tener solo una instacia ya me las apaño yo xD

    saludos

    Grugnorr

                                    Esa implementación del Singleton es la de Bilas en el Gems1.

    Aquí tenéis el link dentro de su web:
    http://www.drizzle.com/~scottb/publish/gpg...1_singleton.htm

    La movida del constructor está *explicada* aquí:

    Citar
    So how does this work? All of the important work is done in the Singleton constructor, where it figures out the relative address of the derived instance and stores the result in the singleton pointer (ms_Singleton). Note that the derived class could be deriving from more than just the Singleton, in which case "this" from MyClass might be different from the Singleton "this". The solution is to take a nonexistent object sitting at address 0x1 in memory, cast it to both types, and see what the difference is. This will effectively be the distance between Singleton and its derived type MyClass, which it can use to calculate the singleton pointer.

    Cada implementación de un singleton tiene algún defecto, el Singleton del Design Patterns no llama al destructor, así que aunque la memoria se libere por el SO, las demás cosas que debieran ser liberadas no lo son :S. Éste singleton es...lioso y no sé si tenía algún fallo más.

    El libro Modern C++ Design tiene un capítulo dedicado a Singletons, del que se deduce que no existe una implementación óptima de entre las que presenta, pero hay algunas que yo consideraría lo suficientemente general y efectiva, registrando la función que libera recursos con atexit()


    PD: El Singleton que hice yo a partir del Phoenix Singleton del Modern desapareció con algún formateado....no lo encuentro :(                                
    hat the hells!

    Zaelsius

                                    Puede que sean cosas mias...XD

    Las implementaciones las ví buscando en Google. Eran "papers" de profesores de universidades...

    Edit: He encontrado uno de los documentos que me dejó traspuesto:http://www.cs.technion.ac.il/~gabr/papers/...ngleton_cuj.pdf

    Correis peligro de volveros locos si llegais a la última página  :X9:                                

    CordayUK

                                    exacto como dice Grugnorr, el singleton que yo mande esta sacado del game gems I (ademas lo pone en el codigo comentado). el motivo de calcular la posicion del puntero asi es simplemente por si tu objeto singleton deriva de alguna otra clase ademas de la plantilla singleton. pero no es totalmente necesario, podeis suprimir esos pasos y ya esta :)                                

    O2

     Me pasa lo mismo que comenta Zaelsius:

    VS.NET no me deja hacer:

    template<typename Type>
    class CSingleton : public Type {


    Me dice que hay que definir Type primero.

    He tenido que recurrir a dejar los constructores y destructores de "Type" (en este caso CWindow) como publicos, pues sino CSingleton no puede llamar a ellos y no puede hacer el new apropiado.

    Alguna sugerencia?






    Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
    Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
    Muchísimas gracias.