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

 double
del quale suppongo basti fare un bellissimo template ... che risolva tutto!!
Un'altra cosa è lavorare per un progetto dove i 7 tipi, sono totalmente
diversi fra loro!!! La struttura di un singolo oggetto ... dove per oggetto
intengo la singola informazione del linguaggio per il quale è stato scritto
l'interprete in C e del quale ho intenzione di riscrivere in C++, è una cosa
simile (non guardate come ve lo scrivo):
Classe + esterna:
class oggetto;
1 classe oggetto contiene i seguenti tipi di dato:
class String
class Number
class Constant
class Variable
class Function
class List
Poi ci sono alcune di queste classi che hanno altri tipi di oggetti ... ad
esempio Number si divide  in
class Signed
class Unsigned
class Real
class Ratio
e ancora Function:
class System
class User
Nonché la class Lista non è altro che una lista di Oggetti .... (definizione
ricorsiva di tutta la struttura del tipo dei dati). Beh ... ammesso che in
C++ posso disporre questo tipo di struttura come mi pare e piace ... visto
che gli operatori che mi interessa implementare sono questi:
+   -   *    /    <   >   <=   >=   ==   !=   ^   &   |   &&   ||
Che mettono in relazione qualsiasi tipo di dato (ad esempio String ^ Signed)
secondo delle relazioni logiche che sono previste da questo linguaggio per
cui sto procedendo alla stesura dell'Interprete (non chiedetemi che senso
hanno ... perché sarebbe troppo complicati), come posso io gestire tutti i
casi senza farmi un culo enorme? En beh? Anche perché poi  .... la struttura
di valutazione delle istruzioni si basa su di una lista di "oggetti", dove
appunto ogni oggetti può essere di quel tipo!
Certo che sarebbe troppo comodo confrontare oggetti + o - uguali tra loro
come mi avete detto voi ... in quel caso sarei stato capacissimo farlo pure
io ... ma una soluzione a questo problema che non sia disastrosa ... beh ...
non l'ho ancora trovata! Come vedete gli operatori sono 15, e i tipi di dato
sono 6, che se espansi (cioè eliminare ad esempio Number per sostituirlo
direttamente con i tipi contenuti) ben 10. Facendo un calcolo di tutti i
possibili casi per cui devo implementare i relativi operatori ... quante
riscritture mi vengono fuori?

 riscritture mi vengono fuori?
dipende dalla definizione degli operatori. Se la definizione prevede una
operazione completamente diversa per ogni caso ... allora devi implementare ogni
caso separatamente. Ma non e' un riscrittura. E' l'implementazione delle
specifiche. Se invece la specifica non prevede che gli operatori facciano cose
terribilmente eterogenee, partendo da quella specifica puoi arrivare ad una
definizione migliore che non quella "pedestre".
Andrea Ferro
---------
Brainbench C++ Master. Scored higher than 97% of previous takers
Scores: Overall 4.46, Conceptual 5.0, Problem-Solving 5.0
More info 
http://www.brainbench.com/transcript.jsp?pid=2522556

riscritture mi vengono fuori?
Se a tutti voi dare un senso e questo senso non e' descrivibile da
regole generali (come ad esempio la promozione) e' assolutamente
*evidente* che devi specificare tutti i casi. In totale direi
qualcosa come:
  Numero casi = N*OP(1)+
                N*N*OP(2)+
                N*N*N*OP(3)+
                ...
dove N e' il numero di tipi distinti e OP(k) e' il numero di
operatori con k argomenti. Questo non per limiti del C++ (o della
programmazione Object Oriented) ma per come poni il problema.
Per venirne fuori in modo elegante e' fondamentale capire quali
sono le regole *generali* che sicuramente sono presenti.
Se non ci sono regole generali vedo anche male l'utente di
questo mitico linguaggio... come unica possibilita' avrebbe di
mandare a memoria il significato di tutte queste combinazioni.
Andrea
--
Andrea Griffini, Programmer
http://www.netservers.com/~agriff

 ha scritto nel messaggio
news:3ad1f9b0...@news.interbusiness.it...


 
http://www.netservers.com/~agriff
Questo mostra la limitata visione della programmazione ad Oggetti rispetto
alla potenza di calcolo della programmazione Funzionale derivata da tutta la
teoria degli Insiemi della mitica e temuta Matematica!

 teoria degli Insiemi della mitica e temuta Matematica!
No, questo mostra che se non specifichi adeguatamente un problema, non
puoi sperare di ottenere risposte soddisfacenti.
Ciao,
Nicola
P.S. Hai mai sentito parlare di Turing-completezza?
-- 
Posted from 
mail.divalsim.it
 [194.185.113.194] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG

 ha scritto nel messaggio
news:3AD301ED...@divalsim.it...


 En beh?
Arrivo.
Dato che e' un interprete le performance del calcolo in se non sono rilevanti.
Quindi promuoviamo semplicemente i numeri a double. Puoi anche usare altri
schemi.
Supponiamo:
struct Double;
struct Object {};
struct Number: Object { virtual Double promote() const=0; };
struct SignedChar: Number { virtual Double promote() const; };
struct UnsignedChar: Number { virtual Double promote() const; };
struct SignedShort: Number { virtual Double promote() const; };
struct UnsignedShort: Number { virtual Double promote() const; };
struct SignedInt: Number { virtual Double promote() const; };
struct UnsignedInt: Number { virtual Double promote() const; };
struct SignedLong: Number { virtual Double promote() const; };
struct UnsignedLong: Number { virtual Double promote() const; };
struct Float: Number { virtual Double promote() const; };
struct Double: Number { virtual Double promote() const; };
struct String: Object { UnsignedInt len() const; };
implementiamo gli operatori > >= <= !=
template <class T1, class T2>
bool operator>( const T1& r1, const T2& r2)
{ return !(r2 <= r1); }
template <class T1, class T2>
bool operator>=( const T1& r1, const T2& r2)
{ return !(r2 < r1); }
template <class T1, class T2>
bool operator<=( const T1& r1, const T2& r2)
{ return (r2 < r1) || (r2 == r1); }
template <class T1, class T2>
bool operator!=( const T1& r1, const T2& r2)
{ return !(r2 == r1); }
restano solo gli operatori < e ==.
template <class T1, class T2>
bool operator==( const T1& r1, const T2& r2 );
template <>
bool operator==<Double,Double>( const Double& r1, const Double& r2 );
{ /* TO DO */ }
template <>
bool operator==<String,String>( const String& r1, const String& r2 );
{ /* TO DO */ }
template <class T1, class T2>
bool operator==( const T1& r1, const T2& r2)
{ return r1.promote() == r2.promote(); }
template <class N>
bool operator==( const String& s, const N& n );
{ return s.Len()==n; }
template <class N>
bool operator==( const N& n, const String& s );
{ return s.Len()==n; }
template <class T1, class T2>
bool operator<( const T1& r1, const T2& r2 );
template <>
bool operator< <Double,Double>( const Double& r1, const Double& r2 );
{ /* TO DO */ }
template <>
bool operator< <String,String>( const String& r1, const String& r2 );
{ /* TO DO */ }
template <class T1, class T2>
bool operator<( const T1& r1, const T2& r2)
{ return r1.promote() < r2.promote(); }
template <class N>
bool operator<( const String& s, const N& n );
{ return s.Len()<n; }
template <class N>
bool operator<( const N& n, const String& s );
{ return s.Len()>n; }
E questo e' il caso dei template, usabile se hai conoscenza diretta dei tipi
degli oggetti. In maniera simile (nel senso del'uso delle proprietà degli
operatori e del concetto di promozione) puoi fare la cosa usando l'ereditarietà.
Meno efficiente ma necessario se non hai la conoscenza dei tipi a priori e devi
usare il polimorfismo.


 schemi.
Il problema è appunto questo ... tu dici che essendo un interprete
basterebbe un solo tipo di numero chiamato Real che possa rappresentare
tutti gli altri! Infatti non ci sarebbero grossi problemi se volessimo fare
le cose alla cazzo di cane! MA visto che questo particolare linguaggio, ha
il sacrosanto *obbligo* di fare molte cose con un ristretto numero di
funzioni e di tipi di dato in modo tale da permettere la computazione di
innumerevoli quantità di problemi ... il + grosso problema si riferisce
appunto al risultato dell'informazione che ogni singola istruzione mi
restituisce! Sono stati definiti 4 tipi diversi di numero in modo tale da
mantenere costante la precisione ... Un Ratio per conto mio mantiene molto
di + la precisione di un real visto che la divisione tra i due long
(RATIONAL = struct ratio{long num; unsigned long denom}; ) rimane sospesa
fino a che non è necessaria! Questo appunto perché la divisione tra 2 reali
è ben diversa da quella realizzata tra due long! Altro problema poi si
verificherebbe per le funzioni che utilizzano gli operatori   |   &  e  ^
che non possono essere eseguiti tra numeri con virgola mobile ... potrei
pure provare a convertire il double con un cast in un
long long int e poi eseguire la stessa operazione ... ma il risultato non
sarebbe mai lo stesso! Ecco il perché di diversi tipi di numeri!
Cmq ripeto ... le classi sono proprio queste:
Signed
Unsigned
Real
Ratio
non ci sono signedchar, signedshort o che altro! La classe Signed contiene
solo un Long e basta ... niente di +! Altrimenti il problema si sarebbe
complicato di molto!! Su ogni classe sono stati definiti gli operatori tra
oggetti della stessa classe ... vale a dire String <op> String; Signed <op>
Signed e via dicendo!
Poi passando all'implementazione finale ... c'é una lista di oggetti ...
dove appunto ripeto, la classe Object è quella che contiene (o da dove si
deriva) ogni altro tipo (Number, String ....). Però io non conosco a priori
il tipo di oggetto ... vale a dire che ad una stessa funzione posso passare
qualsiasi cosa ... un Stringa al posto di una Costante ... o Un numero al
posto di una funzione ... il tutto poi viene gestito dalla funzione che a
seconda del tipo degli argomenti si comporta in un modo + o - diverso. Ecco
perché ... già la realizzazione della struttura dati inzia a diventare un
po' troppo complessa! Se faccio un union da inserire come parte dati nella
Class Object ... poi alla fine oltre a fare un   xxx = new <Classe oggetto>
(tanto per farti capire come ....) poi alla fine da qualche parte devo
sempre e cmq fare il match con uno switch per abbinare le diverse
implementazioni degli operatori no? Intendo:
int operator > (const String &s, const Object &o) {
switch(o.type()) {
  case NUMERO: return s > o.numero();
  case STRING: return s > o.string();
  case CONSTANT: return s > o.constant();
}
}
e da qualche altra parte dovrò sicuramente definire l'operatore
int operator > (const Object &x, const Object &y) {
  switch(x.type) {
    const STRING: return x.string() > y;
  }
}
Cioè è questo il mio dilemma. Che poi ... alla fin fine ... devo sempre
definirmi da qualche altra parte tutti i casi possibili!! Se io invece
utilizzo un approccio con derivazione ... a seconda del tipo devo fare un
cast per vedre l'oggetto Object per quel che è realmente!!!
Il fatto che non do tante altre informazioni in meritò e per via
dell'importanza del progetto!!! Tutto qui ... niente di +!! Anche se a dire
il vero penso che il problema possa essere tranquillamente affrontato in
maniera molto astratta a partire da quel poco che ho fornito!
Grazie per i consigli, ma vorrei capire molto di +!

Da quanto hai descritto quello che ti servirebbe e' un meccanismo di
dispatch multiplo, ovvero la possibilita' di scegliere la funzione da
chiamare effettivamente di fronte a un espressione del tipo:
A a = f(b,c);
Non solo in base al tipo effettivo di b, come avverrebbe in una normale
chiamata a membro, tipo:
A a = b->f(c);
Ma anche in base al tipo effettivo di c.
Questo genere di meccanismo non esiste nativamente in C++, ma esistono
sia estensioni del linguaggio che librerie che consentono di fare cose
simili. Un esempio e' presente su "More Effective C++", di Scott Meyers;
altrimenti puoi cercare su Web con keyword come "Multiple Dispatch" o
"Multi-methods".
Un'altra cosa che puoi fare e' scaricarti l'interprete di qualche
linguaggio che assomiglia al tuo, per vedere come il problema e' stato
risolto da altri (anche se probabilmente trovi solo implementazioni in
C).
Ciao,
Nicola


 ha scritto nel messaggio
news:3AD44DF9...@divalsim.it...


 >
Un ottimo libro che tratta di queste cose e' 
"Modern C++ Design" di Andrei Alexandrescu, Addison Wesley 2001.
Fa delle cose "incredibili" con i template e il capitolo finale e'
proprio 
dedicato ai Multi-methods.
Fabio

visto in questo argomento!
prima di scrivere ti vorrei invitare a moderare un po' i toni: sembra
che l'aiuto che stiamo cercando di darti ti sia dovuto... magari un
po' più di gentilezza...
cmq, una possibile soluzione è questa: prima di tutto definisci le
opportune conversioni:
class String {
/*...*/
public:
  operator int() { return lunghezza_stringa; };
};
poi ti fai una versione template che gestisce tutti i casi
"automatici":
template<class T, class G> bool operator<(T a, G b) { return a<b; }
(ps: c'è qualcosa di sbagliato in questo? Il DJGPP crasha...)
a questo punto ti definisci le opportune specializzazioni del tempate:
template<> 
bool operator< <String,String>(String a,String b) { return /*...*/ }
In questo modo sarà il compilatore a scegliere per te!
Anto
--
Antonio Cuni, 18 anni, studente
Brainbench Certification: 
Master C++: 4.6, higher than 98% of all previous test takers.
C Language: 3.32, higher than 79% of all previous test takers.
http://www.brainbench.com/transcript.jsp?pid=1301688

 po' più di gentilezza...
non vedo che tono sbagliato possa aver usato!! Non ho offeso nessuno
sinceramente! STavo solo tentando di spiegare un po' la natura del problema
che mi son trovato difronte! Al massimo dovresti dirlo a chi qui dentro m'ha
detto di andare dallo psicologo solo perché non conosce né la logica che sta
sotto quel linguaggio, né il significato di certe operazioni!! Ma io
sinceramente non ho insultato nessuno, tantomeno usato toni forti!! Mi
spieghi dove hai trovato un tono offensivo? o che ti ha fatto saltare i
nervi?

 importanti del vero significato del progetto!
Secondo me la ragione fondamentale e' che gli interpreti di linguaggi di
successo hanno diversi anni di eta', ovvero sono stati scritti quando il
C++ era troppo giovane. Bisogna anche considerare che si tratta di un
settore particolarmente maturo e dove esistono strumenti di automazione
orientati al C. Probabilmente cominciano a potersi trovare in giro
interpreti Java scritti in Java.
Ciao,
Nicola

 ha scritto nel messaggio
news:3AD57D51...@divalsim.it...


nervi?
mi riferivo più che altro ai primi messaggi che hai mandato, gli
ultimi IMO sono più soft; non so di preciso, ma era una mia
impressione personale: non, so, forse è a causa dei punti esclamativi
che le tua frasi sembrano forti. Cmq avrei preferito una risposta alla
mia proposta sui template, piuttosto che su questo argomento :-)


visto in questo argomento!
Boh... ti ho perso. Se stai cercando una decisione runtime
(linguaggio typeless per intenderci) e dinamica allora puoi
usare questo sistema...
  // Proxy
  class Object
  {
    public:
      std::string type() { return obj->type() }
      ...
    private:
      BaseObject *obj;
      ...
  };
  // Operatore binario astratto
  class BinaryOperator
  {
    public:
      Object operator()(const Object& a,const Object& b) const
      { return impl(a,b); }
      typedef Object (*ImplPtr)(const Object&,const Object&);
      BinaryOperator(ImplPtr i=&DefOp) : impl(i) { }
    private:
      ImplPtr impl;
      static Object DefOp(const Object&,const Object&)
      { 
        return TheNullObject();
      }
  };
  //
  // Mappa di tutti gli operatori binari (modificabile
  // anche a runtime)
  //
  std::map<std::string,BinaryOperator> binary_operators;
  //
  // Overload per permettere di scrivere a<b invocando
  // l'operatore specifico come da mappa
  //
  Object operator<(const Object& a, const Object& b)
  { return binary_operators[a.type()+"<"+b.type()](a,b); }
  Object operator<=(const Object& a, const Object& b)
  { return binary_operators[a.type()+"<="+b.type()](a,b); }
  Object operator>(const Object& a, const Object& b)
  { return binary_operators[a.type()+">"+b.type()](a,b); }
  Object operator>=(const Object& a, const Object& b)
  { return binary_operators[a.type()+">="+b.type()](a,b); }
  ...
(WARNING: codice non testato, scritto al volo e di mattina presto ;) )
In questo modo cosa significa eseguire l'operazione lo
puoi anche decidere runtime. Si tratta in pratica della
famosa "matrice" in cui puoi decidere di far puntare
diversi incroci ad una stessa implementazione.
Sofisticando un po', se esistono regole generali, si
potrebbe introdurre il concetto di regole che lavorano
come filtri in cascata... con associata ad ogni regola
la funzione di implementazione.
Se e' questa la strada che avevi in mente si puo' provare
ad investigare su come implementarla in C++...


 (linguaggio typeless per intenderci)
Infatti. Data una lista in input, io non so effettivamente che tipo di
oggetti e in quale ordine sono stati memorizzati, perché non c'é niente che
mi fa un controllo sulle informazioni inserite, questo perché appunto il
programmatore è liberissimo di fare quel che gli pare. Quindi, la funzione,
appena si trova a dover valutare una lista, deve anche riconoscere che tipo
di confronti applicare sui vari oggetti. In pratica non è detto che ho
sempre la stessa combinazione di oggetti...una volta potrei avere la coppia
String-Function, un'atra volta Number-Constant e così via.



Proverò...ma sinceramente non ho capito poi tanto il suo funzionamento.