Newsgroup: it.comp.lang.c++
Titolo: File to Class: voi come fareste?

Ho un file di configurazione formattato come da esempio:
Nome=Pippo
Eta=23
Patente=false
...
...
In cui sono presenti circa una sessantina di valori stringa, intero e
boolean (true/false)
Lo devo leggere ed inserire in una classe in cui i membri, ovviamente,
sono del tipo base...
Per ora ho creato una MAP string to string leggendo il file
sequenzialmente e poi, uno alla volta, estraggo i dati dalla mappa
creata inserendoli nei membri di classe previa eventuale conversione
in interi o boolean (le stringhe invece le copio direttamente)...
Il metodo è macchinoso, ci sono alternative? Il file cosi è fatto e
cosi deve rimanere...
Luc.

Mi sai che puoi rigirare il problema in molti modi, ma la macchinosita'
resta: puoi eliminare la mappa leggendo e convertendo al volo valore per
valore, magari all'interno di una classe che faccia da parser e da
factory.
Ciao,
Nicola


Innanzitutto saluti a tutti e scusate se rispondo ad una domanda cosě
vecchia.
Comunque ecco come io affronterei il problema:
#include <iostream>
#include <string>
#include <sstream>
class Luc
{
   private:
      std::string Nome;
      int Eta;
      bool Patente;
      static std::string factorize(std::istream& is)
      {
         std::string line;
         std::getline(is, line);
         std::string::size_type pos = line.find('=');
         if(pos == std::string::npos)
         {
            is.setstate(std::ios_base::failbit);
            return "";
         }
         else return line.substr(pos + 1);
      }
   public:
      Luc()
      : Nome(), Eta(), Patente() {  }
      Luc(const std::string& Nome_, int Eta_, bool Patente_)
      : Nome(Nome_), Eta(Eta_), Patente(Patente_) {  }
      friend std::istream& operator>>(std::istream&, Luc&);
};
std::istream& operator>>(std::istream& is, Luc& l)
{
   Luc tmp;
   // legge Nome
   tmp.Nome = Luc::factorize(is);
   // legge Eta
   std::istringstream in(Luc::factorize(is));
   in >> tmp.Eta;
   // legge Patente
   in.str(Luc::factorize(is));
   // nella lettura precedente si arriva alla fine della stringa
   in.clear(in.rdstate() & ~std::ios_base::eofbit);
   in >> std::boolalpha >> tmp.Patente;
   in.clear(in.rdstate() & ~std::ios_base::eofbit);
   // Aggiunge lo stato di in a quello di is
   is.setstate(in.rdstate());
   if(is) l = tmp;
   return is;
}
Saluti  Manlio Perillo

 [...]
Ho implementato un'altro metodo molto più flessibile del precedente.
E' un poco lunghetto, spero che non sia di disturbo a qualcuno.
I commenti sono quasi del tutto assenti, ma credo risulti chiaro
l'approccio adottato.
/**** file CFGReager.hpp ***/
#include <string>
#include <map>
#include <algorithm>
#include <istream>
#include <sstream>
std::pair<std::string, std::string> factorize(std::istream& is)
{
   std::string line;
   std::getline(is, line);
   std::string::size_type pos = line.find('=');
   return std::make_pair(line.substr(0, pos), line.substr(pos + 1));
}
class CFGProxy_base
{
   public:
      virtual CFGProxy_base& operator=(const std::string&) = 0;
};
template<typename T> class CFGProxy: public CFGProxy_base
{
   private:
      T* ref;
   public:
      CFGProxy(T* value)
      : ref(value) {  }
      CFGProxy& operator=(const std::string& s)
      {
         std::istringstream in(s);
         in.exceptions(std::ios_base::failbit |
                       std::ios_base::badbit);
         in >> *ref;
         return *this;
      }
};
// specializzazione per bool
template<> class CFGProxy<bool>: public CFGProxy_base
{
   private:
      bool* ref;
   public:
      CFGProxy(bool* value)
      : ref(value) {  }
      CFGProxy& operator=(const std::string& s)
      {
         std::istringstream in(s);
         in.exceptions(std::ios_base::failbit |
                       std::ios_base::badbit);
         in >> std::boolalpha >> *ref;
         return *this;
      }
};
// specializzazione per std::string
template<> class CFGProxy<std::string>: public CFGProxy_base
{
   private:
      std::string* ref;
   public:
      CFGProxy(std::string* value)
      : ref(value) {  }
      CFGProxy& operator=(const std::string& s)
      {
         *ref = s;
         return *this;
      }
};
class CFGMap
{
   private:
      typedef std::map<std::string, CFGProxy_base*> map_type;
      map_type map_;
      struct deletion
      {
         void operator()(map_type::value_type& v) { delete v.second; }
      };
   public:
      template<typename T> void registra(T* type, const std::string&
                                         value)
      {
         map_[value] = new CFGProxy<T>(type);
      }
      // restituisce una referenza per comodità
      CFGProxy_base& operator[](const std::string& value)
      {
         return *map_[value];
      }
      ~CFGMap()
         std::for_each(map_.begin(), map_.end(), deletion());
      }
};
Infine ecco un esempio che mostra l'utilizzo di tali classi:
/**** file Luc.hpp ****/
#include "CFGReader.hpp"
#include <iostream>
class Luc
{
   private:
      std::string Nome;
      int Eta;
      bool Patente;
   public:
      Luc()
      : Nome(), Eta(), Patente() {  }
      Luc(const std::string& Nome_, int Eta_, bool Patente_)
      : Nome(Nome_), Eta(Eta_), Patente(Patente_) {  }
      friend std::istream& operator>>(std::istream&, Luc&);
};
std::istream& operator>>(std::istream& is, Luc& l)
{
   Luc tmp;
   CFGMap map_;
   map_.registra(&tmp.Nome, "Nome");
   map_.registra(&tmp.Eta, "Eta");
   map_.registra(&tmp.Patente, "Patente");
   try
   {
      for(int i = 0; i < 3; ++i)
      {
         std::pair<std::string, std::string> pair_ = factorize(is);
         map_[pair_.first] = pair_.second;
      }
   }
   catch(const std::ios_base::failure&)
   {
      is.setstate(std::ios_base::failbit);
   }
   std::cout << "tmp = " << tmp << '\n';
