Newsgroup: it.comp.lang.c++
Titolo: variabili membro template

Come è noto non è possibile avere variabili membro template
in una classe:
class foo
{
    template<class T> val;
};
tuttavia è facile 'imitarli':
#include <iostream>
struct foo
{
   template<class T> T& val()
   {
      static T rep;
      return rep;
   }
};
int main()
{
   foo f;
   f.val<double>() = 5.67;
   std::cout << f.val<double>() << '\n';
}
Da questa constatazione (che sicuramente sarà nota, o no?), si
possono immaginare molte tecniche interessanti.
La prima applicazione di queste 'pseudo' variabili membro
template è una classe che può contenere *tutto*:
#include <utility>   // per pair
#include <typeinfo>  // per bad_cast
class Any
{
   private:
      //
      // richiede che T abbia un costruttore di default
      //
      template<class T> std::pair<T, bool>& val() const
      {
         // inizializzato con T(), false.
         static std::pair<T, bool> rep;
         return rep;
      }
      Any(const Any&);
      Any& operator=(const Any);
   public:
      Any() {  }
      template<class T> Any(const T& obj)
      {
         val<T>() = std::make_pair(obj, true);
      }
      template<class T> Any& operator=(const T& obj)
      {
         val<T>() = std::make_pair(obj, true);
         return *this;
      }
      template<class T> bool has_type() const
      {
         return val<T>().second;
      }
      template<class T> operator const T&() const
      {
         if(!val<T>().second) throw std::bad_cast();
         return val<T>().first;
      }
};
#include <iostream>
int main()
{
   Any any;
   any = 7;
   any = 2.3f;
   std::cout << any.has_type<int>() << '\n';
   std::cout << any.has_type<float>() << '\n';
   int k = any;
   std::cout << k << '\n';
}
Come si vede, Any non funziona proprio come l'equivalente
in Java, credo che una classe di questo si possa chiamare:
"multipolimorfica a compile-time".
Infatti può contenere più tipi diversi contemporaneamente.
Qualcuno sa se potrebbe essere utile a qualcosa?
Un'altro modo in cui Any può essere visto è come un
contenitore di tipi.
Questa idea mi ha portato a definire una classe che riporto
in reply a questo messaggio.
Manlio Perillo

 template è una classe che può contenere *tutto*:
ma scusa, la classe Object che ti avevo postato qualche thread più
sotto faceva proprio questo... hai provato a darci un'occhiata?

 ha scritto nel messaggio
news:RmWQ5.71803$BF.25...@news.infostrada.it...

Certo che ci ho dato una occhiata, era interessante.
Ma la tua Object può contenere un *solo* tipo di oggetto
alla volta, inoltre usava il polimorfismo (dynamic_cast).
La mia Any lavora a compile-time.
E' poi ho postato l'esempio perchè pensavo potesse essere
interessante.
Manlio Perillo


Ecco un'altra applicazione che spero sia interessante.
//
// richiede che mapped_type abbia un costruttore di default
//
template<class mapped_type> class type_map
{
   private:
      type_map(const type_map&);
      type_map& operator=(const type_map&);
   public:
      type_map() {  }
      template<class key_type>
      mapped_type& operator()(const key_type&) const
      {
         static mapped_type value;
         return value;
      }
};
type_map è *come* una mappa.
Ecco come può essere usata:
#include <string>
#include "type_map.hpp"
// variabile globale
const type_map<std::string> type_name;
// macro per registrare i nomi dei tipi
// usa un trucco per evitare di sprecare memoria
#define REGISTER(tipo)       \
{                            \
   tipo tmp;                 \
   type_name(tmp) = # tipo;  \
}
Ossia abbiamo una CTTI (Compile Time Type Information).
Ecco un esempio:
#include <iostream>
int main()
{
   REGISTER(int);
   REGISTER(int*);
   // non è applicabile alle referenze
   // REGISTER(int&);
   int k;
   int& r = k;
   std::cout << type_name(k) << '\n';
   std::cout << type_name(r) << '\n';
   int* p = &k;
   std::cout << type_name(p) << '\n';
}
Mi piacerebbe sapere se questa idea è già venuta a
qualcuno.
Per concludere mi è venuto in mente anche un modo per utilizzare
Any.
Lo posto in reply a questo messaggio.
Manlio Perillo


Ecco qui:
L'idea č di definire un' oggetto che permette di
instanziare una classe conoscendone il nome
(l'idea mi č venuta leggendo C++ Informer #2).
#include <map>
#include <string>
#include "any.hpp"
#include "type_map.hpp"
class Factory
{
   private:
      typedef std::map<std::string, Any*> map_type;
      std::map<std::string, Any*> map_;
      type_map<std::string> type_name_;
   public:
      template<class Type>
      void registration(const std::string& name)
      {
         Any* obj = new Any;
         Type tmp;
         *obj = tmp;
         map_[name] = obj;
         type_name_(tmp) = name;
      }
      bool unregistration(const std::string& name)
      {
         map_type::iterator found = map_.find(name);
         if(found == map_.end()) return false;
         delete found->second;
         map_.erase(found);
         return true;
      }
      const Any& class_by_name(const std::string& name)
      {
         map_type::iterator found = map_.find(name);
         if(found == map_.end()) throw std::bad_cast();
         return *(found->second);
      }
      template<class Type>
      std::string name_by_class(const Type& type)
      {
         return type_name_(type);
      }
};
Esempio:
#include <iostream>
int main()
{
   Factory factor;
   std::string name = "int";
   factor.registration<int>(name);
   const Any& obj = factor.class_by_name(name);
   int k = obj;
   std::cout << factor.unregistration(name) << '\n';
   std::cout << factor.name_by_class(k) << '\n';
}
Come si vede, il comportamento č un poco particolare.
Infatti con class_by_name non viene creata una nuova
istanza, ma viene restituito una const reference sempre
allo stesso oggetto.
Inoltre probabilmente ci sono delle cose da sistemare.
Vorrei porre l'attenzione su std::map<std::string, Any*>.
Questo container č *come se* contenesse tipi diversi.
Questa credo sia una cosa molto interessante, perchč
si puň usare questo 'trucco' come alternativa
al polimorfismo ed alle classi derivate.
Comunque presenta dei problemi, ci devo pensare ancora
un po su.
Infine approfitto di questo mio post per salutare e
ringraziare tutto il newsgroup.
Infatti dalla settimana prossima credo non potrň piů
postare per un bel po di tempo.
Saluti
Manlio Perillo

 };
Ho un dubbio:
foo f;
foo g;
f.val<double>( ) perche' dovrebbe essere diversa da
g.val<double>( ) ?
In effetti per tutte e due il tipo di funzione e'
double& foo::val<double>( void ); 
per cui ogni buon compilatore non dovrebbe definire funzioni diverse.
Difatti 
ghigo@sphera:/tmp$ cat t1.cc
#include <iostream>
struct foo {
        template <class T> T & val( ){ static T v; return v; } 
};
 
int main( ){
 
        foo     f,g;
 
        f.val<double>( ) = 5.0;
        cout << "f.val<double>( ) = " << f.val<double>( ) << endl;
 
        g.val<double>( ) = 3.0;
        cout << "f.val<double>( ) = " << f.val<double>( ) << endl;
        cout << "g.val<double>( ) = " << g.val<double>( ) << endl;
 
/*        cout << " f.val<double> = " << f.val<double>
             <<"; g.val<double> = " << g.val<double>
             <<endl;
*/
 
}
ghigo@sphera:/tmp$ g++ -Wall -o t1 t1.cc
ghigo@sphera:/tmp$ ./t1
f.val<double>( ) = 5
f.val<double>( ) = 3
g.val<double>( ) = 3
ghigo@sphera:/tmp$ 
ghigo@sphera:/tmp$ gcc -v
Reading specs from /usr/lib/gcc-lib/i386-linux/2.95.2/specs
gcc version 2.95.2 20000220 (Debian GNU/Linux)                              
 
P.S. come si fa ad ottenere l'indirizzo di una funzione membro template ?
se ho
struct G { void g( ); }
l'indirizzo di G::g( ) e' G::g.
Ma se ho
struct F { template <class T > void f( ); }
l'indirizzo di F::f<double>( )  quale e' ? il gcc mi da errore se provo con
F::f<double> ( eppure la simbologia mi pare logica, ed univoca )

 F::f<double> ( eppure la simbologia mi pare logica, ed univoca )
Dovrebbe essere &F::f<double> ; tieni pero' presente che con i template
le cose si complicano. L'errore che ottieni e' in compilazione o in
link?
Ciao,
Nicola

Nicola Musatti wrote:

Ho capito dove sbagliavo: non e'
F::f<double>, ma F<double>::f
Comunque il gcc per le funzioni membro static mi accetta anche la sintassi 
senza la &; per quelle non static "vuole" ( in realta mi da' un warning )  
la &.
Comunque ho una perplessita':
ghigo@sphera:/tmp$ g++ -Wall -o prova prova.cc && ./prova
Indirizzo di X::f( ) 1
Indirizzo di &X::f( ) 1
Indirizzo di Y<int>::g( ) 1
Indirizzo di &Y<int>::g( ) 1
ghigo@sphera:/tmp$ cat ./prova.cc
#include <iostream>
 
struct  X {
        static void f( ){ }
};
 
template <class T>
struct Y {
        static  void G( ){ }
};
 
 
int main( ){
        cout << "Indirizzo di X::f( ) " << X::f << endl;
        cout << "Indirizzo di &X::f( ) " << & X::f << endl;
        cout << "Indirizzo di Y<int>::g( ) " <<  Y<int>::G << endl;
        cout << "Indirizzo di &Y<int>::g( ) " <<  &Y<int>::G << endl;
}
ghigo@sphera:/tmp $
Perche' count stampa 1 quando gli dico X::f( ) ? ( printf invece stampa un 
indirizzo 'bello' ) ?

 indirizzo 'bello' ) ?
Non sono sicuro. Forse sa interpretare la sintassi di puntatore a membro
come spiazzamento e lo stampa cosi'.
Ciao,
Nicola

 indirizzo 'bello' ) ?
Perchč usa l'unico overload diponibile, cioč:
   std::ostream operator<<( bool );
Se vuoi l'indirizzo "bello" devi fare reinterpretare il valore come un
void*, che č quello che fa printf.
   cout << (void*)& X::f << endl;
Da notare, per inciso, che questa operazione non č possibile con nessuna
delle sintassi di casting proprie del C++, nemmeno con reinterpret_cast<>.
Max

 
Intendevo ovviamente:
   std::ostream& std::ostream::operator<<( bool );  
Max

    std::ostream operator<<( bool );
Scusa ho scritto male: in realta a cout passo X::f e NON X::f( ); per cui 
X::f puo' essere solo un puntatore a funzione.... per cui non riesco a 
capire il perche' del passaggio puntatore a funzione -> bool


Certo, è ovvio che intendessi scrivere "X::f".
Credo che la conversione implicita da puntatore-a-funzione a bool sia
prevista dallo standard. Quella da puntatore-a-funzione a void*, invece,
sicuramente non è consentita (anzi, è un tipo di cast che non è ottenibile
nemmeno in modo esplicito, come ho detto nell'altro messaggio).
Non c'è un overload di operator<< che accetti un puntatore-a-funzione (non
esistendo, del resto, un tipo "generico" di puntatore-a-funzione),
pertanto l'unico overload usabile è quello che accetta un bool, che è
quello che sceglie il compilatore.
Max