Newsgroup: it.comp.lang.c++
Titolo: Funzioni membro template (pseudo) virtuali

Ciao a tutti,
Visto che Kiuhnm da qualche giorno ha smesso di stuzzicarci e che di
conseguenza il newsgroup e' praticamente entrato in catalessi, ho
pensato di sottoporvi un quesito.
In una libreria per l'accesso a database ho la necessita' di
rappresentare in modo generico un campo di una tabella; supponiamo di
avere
class Field;
Devo definire un modo per recuperare il valore del campo nel tipo
corretto (ad es se e' un campo intero in un int, se e' stringa in una
std::string, ecc.), ma voglio che la classe Field sia indipendente dal
DBMS sottostante, quindi la definisco come classe base astratta.
Tralasciamo per il momento il problema di individuare il tipo corretto
del campo; per recuperare il valore potrei definire delle funzioni
membro come
class Field {
  public:
    virtual int getInt() const = 0;
    virtual std::string getString() const = 0;
};
(Librerie come la VCL della Borland fanno piu' o meno cosi'). Pero'
voglio rendere esplicito il fatto che si tratta di una famiglia di
funzioni che si differenzia sul tipo, ovvero vorrei poter scrivere
cosi':
class Field {
  public:
    template <typename ValueT>
    virtual ValueT get() const = 0;
};
Purtroppo pero' il C++ non consente la definizione di funzioni membro
template virtuali; il motivo e' abbastanza intuitivo, poiche' si tratta
di un insieme potenzialmente infinito di funzioni, la virtual table
avrebbe dimensioni potenzialmente infinite.
Che fare?
In realta' il problema cosi' come l'ho posto e' troppo aperto e non
sono convinto che esista una soluzione generale; penso pero' che anche
l'arrivare a un insieme di vincoli operativi che consenta comunque una
soluzione efficace dal punto di vista pratico sia istruttivo. Il
problema puo' anche essere riformulato cosi':
// Field.hpp
class FieldBase {};
class Field {
  public:
    template <typename ValueT>
    ValueT get() const;
  private:
    FieldBase * fb;
};
// Field.cpp
class MySqlField : public FieldBase {
  public:
    template <typename ValueT>
    ValueT get() const { }
};
class PostgresqlField : public FieldBase {
  public:
    template <typename ValueT>
    ValueT get() const { }
};
Come posso implementare
template <typename ValueT>
ValueT Field::get() const;
In modo che venga richiamata la funzione membro template get della
classe corretta, senza che i dettagli di MySqlField e PostgresqlField
traspaiano in Field.hpp?
Buon lavoro!
Ciao,
Nicola

 pensato di sottoporvi un quesito.
Provo.
class Field {
    virtual void read(void mem*, int sz) const = 0;
public:
    template <typename ValueT> ValueT get() const {
        ValueT val;
        read((void*)(&val), sizeof(ValueT));
        return val;
    }
};
E' una soluzione *estremamente* semplificata, read(...) si smazza tutto il 
lavoro di leggere sz bytes.
--*PaN!* 

Nicola Musatti ha scritto:

PAN! ti ha già risposto chiarendo a me al contempo un dubbio che 
mi venne dal tuo post, ossia :
dato che un membro template è già di per sé stesso in grado di 
dare l'opportuno valore di uscita (pur con sintassi diversa, 
dato che la info la fornisci tu, specializzandolo) coprendo 
tutti i tipi possibili, perché allora rimane essenziale la 
virtualizzazione e la reimplementazione ?
Un unico template dovrebbe poter coprire tutti i casi, rendendo 
superflua la reimplementazione dei vari casi .... ovviamente 
questo vale per tipi la cui implementazione sia in effetti 
esprimibile con codice generico. Se i tipi hanno layout 
profondamente diversi (che so, un albero sparso vs. un dato 
compatto), allora la mia risposta è inadatta (forse quella di 
PAN! è ancora valida, dato che delega a read il mazzo, con le 
sue parole).
ciao
Soviet
CUT ALL

 è ancora valida, dato che delega a read il mazzo, con le sue parole).
La cosa dovrebbe essere un pelo piu' complicata... per esempio, il codice 
che ho scritto non puo' funzionare per le stringhe. Prima ero un po' preso, 
ora cerchero' di spiegarmi meglio.
Quello che intendevo dire era piu' o meno questo: e' possibile spostare la 
"logica" template per classi sulla classe base, mantenendo alcune specifiche 
funzioni virtuali che verranno implementate per ogni discendente.
Su "Field" si puo' anche fare un po' di specializzazione, implementando 
qualche variante, ad esempio per "string"
class Field {
    virtual void read(void mem*, int sz) const = 0;
    virtual size_t getLength() const = 0
public:
    template <typename ValueT> ValueT get() const {
        ValueT val;
        read((void*)(&val), sizeof(ValueT));
        return val;
    }
    template<typename TE, typename TT, typename TA> std::basic_string<TE, 
TT, TA> > get<std::basic_string<TE, TT, TA> >() const {
        size_t len = getLength();
        size_t sz = len / sizeof(TE);
        TE * s = new TE[sz + 1];
        read((void *)s, len);
        s[sz] = 0; // null-terminated string
        std::basic_string<TE, TT, TA> ret(s);
        delete[] s;
        return ret;
    }
};
ecc...
(ho scritto tutto nel mail editor, se ho scritto boiate fatemi sapere =))
--*PaN!*

 lavoro di leggere sz bytes.
Mi pare che tu stia dando per scontato di lavorare con aggregati (in
soldoni le classiche struct del C). In generale pero' non e' detto che
sia possibile semplicemente copiare un buffer reperito in qualche modo
sulla tua variabile val. L'esempio di std::string e' particolarmente
significativo.
Il problema si complica ulteriormente se cerchi di soddisfare
esattamente la mia seconda enunciazione del problema. Pero' e' un
approccio che si puo' far funzionare e che potrebbe risultare
preferibile al mio.
Ciao,
Nicola

 del campo;
Veramente il problema è tutto qui :)
E' chiaro che il C++ non offre un supporto diretto per quanto chiedi.
Almeno cerchiamo di fare meno fatica possibile.
Il codice che segue non è definitivo, nel senso che non ho provato a 
semplificarlo, renderlo più naturale, più completo, e soprattutto a 
creare una buona astrazione da usare in pratica.
Le parti evidenziate sono quelle che vanno modificate per aggiungere 
nuovi campi.
In più c'è quell'odioso switch che non può essere eliminato. E' quello 
che esegue il "dispatching" (limitato al dumping, in questo esempio).
Se però sai già che riceverai 100 elementi dello stesso tipo, non farti 
scrupoli ad usare direttamente static_cast :)
---------------------
#include <iostream>
using namespace std;
template <class T>
class type_to_id;
//=============================================================================
template <> struct type_to_id<double> { static const unsigned val = 1; };
template <> struct type_to_id<string> { static const unsigned val = 2; };
//=============================================================================
class Field
{
	unsigned type;
protected:
	Field(unsigned t) : type(t) {}
public:
	virtual ~Field() {}
	unsigned getType() const { return type; }
	bool isType(unsigned t) const { return t == type; }
};
template <class T>
class FieldGet : public Field
{
	T data;
public:
	FieldGet(const T& data) : data(data), Field(type_to_id<T>::val) {}
	T get() const { return data; }
};
//=============================================================================
class FieldDouble : public FieldGet<double>
{
public:
	FieldDouble(double d) : FieldGet<double>(d) {}
};
class FieldString : public FieldGet<string>
{
public:
	FieldString(const string& s) : FieldGet<string>(s) {}
};
//=============================================================================
void dump(Field* f)
{
	switch (f->getType())
	{
	case type_to_id<double>::val:
		cout << static_cast< FieldGet<double>* >(f)->get() << endl;
		break;
	case type_to_id<string>::val:
		cout << static_cast< FieldGet<string>* >(f)->get().c_str() << endl;
		break;
	default:
		throw;
	}
}
int main()
{
	FieldDouble FD(123.456);
	FieldString FS("abcdefg");
	dump(&FD);
	dump(&FS);
}
---------------------
Kiuhnm

 virtualizzazione e la reimplementazione ?
La virtualizzazione e' necessaria per nascondere agli utenti della
classe i dettagli delle diverse implementazioni per particolari
tecnologie di database e la reimplementazione serve in sostanza a
fornire alle classi derivate l'informazione sul tipo di dato da
reperire, che potrebbe risultare necessaria sia perche' il tipo di dato
da restituire potrebbe prevedere un costruttore, sia perche'
l'informazione potrebbe essere necessaria a interpretare i dati
provenienti dal database.
Ciao,
Nicola

 Veramente il problema è tutto qui :)
Direi che nella tua risposta ci sono tutti gli elementi della
soluzione, ma hai stravolto il caso d'uso. Supponi di aver appena
interrogato una tabella di cui conosci la struttura e quindi puoi
assumere a priori di conoscere il tipo corretto del campo su cui operi.
Qualcosa come:
ResultSet rs = query.exec("select campo from tabella");
std::cout << rs["campo"].get<std::string>();
Dove result_set["campo"] restituisce un reference a Field.
Ciao,
Nicola

 Che fare?
Premetto che ho letto un po' di fretta il messaggio e che ci ho pensato 
poco perché in questo momento sono di fretta, per cui facilmente mi è 
sfuggito qualcosa, comunque invece di usare una funzione membro template 
io farei restituire alla get una classe proxy che abbia l'operatore di 
conversione template (io uso molto questo "pattern" per avere a 
disposizione l'overloading sul tipo di ritorno).
In questo modo non essendo template la funzione get può benissimo essere 
virtuale ed inoltre la chiamata è più semplice in quanto il compilatore 
è in grado di dedurre il tipo, per cui diventerebbe:
class Field
{
private:
     struct FieldProxy
     {
         template <class T> operator T()
         {
             ...
         }
     };
public:
     virtual FieldProxy get() const = 0;
};
...
Field* field = new MySQLField();
...
int a = field->get();
oppure
std::string s = field->get();
ecc...
Spero di averti dato un'idea.
Ciao.

 assumere a priori di conoscere il tipo corretto del campo su cui operi.
Allora la soluzione è banale e non serve alcuna chiamata virtuale (nel 
mio codice non ve ne sono).
Kiuhnm

 Spero di averti dato un'idea.
Il "trucco" della conversione mi mancava: geniale!
Perň in questo modo hai una chiamata virtuale. Se invece la conversione 
viene eseguita manualmente (per es. tramite static_cast), la chiamata 
virtuale puň essere evitata.
Kiuhnm

 std::string s = field->get();
E' un approccio interessante, tanto piu' che basta riscriverlo come
class Field
{
public:
    template <typename ValueT>
    ValueT get() { return do_get(); }
private:
     struct FieldProxy
     {
         template <class T> operator T()
         {
             ...
         }
     };
     virtual FieldProxy do_get() const = 0;
};
per rientrare nel mio caso d'uso.
I dettagli dell'implementazione di FieldProxy e do_get() non sono a mio
avviso del tutto trascurabili, ma e' un discorso che intendo riprendere
piu' in la'.
Ciao,
Nicola

     ValueT get() { return do_get(); }
Come fa il compilatore a dedurre ValueT?
Cosě rendi vano l'uso di FieldProxy.
Tanto varrebbe usare
   template <typename T>
   void get(T& t);
Kiuhnm

 pensato di sottoporvi un quesito.
Penso sia venuto il momento di espandere il discorso, in modo che le
mie risposte precedenti risultino piu' comprensibili.
1) La sintassi
Field f = ...
std::string s = f.get<std::string>();
a me piace. Tenete presente che le funzioni membro get<T>() servono
piuttosto a ritornare il valore del tipo corretto in base al campo cui
fanno riferimento che non a effettuare conversioni di tipo. Per questo
trovo meglio chiedere al programmatore di specificare il tipo da
ritornare piuttosto che affidarmi alla deduzione di argomenti/valori
ritornati, col rischio di errori nascosti. Come dicevo hanno piu' in
comune con un'ipotetica famiglia di funzioni membro getInt(),
getString(), ecc. che con un template vero e proprio, ma esprimendo il
tipo su cui operano in modo esplicito facilitano la scrittura di codice
generico.
2) Le sottoclassi di Field hanno bisogno di conoscere il tipo passato o
un'informazione equivalente per decidere come recuperare il valore del
campo. In altre parole e' il tipo di dato che cerco di reperire che
quida l'interrogazione al database e non il tipo del campo del database
che guida la costruzione dell'opportuna sottoclasse di Field. Se fate
caso al mio secondo esempio Field e' derivata in base al DBMS da
interfacciare e non al tipo del campo corrispondente.
Ciao,
Nicola

 Buon lavoro!
Ho ripulito un po' il codice: non vi sono chiamate virtuali e confermo 
che il VC2005 ottimizza il codice eliminando tutti i passaggi intermedi.
----------------------
//=============================================================================
// Infrastruttura
//=============================================================================
class Field
{
	class wrap
	{
		Field* f;
	public:
		wrap(Field* f) : f(f) {}
		template <class T>
		operator T() { return ( (FieldGet<T>*)f )->get(); }
	};
protected:
	Field() {}
	~Field() {}
public:
	wrap get() { return wrap(this); }
};
template <class T>
class FieldGet : public Field
{
protected:
	T FG_data;
	FieldGet() {}
	FieldGet(const T& t) : FG_data(t) {}
public:
	T get() const { return FG_data; }
};
//=============================================================================
// Esempio
//=============================================================================
#include <iostream>
using namespace std;
class FieldDouble : public FieldGet<double>
{
public:
	FieldDouble(double d) : FieldGet(d) {}
};
class FieldString : public FieldGet<string>
{
public:
	FieldString(const string& s) : FieldGet(s) {}
};
class FieldChar : public FieldGet<char>
{
public:
	void init() { FG_data = 'c'; }
};
int main()
{
	FieldDouble FD(123.456);
	FieldString FS("abcdefg");
	FieldChar FC;
	FC.init();
	Field* fd = &FD;
	Field* fs = &FS;
	Field* fc = &FC;
	cout << ( (double) fd->get() )         << endl;
	cout << ( (string) fs->get() ).c_str() << endl;
	cout << ( (char)   fc->get() )         << endl;
}
----------------------
Kiuhnm

 
Magari si noterà la mia influenza con linguaggi dinamici...
class FieldGetter {
   void* getField(typeId) const = 0;
}
class Field {
   public:
     template <typename ValueT>
     ValueT get() const {
        pointer = fg->getField(typeid(ValueT));
        value = *static_cast<ValueT*>(pointer);
  	delete pointer;
        return value;
     }
   private:
     FieldGetter *fg;
};
getField può usare un array associativo per individuare la funzione da
usare per il dato tipo.
Il tutto non testato (e sono anni che non uso C++).
Riguardo l'efficienza: il collo di bottiglia è l'accesso al database,
                       quindi non dovrebbe importare.
Saluti  Manlio Perillo

 usare per il dato tipo.
Questo e' quello che manca alla soluzione presentata qualche giorno fa
da *PaN!* .
Combinando le due si puo' scrivere qualcosa come:
#include <typeinfo>
class FieldGetter {
public:
    virtual void getField(std::type_info const &, void *) const = 0;
};
class Field {
public:
    Field(FieldGetter * f) : fg(f) {}
    template <typename ValueT>
    ValueT get() const {
        buffer_allineato<ValueT> buf;                    // [1]
        fg->getField(typeid(ValueT), &buf);
        return *reinterpret_cast<ValueT *>(&buf);
    }
private:
    FieldGetter *fg;
};
class MySqlFieldGetter : public FieldGetter {
public:
    void getField(std::type_info const &, void *) const {
    }
   template <typename ValueT>
    ValueT get() const {                                // [2]
        return ValueT();
    }
};
int main()
{
    MySqlFieldGetter * msfg = new MySqlFieldGetter;
    Field f(msfg);
    return f.get<int>();
}
Dove buffer_allineato<ValueT> (vedi [1]) e' un tipo costruito
opportunamente in modo da eliminare la necessita' di un costruttore di
default per ValueT; ad esempio si puo' pensare di usare per questo
boost::optional.
A questo punto esistono tanti modi per implementare getField, che
dipendono sostanzialmente dalla tecnologia sottostante. Per completare
il mio esercizio originale si potrebbe trovare un modo per far si che
richiami la corretta specializzazione della funzione membro template
get<>() (vedi [2])
Quando avete finito [ ;-) ] vi mostro la mia versione originale che,
devo ammettere, e' alquanto piu' macchinosa.
Ciao,
Nicola

Ciao,
visto che siete tutti pigri o in vacanza concludo il discorso da solo.
L'idea e' completare il seguente programma in modo che la chiamata
f.get<int>() nella funzione main() [1] richiami la corrispondente
specializzazione della funzione membro template MySqlFieldGetter::get()
[2]:
class FieldGetter {
public:
    virtual ~FieldGetter() {}
};
class Field {
public:
    Field(FieldGetter * f) : fg(f) {}
    template <typename ValueT>
    ValueT get() const;
private:
    FieldGetter *fg;
};
class MySqlFieldGetter : public FieldGetter {
public:
    template <typename ValueT>
    ValueT get() const;                     // [2]
};
int main() {
    MySqlFieldGetter * msfg = new MySqlFieldGetter;
    Field f(msfg);
    std::cout << f.get<int>() << '\n';    // [1]
}
Un'implementazione completa della soluzione suggerita da *Pan!* e
Manlio Perillo e' la seguente:
// Dinamica.hpp
#include <typeinfo>
#include <boost/optional.hpp>
class FieldGetter {
public:
    virtual ~FieldGetter() {}
// Il dispatching avviene tramite una singola funzione membro
// virtuale che rende dinamica l'informazione sul tipo passato
// come parametro al template
    virtual void getField(std::type_info const &, void *) const = 0;
};
class Field {
public:
    Field(FieldGetter * f) : fg(f) {}
// boost::optional e' utilizzato come buffer
    template <typename ValueT>
    ValueT get() const {
        boost::optional<ValueT> buf;
        fg->getField(typeid(ValueT), &buf);
        return *buf;
    }
private:
    FieldGetter *fg;
};
class MySqlFieldGetter : public FieldGetter {
public:
    void getField(std::type_info const & t, void * v) const;
    template <typename ValueT>
    ValueT get() const;
};
// Dinamica.cpp
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/vector.hpp>
#include "Dinamica.hpp"
// Per ricostruire l'informazione statica sul tipo e quindi
// scegliere la specializzazione corretta del template e'
// necessario sapere tra quali tipi cercarlo.
typedef boost::mpl::vector<int, std::string, double> types;
// La classe template dispatcher sostituisce una sequenza di
// if-else che provano esplicitamente tutti i tipi possibili. E' piu'
// complicata, ma una volta scritta e' sufficiente aggiungere
// nuovi tipi al vettore compile-time 'types'.
template <typename IterT>
struct dispatcher
{
    dispatcher(std::type_info const & t, void * v) : ti(t), val(v) {}
    typedef typename boost::mpl::deref<IterT>::type type;
    template<typename FieldGetterT>
    void operator()(FieldGetterT const & self) const
    {
        if ( ti == typeid(type) )
        {
            boost::optional<type> & opt =
                    *reinterpret_cast<boost::optional<type> *>(val);
             opt = self.get<type>();
        }
        else
        {
            dispatcher<typename boost::mpl::next<IterT>::type>(ti,
                    val)(self);
        }
    }
    std::type_info const & ti;
    void * val;
};
template <>
struct dispatcher<boost::mpl::end<types>::type>
{
    dispatcher(std::type_info const &, void *) {}
    template<typename FieldGetterT>
    void operator()(FieldGetterT const &)
    {
        throw std::out_of_range("Tipo non supportato");
    }
};
template <typename ValueT>
inline ValueT MySqlFieldGetter::get() const {
    return ValueT();
}
// Un compilatore decente e' in grado di espandere inline tutte le
// successive chiamate a dispatcher::operator()
void MySqlFieldGetter::getField(std::type_info const & t,
        void * v) const
{
    dispatcher<boost::mpl::begin<types>::type>(t, v)(*this);
}
// Fine Dinamica.cpp
A mio avviso l'unico difetto di questa soluzione e' che il
funzionamento corretto di std::type_info non e' garantito quando ci
sono di mezzo delle librerie dinamiche. A questo si potrebbe ovviare
utilizzando al posto di std::type_info e boost::optional una forma di
union discriminata o una classe "variant". La mia versione invece non
fa uso di chiamate virtuali, ma ha la scocciatura di richiedere
l'istanziazione esplicita delle specializzazioni necessarie di
Field::get(). Questa variante non modifica le dichiarazioni presenti in
cima al messaggio.
// Statica.cpp
#include <stdexcept>
#include <string>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/advance.hpp>
#include <boost/mpl/vector.hpp>
#include "Statica.hpp"
// In questo caso l'iterazione a compile time avverrebbe sulle
// sottoclassi di FieldGetter
typedef boost::mpl::vector<MySqlFieldGetter> types;
template <typename ValueT, typename IterT>
struct dispatcher
{
    dispatcher(FieldGetter * f) : fld(f) {}
    typedef typename boost::mpl::deref<IterT>::type type;
    ValueT operator()() const
    {
        type * f = dynamic_cast<type *>(fld);
        if ( f != 0 )
            return f->get<ValueT>();
        else
        {
            return dispatcher<ValueT,
                    typename boost::mpl::next<IterT>::type>(fld)();
        }
    }
    FieldGetter * fld;
};
template <typename ValueT>
struct dispatcher<ValueT, boost::mpl::end<types>::type>
{
    dispatcher(FieldGetter *) {}
    ValueT operator()() const
    {
        throw std::out_of_range("Tipo di campo sconosciuto");
    }
};
template <typename ValueT>
inline ValueT MySqlFieldGetter::get() const
{
    return ValueT();
}
template <typename ValueT>
ValueT Field::get() const
{
    return dispatcher<ValueT,
                    typename boost::mpl::begin<types>::type>(fg)();
}
// Questo e' l'aspetto piu' noioso. Le macro possono essere di
// aiuto, ma solo fino a un certo punto.
template int Field::get<int>() const;
template std::string Field::get<std::string>() const;
template double Field::get<double>() const;
// Fine Statica.cpp
Devo dire che se non fosse per il problema delle librerie dinamiche
penserei seriamente se non passare all'altra soluzione. Spero vi siate
divertiti.
Ciao,
Nicola

scusami, potrebbe essere utile un bel "visitor"?

 scusami, potrebbe essere utile un bel "visitor"?
Prova a descriverne l'applicazione.
Ciao,
Nicola

pensavo che Field potrebbe accettare un visitor, il quale sa di che
tipo è field: qualcosa del genere (scusa gli errori eventuali, ma sto
imparando da poco il c++):
class Field
{
   public:
      virual void accept(FieldVisitor* v) = 0;
      virtual ~Field() { }
}
class IntField : public Field
{
      int value;
   public:
      virtual void accept(FieldVisitor* v) { v->visit(this); }
      int getValue() { return value; }
      virtual ~IntField() { }
}
class FloatField : public Field
{
      float value;
   public:
      virtual void accept(FieldVisitor* v) { v->visit(this); }
      float getValue() { return value; }
      virtual ~FloatField() { }
}
class FieldVisitor
{
   public:
      virtual void visit(IntField* field) = 0;
      virtual void visit(FloatField* field) = 0;
      virtual ~FieldVisitor() { }
}
class PrinterFieldVisitor : public FieldVisitor
{
   public:
      virtual void visit(IntField* field) { printf("%d\n",
field->getValue()); } // perdonami il printf !!!
      virtual void visit(FloatField* field) { printf("%f\n",
field->getValue()); } // perdonami il printf !!!
      virtual ~FieldVisitor() { }
}
Che ne dici? Spero di non essere stato troppo superficiale nella
lettura dei tuoi post e nella comprensione del problema


Rileggendo il problema mi sono accorto che la risposta è incompleta:
potrebbe esserti utile anche il pattern "Builder" (GoF):
Field possiede un riferimento ad una sua implementazione FieldImpl che
è legata al db in particolare. Questa ti consente di reperire le
informazioni necessarie (come il valore di un campo) dal db. Pertanto
hai due gerarchie parallele: una con radice Field ed un altra con
radice FieldImpl.
Spero di non essere stato troppo confusionario.


 radice FieldImpl.
Sbaglio o di solito programmi in Java? :-)
L'errore fondamentale del tuo esempio precedente, che ora che ci penso
ha fatto anche Kiuhnm, e' assumere che il tipo di dato rappresentato
sia una proprieta' immanente di un campo. Nell'ambito dei DBMS questo
e' sostanzialmente vero dal punto di vista dell'SQL: se ho dichiarato
un campo numeric(10,4) questo non e' varchar(10); non lo e' invece dal
punto di vista del C++ perche' la corrispondenza tra tipo di dato SQL e
tipo di dato C++ non e' necessariamente univoca. In genere ogni campo
di un record risultato di una query viene convertito su richiesta nel
tipo specificato al momento, con un'eventuale segnalazione di errore
qualora la conversione non sia possibile.
Da qui la necessita' di prevedere piu' funzioni membro per il recupero
del dato.
Per quanto riguarda l'applicazione del pattern Visitor come descritto
dalla GoF mi rendo conto che nei linguaggi che non supportano
adeguatamente la programmazione generica e' spesso insostituibile; in
C++ spesso si trovano soluzioni piu' efficaci basate su template.
Ciao,
Nicola

 sia una proprieta' immanente di un campo.
Quanto dici non č per niente chiaro. Tu hai posto delle condizioni e io 
le ho rispettate dalla prima fino all'ultima.
Ti rendi conto che modifichi le regole del gioco di volta in volta? 
Difficilmente ti si potrŕ dare una soluzione accettabile.
Kiuhnm

 Difficilmente ti si potrà dare una soluzione accettabile.
Non mi pare, rileggendo il messaggio originale. E' vero invece che la
spiegazione delle scelte e' sparsa in diversi messaggi. Tu e Giacomino
avete impostato il problema in modo molto diverso da come io l'ho
specificato.
Ciao,
Nicola

 divertiti.
Sicuramente, anzi ho imparato qualcosa che non avevo avuto ancora il tempo 
di guardare (l'uso di boost::mpl::vector ;)).
Comunque ti capisco, anch'io ho problemi con le librerie dinamiche... 
All'eccesso nel mio caso, dovendo linkare roba di compilatori diversi, ho 
dovuto bannare std::* da tutte le interfacce. La cosa risulta abbastanza 
fastidiosa sopratutto per le std::string.
--Marco *PaN!* Foco, col nome completo, e scusate la fretta =)