Newsgroup: it.comp.lang.c++
Titolo: Liste simil Python

Dato che sono nuovo sul NG per prima cosa un grosso Saluto a Tutti!
Il mio problema è che vorrei implementare una lista simil Python in un 
programma C/C++! Avevo pensato a una cosa tipo : (vedi codice in fondo)
solo che i gioiosi puntatori void non mi permettono di accedere ai vari 
campi a cui puntano!Il che rende abbastanza complicata la questione nel 
caso di confronti! dovrei confrontare le aree di memoria e inizialmente 
avevo pensato di usare memcmp() ma nel caso di stringhe e co non mi 
basta confrontare la memoria(le aree di memoria potrebbero essere 
diverse ma le stringhe uguali tranne che qualche lettera è maiuscola).
Ho pensato allora a questa soluzione, salvare tutti i dati come array di 
caratteri(è possibile con ogni tipo di dato no?) e poi, sapendo che tipo 
di dati sono ricustruirli, nel caso necessiti di fare una qualche 
elaborazione. Questo mi permetterebbe di cambiare la struttura in:
typedef struct List_Element
{
	char type;
	//Questo punta all'inizio dei dati nella memoria
	char *data;
	long int dim;
}List_Element;
Con l'evidente vantaggio di lasciarmi raggiungere i valori a partire dal 
puntatore data. E' un idea perlomeno sensata? Avete suggerimenti ?
Grazie e scusate la logorrea :-)
Ciao
Wezzy
#include <vector>
using namespace std;
typedef struct List_Element
{
	char type;
	//Questo punta all'inizio dei dati nella memoria
	void *data;
}List_Element;
class PList
{
	vector<List_Element*> Vect;
	int append(void *obj, char type);
}

 puntatore data. E' un idea perlomeno sensata? Avete suggerimenti ?
In C++, non devi fare nulla del genere!
Prova a dare una occhiata ai templates!
Tommy


cut...
Ciao,
come gia' ti e' stato detto da Tommy, per programmare in C++, conviene
studiarsi la STL che ti risparmia il vecchio tipo di programmazione simil C,
con array, liste fatte a mano, puntatori a char ecc...
In particolare dovresti studiare e guardare degli esempi di come usare
vector, string, list e gli algoritmi associati.
All'inizio puo' sembrare un altro linguaggio ma ti assicuro che ne vale la
pena.
Si tratta di implementazioni di contenitori standard e di algoritmi che
altrimenti dovremmo implementare ogni volta noi a mano con pericolo di bugs
nascosti.
Al posto dei char *, con l'STL conviene usare le string che non hanno
bisogno di avere memoria allocata dinamicamente ed evitano tutti i problemi
associati ai puntatori.
Non c'e' piu' bisogno di implementarsi le liste, perche' ora c'e' list che
e' implementata a template e cioe' accetta qualsiasi oggetto o puntatore ad
esso ed ugualmente non devi allocare e disallocare.
Per qualsiasi tipo di array puoi usare vector anch'esso un template.
Per approfondimenti guarda su "Thinking in C++" di Eckel :
www.bruceeckel.com
  sono 2 volumi gratuiti scaricabili
E per un aiuto in linea ci sono:
http://www.dinkumware.com/manuals/reader.aspx?lib=cpp
e
http://www.sgi.com/tech/stl/
Maurus

 
L'ho già scaricato e me lo sto bevendo
Ciao & Grazie
Wezzy


Anche se ti disfi dei puntatori void e introduci il tipo Raw (memoria
grezza, che tu codifichi sotto forma di array di char), non risolvi la
questione.
Primo, non puoi pensare di salvare in questo modo qualsiasi tipo di
oggetto(nel senso istanza di una classe), perche' potrebbe avere come membri
delle strutture che salvate come array di char perderebbero il loro
originale allineamento e non potresti poi ricostruirlo.
Inoltre anche avendo nella lista, diversi oggetti codificati in tal modo, in
fase di ricostruzione del dato originario, ti servira' sapere che tipo di
oggetto era, per cui dovrai predisporre un campo che ti identifica l'oggetto
messo in lista, in modo tale da poter fare poi un bello switchone che ti
esamina caso per caso...un lavoraccio.
A questo punto sarebbe meglio crearsi una bella lista di puntatori ad una
certa classe Generica, e poi tu derivi da Generica tutte le tue classi di
cui hai bisogno per metterle in lista.
Quindi nella lista metterai i vari puntatori a questi oggetti vari che
derivano dalla classe Generica.
Poi in fase di recupero dati dovrai darti da fare a colpi di typeid pero'
per riconoscere il tipo di oggetto che hai messo.
Anche qua in effetti lo switchone non te lo risparmi...
Maurus

Grazie mille per i preziosi suggerimenti!
Ci lavorerň su!
Grazie
Wezzy

 qualsiasi tipo.
[...]
se sei su Windows, dai un'occhiata al tipo VARIANT di OLE e alla classe 
wrapper _variant_t dichiarata in comdef.h. Potrebbero aiutarti a capire 
in che guaio ti stai cacciando :-)
-- 
 < 
http://www.reactos.com/
 >  Open source clone of Windows NT. Current
     Don't stand, REACT!      version 0.0.21. C, C++ and ASM developers
                              and beta testers are welcome!

 sbaglio)! e ciň non va bene per il mio scopo.
molto probabilmente ti sbagli. Avere una collezione di dati non omogenei 
non serve a nulla, dato che con essi non puoi fare *nessuna* operazione.
Se tu hai intenzione di collezionare dei dati sicuramente vuoi anche 
farci qualcosa, no? Allora, pensa a cosa vuoi farci e codifica queste 
operazioni come metodi di una classe astratta; ad esempio se hai una 
lista di dati "non omogenei" ed il tuo scopo e` di stamparli a video:
class Printable
{
public:
    virtual void print() = 0;
    virtual ~Printable() { }
};
A questo punto devi creare una classe concreta per ogni tipologia di 
oggetto che vuoi memorizzare:
class PrintableInt: public Printable
{
    int value;
public:
   PrintableInt(int v): value(v) { }
   void print() { std::cout << value << std::endl; }
};
class PrintableDouble: public Printable
{
    double value;
public:
   PrintableDouble(double v): value(v) { }
   void print() { std::cout << value << std::endl; }
};
Nota che in questa fase potrebbero esserti di aiuto i template, a seconda 
di quanto sono omogenei i tuoi dati non omogenei ;-)
template<class T>
class PrintableT: public Printable
{
    T value;
public:
   PrintableT(T v): value(T) { }
   void print() { std::cout << value << std::endl; }
};
typedef PrintableT<int> PrintableInt;
typedef PrintableT<double> PrintableDouble;
Con questa infrastruttura la tua "lista simile a python" e` presto fatta:
std::vector<Printable*> mylist;
La differenza tra un linguaggio statico come il C++ e uno dinamico come 
Python sta proprio nella necessita` di dover scrivere "a mano" tutta 
l'infrastruttura necessaria per far funzionare il programma.
Nota che la soluzione di cui parlo in questo post e` la stessa adottata 
da python, solo che e` "mascherata" da una sintassi gradevole; 
concettualmente le liste di python corrispondono a qualcosa di simile a 
questo:
class PyObject
{
public:
    virtual PyObject GetAttr(std::string) = 0;
    virtual void SetAttr(std::string, PyObject*) = 0;
    // altre operazioni
};
std::vector<PyObject*> pythonList;
Ovviamente python non e` implementato cosi` (per i dettagli e` meglio 
leggersi direttamente i sorgenti), pero` dovrebbe rendere l'idea.
ciao Anto
-- 
"Computer science is not about computers any more than astronomy
is about telescopes." -- EW Dijkstra

 non serve a nulla, dato che con essi non puoi fare *nessuna* operazione.
tranne che contarli  ed individuare ripetizioni :-)
--------
Sergio Bianchi

 tranne che contarli  ed individuare ripetizioni :-)
Non puoi fare neanche questo, perche' se i dati sono eterogenei non
sono confrontabili per definizione. Definisci il criterio di
confronto, e da questo presumibilmente derivera' il disegno piu'
logico per il tuo contenitore.
Ciao,
Nicola

"Nicola Musatti" <Nicola....@ObjectWay.it> ha scritto nel messaggio
news:a327cf48.03011...@posting.google.com...

Non sono d'accordo. I dati presenti in memoria, qualunqe essi siano, sono
sempre referenziabili con un puntatore a void.
Una collezione di tali puntatori (un contenitore in cui posso inserirli) mi
permette di contarli e di verificare se nel contenitore è già presente il
medesimo puntatore.
-----
Sergio Bianchi

 medesimo puntatore.
Ammesso che due oggetti che hanno puntatori diversi siano sempre diversi.
Ovvero, ammesso che il puntatore alla zona di memoria occupata
dall'oggetto renda unico l'oggetto a prescindere dal suo contenuto reale.
Un esempio contrario a questo è il seguente:
int a = 10, b = 5, c = 10;
void * pa = &a, *pb = &b, *pc = &c;
pc != pa anche se c == a!
Ciao.
*** Azimuth ***

 puntatore.
Certo. Bisogna però non dimenticare che, in C++, se per due puntatori è
vera la seguente espressione
     p1 == p2
non è detto che valga anche
     (void*)p1 == (void*)p2
Max


<CUT>
Certo, ho parlato di confronto del puntatore ma avrei dovuto dire che era
possibile solo questo tipo di confronto. Sono stato probabilmente troppo
sintetico.
La mia era solo una breve precisazione a fronte di un categorico "non si può
fare nulla" che mi ha spinto a pensare fino a dove si poteva "spremere il
limone". In questa ottica vorrei comunque invitare il newsgroup ad evitare
approfondimenti sulla verifica , a partire da due puntatori , di quanti byte
a seguire la memoria coincide ;-).
Ciao a tutti.
----
Sergio Bianchi

      (void*)p1 == (void*)p2
In particolare, se una classe ha piu' classi base, a maggior ragione
se con eredita' virtuale, e' possibile che due puntatori a classi
diverse che puntano allo stesso oggetto abbiano valori diversi. In
altre parole se
class A {};
class B {};
class C : public B, public virtual A {};
C c;
A * pa = &c;
B * pb = &c;
Non e' detto che (void*)pa == (void*)pb .
Ciao,
Nicola

 altre parole se
A riprova di quello che dici:
<codice>
#include <iostream>
class base {
};
class A_base : public base {
};
class B_base : public base {
};
class last : public virtual A_base, public virtual B_base {
};
int main() {
         last I1;
         last *p1=&I1;
         A_base *p2=&I1;
         B_base *p3=&I1;
         base *p4=&I1;
         std::cout << "last*  :" << static_cast<void*>(p1) << std::endl;
         std::cout << "A_base*:" << static_cast<void*>(p2) << std::endl;
         std::cout << "B_base*:" << static_cast<void*>(p3) << std::endl;
         std::cout << "base*  :" << static_cast<void*>(p4) << std::endl;
}
</codice>
da come output (linux,g++ 2.95.4: g++ file.cpp):
<output>
last*  :0xbffff870
A_base*:0xbffff878
B_base*:0xbffff879
base*  :0xbffff878
</output>
a proposito della derivazione virtuale, la classe base deve essere
inizializzata in quella piu' derivata....
il costruttore di last come sarebbe stato?
ciao
david

 david
Essendo tutte le classi base dotate di costruttore di default il
problema non dovrebbe porsi. In ogni caso basta aggiungere
l'inizializzazione esplicita:
last::last() : base() {}
Ciao,
Nicola