Newsgroup: it.comp.lang.c++
Titolo: Dinamismo

Salve,
    sto utilizzando una libreria che mette a disposizione delle classi C++
per svolgere determinati compiti; la particolare architettura della libreria
fa sì che esistano diverse classi che compiono la medesima funzione anche se
con codice diverso.
Nella mia particolare applicazione mi trovo a dover dare all'utente la
possibilità di scegliere la classe da utilizzare, ad esempio tramite la
richiesta di una stringa che lo identifichi.
Non volendo ricorrere a costrutti C, ossia volendo evitare l'utilizzo di
blocchi switch o di un mucchio di if, ho cercato di utilizzare una tecnica
di questo tipo(faccio presente che preferirei non andare a manipolare tutte
le classi della libreria per derivarle da una mia classe...in modo poi da
simulare dei costruttori virtuali; leggi factory):
 ho creato una classe(in realtà ho fatto una classe astratta pura e ho
manipolato nelle derivate, ma non è rilevante), al suo interno ho messo una
std::map che individua una corrispondenza tra il nome dell'algoritmo
implementato nella classe della libreria e un puntatore ad un membro della
mia classe che va ad istanziare e ritornare un puntatore alla classe
richiesta della libreria.
Nonostante il codice funzioni, non mi sembra di aver prodotto una soluzione
molto elegante; vorrei sapere se, senza ricorrere alla factory, che mi
porterebbe a mettere le mani su circa 1Mb di sorgenti altrui, c'è una
soluzione migliore per fare quello che ho descritto.
Ringrazio anticipatamente.
P.S.: forse il metodo da me usato può non essere immediatamente chiaro,
perciò faccio un esempio stupido, che non mostra le limitazioni cui sono
soggetto, ma che dovrebbe comunque far comprendere il metodo da me usato.
/*--------*/
#include <iostream>
#include <map>
class base {
 public:
  virtual void doIt() = 0;
};
class T1 : public base{
 public:
  inline void doIt() { std::cout << "uno" << endl; }
};
class T2 : public base {
 public:
  inline void doIt() { std::cout << "due" << endl; }
};
class factory {
 typedef void (*pointer)();
 private:
  std::map <int, pointer> table;
  pointer f;
  factory();
  inline void populate()
   table.insert(table.end(), std::make_pair(1, &factory::createT1));
   table.insert(table.end(), std::make_pair(2, &factory::createT2));
  }
  base * createT1() { return(new T1); }
  base * createT2() { return(new T2); }
 public:
  inline factory(const int & ID)
   populate();
   f = table[ID]; /*ID è supposto essere o 1 o 2*/
  }
  inline base * get()
   return(this->*f);
  }
};
int main() {
 int code;
 std::cout << ":"; std::cin >> code; /* code è supposto essere o 1 o 2*/
 factory x(code); base *object = x.get(); object->doIt(); delete object;
 return(EXIT_SUCCESS);
}
/*--------*/

[loom] wrote:

Per evitare alcune ripetizioni del codice ( ma il metodo è sostanzialmente 
lo stesso )
[ghigo@therra /tmp]$ cat prova.cpp
#include <iostream>
#include <string>
#include <map>
 
using namespace std;
 
class base { public: virtual void doit( ) = 0; };
 
 
struct createIT_base {
        virtual base * operator( )( ) =0;
};
 
template<typename T>
  struct createIT : createIT_base {
        T* operator( )( ) { return new T(); }
  };
 
map<string,createIT_base * >    mappa;
 
#define ADD_MAPPA( type ) \
        mappa[ #type ] = new createIT<type>( );
 
#define GET_P_CLASS( type )   ( dynamic_cast<type*>((*mappa[ #type ])()  ))
 
// qua' "aggiungi le classi"
class T1 : public base { public: void doit( ){ cout << "T1" << endl; } };
class T2 : public base { public: void doit( ){ cout << "T2" << endl; } };
 
void init( ){
 
// qua' le memorizzi
ADD_MAPPA( T1 );
ADD_MAPPA( T2 );
 
}
 
 
int main( ){
 
        init( );
        GET_P_CLASS( T1 )->doit( );
        GET_P_CLASS( T2 )->doit( );
 
        return 0;
}
[ghigo@therra /tmp]$ g++ -Wall -o prova prova.cpp
[ghigo@therra /tmp]$ ./prova
T1
T2
[ghigo@therra /tmp]$
Con il dynamic_cast<> ti poni al riparo da brutte sorprese; forse la 
GET_P_CLASS la si otteneva anche con un template.... ma tant'è
Nella macro GET_P_CLASS bisognerebbe introdurre il controllo sull'esistenza 
dell'elemento richiesto....