Newsgroup: it.comp.lang.c++
Titolo: template & static

Utilizzo il seguente codice per implementare classi con non più di una 
istanza.
/**
  * template for a class with only 0 or 1 instances
  */
template<class T>
class Singleton {
	private:
		static T *theSingleton;
	public:
		static T* getSingleton() {
			return theSingleton;
		}
 
		Singleton() {
			assert(theSingleton==0);
			theSingleton=static_cast<T*>(this);
		}
 
		~Singleton() {
			assert(theSingleton==static_cast<T*>(this));
			theSingleton=0;
		}
};
template<class T>
T* Singleton<T>::theSingleton=0;
/**
  * @return the pointer to the single instance or null if no instances
  */
template<class T>
T* the() {
	return Singleton<T>::getSingleton();
}
/**
  * assumes that the object has an instance and
  * @return a reference to the single instance
  */
template<class T>
T &theRef() {
	T* p=Singleton<T>::getSingleton();
	assert(p);
	return *p;
}
Il codice funziona bene. Se voglio una classe "singleton" faccio:
class My : public Singleton<My> {....};
E poi posso recuperare l'istanza della classe con:
	the<My>()
e
	theRef<My>()
Il problema è che se X è una classe che NON deriva da Singleton, e faccio:
	the<X>()
il compilatore NON mi dà errore! Sembra che il puntatore X::theSingleton 
e la funzione statica X::getSingleton vengano implementati comunque! E' 
normale questo? Come faccio ad evitarlo?
E.

 Come faccio ad evitarlo?
Non sono sicuro che tu possa, dal momento che nel tuo codice la
costruzione delle istanze di sottoclassi di Singleton non e'
incapsulata in nessun modo. Secondo me la tua classe Singleton ha tali
e tanti problemi che l'unica cosa buona che puoi fare e' sopprimerla
senza farla soffrire. Se proprio vuoi usare il pattern Singleton, ed
e' raramente una buona idea, ti conviene usare un'implementazione
canonica.
Se invece vuoi imporre l'esistenza di una singola istanza, e anche
questa non mi pare un'idea eccezionale, concentrati su quello. Ad
esempio:
template <typename T>
class Single {
  protected:
    Single() { assert(!built); }
    ~Single() {}
  private:
    Single(Single const &);
    Single & operator=(Single const &);
    static bool built;
};
template <typename T>
bool Single<T>::built = false;
Ciao,
Nicola

 istanza.
Non mi sembra che funzioni:
class A: public Singleton<A> {
public:
  void tell() {
      std::cout << "hello I'm an A: id = " << this->m_id << std::endl;
  }
  A(int id): m_id(id) { };
private:
  int m_id;
};
int main()
{
    A a(1);
    A b(2);
    A c(3);
    a.tell();
    b.tell();
    c.tell();
    return 0;
}

 Non mi sembra che funzioni:
A me il codice che hai proposto dà il seguente messaggio in runtime:
Assertion failed: theSingleton==0, file main.cpp, line 19
Questo è proprio quello che volevo ottenere. Se cerchi di istanziare più 
volte la classe A ottieni un errore in runtime.
E.


 bool Single<T>::built = false;
Non voglio solo assicurarmi che non vengano create piů istanze, ma 
voglio poter accedere all'eventuale (unica) istanza della classe.
E.

?manu* wrote:
 > ...
Ho risolto! Ho messo nella class Singleton<T> un typedef:
template<class T>
class Singleton {
	...
	typedef T ThisIsASingleton;
	...
}
che poi vado a controllare nelle funzioni the e theRef:
template<class T>
T* the() {
	typedef T::thisIsASingleton Check;
	return Singleton<T>::getSingleton();
}
Questo mi ha permesso di scovare un bug che mi perseguitava da qualche 
giorno.
E.

 Non ci metti built=true nel costruttore e built=false nel distruttore?
Hai ragione, e' una svista (il built nel distruttore ha senso solo se
si vuole che non esistano piu' istanze contemporaneamente, piuttosto
che ne venga costruita al piu' una).
Ciao,
Nicola

 come parametro esplicito.
In effetti è quello che avevo fatto inizialmente. Ma il codice era 
diventato veramente troppo complicato e ingestibile. Mi dovevo portare 
dietro tutti questi puntatori da una parte all'altra.
E.

 reference solamente per oggetti che non possono essere distrutti.
Io resto convinto del principio di restituire per reference solo
oggetti ricevuti come argomento per reference oppure *this. Nel caso
di vector la cosa "giusta" sarebbe restituire un'oggetto proxy che
consenta l'assegnamento mantenendo uno smart pointer al contenitore.
Mi rendo pero' conto che vector deve garantire prestazioni
paragonabili a quelle di un array nativo.
Ciao,
Nicola