Newsgroup: it.comp.lang.c++
Titolo: scrivere una classe complex portabile

Saluti a tutti.
Devo interfacciarmi a delle funzioni in C (la 
libreria CBLAS) in cui, per questioni di portabilita',
i numeri complessi sono gestiti come void*.
Assodato che la std::complex e' inusabile (anche perche'
non mi fido complemetamente di come sono implementate
le varie funzioni matematiche ) ho il problema di
scrivere una classe in modo portabile.
Scartata la scelta
class complex
{
    float re;
    float im;
};
resta da fare
class complex
{
    float v[2];
    // ...
};
Putroppo non e' detto che un array di complex abbia
gli elementi contigui in memoria come dovrebbe.
Inoltre si deve considerare il fatto che una classe
complex 'conveniente' non e' un POD.
Quindi in C++ non ci sono soluzioni (e questo credo sia
un grave limite del linguaggio).
Forse si puo' fare qualcosa usando delle varie estensioni
per 'impacchettare' la struttura, ma non saprei.
Credo che servirebbe una estensione al C++, una nuova keyword
'interface' usata in questo modo:
class complex: interface float[2]
{
    //...
};
Questa scrittura significa che complex e' in realta'
*completamente* equivalente ad un float[2], ed il cui
unico scopo e' di fornire una interfaccia (costruttori,
overloading degli operatori, etc).
Cosa ne pensate?
Grazie  Manlio Perillo

 Cosa ne pensate?
Non c'è nessun bisogno di aggiungere nuove semantiche a un linguaggio già
enorme quando si possono risolvere i problemi diversamente.
Il miglior consiglio che ti possa dare -- e che tutti imparano presto -- è di
sempre supporre che la colpa sia tua e non del linguaggio. Così, avrai
praticamente sempre raggione. E poi anche se un giorno tu avessi un idea
veramente geniale per cambiare il linguaggio (e non è il caso qui), hai proprio
il tempo di aspettare che sia studiata, poi aspettata, poi finalmente
incorporata in una prossima versione del tuo compilatore? È molto più utile
risolvere il vero problema che far filosofia. :-)
Claudio Puviani

 Cosa ne pensate?
Che non ce n'e' affatto bisogno, perche' con un poco di cautela non e'
difficile mantenere la "PODdita'" di una classe e questo e' tutto quel
che ti serve.
Ciao,
Nicola


Vedi sopra.
Saluti e grazie  Manlio Perillo


Questo problema non l'ho notato io, e' presente nel 'rationale' per
l'interfaccia
della libreria che devo usare.
In particolare la libreria si chiama CBLAS ed e' una interfaccia in C per la
libreria
BLAS (Basic Linear Algebra Support).
La libreria BLAS e' stata originariamente scritta in Fortran.
La 'reference implementation' della CBLAS e' pensata come wrapper per la
libreria
in Fortran, anche se esistono versioni di CBLAS scritte direttamente in C.


 Grazie  Manlio Perillo
ciao,
 Sbirish

 Invece e' detto.
cerco di interpretare il pensiero di Manlio: lo standard ci assicura che 
i vari complex siano contigui, ma in questo caso il problema e` 
rappresentato dall'eventuale padding del compilatore: quindi se abbiamo:
complex array[10];
void* ptr = (void*) array;
non siamo sicuri che tutti i float siano contigui in memoria e magari la 
libreria non puo` funzionare per questo motivo; e` questo che intendevi, 
Manlio?
Penso che la soluzione piu` semplice sia di scriversi una classe 
ArrayOfComplex e usare quella in luogo dei normali array; mi vengono in 
mente due possibili implementazioni:
1) memorizzare internamente un vero e proprio array di complex (oppure 
usare vector, a seconda dei casi): quando l'utente richiede la 
conversione a void* la classe copia in una opportuna zona di memoria 
tutti i complex in modo che siano contigui.
2) memorizzare internamente un array di float (quindi siamo sicuri che 
sono contigui) e creare "al volo" una istanza di complex quando si cerca 
di accedere ad un elemento.
A naso mi pare che la soluzione (2) sia la migliore, pero` dipende dalla 
situazione.
ciao Anto
-- 
"Computer science is not about computers any more than astronomy
is about telescopes." -- EW Dijkstra


La 2 mi sembra una buona idea, provero' a lavorarci su.



Semplice: la libreria gestisce i numeri complessi come void* mentre io
voglio usare una classe complex con overloading di funzioni ed operatori
matematici.


 tutti i float siano contigui in memoria.
In questo caso un compromesso potrebbe essere passare l'indirizzo
della parte reale come argomento al costruttore della tua classe,
magari utilizzando una factory, per nascondere il momento delicato
dell'attribuzione di segmenti dell'array utilizzato dalla libreria ad
istanze della tua classe. Ad es.:
class MyComplex {
  public:
    MyComplex(float * f) : data(f) {}
  private:
    float * data;
};
float cArray[20];
char * myArray = new sizeof(MyComplex * 10);
MyComplex * p = myArray;
for ( int i = 0; i < 10; ++i )
{
  p = new(p) MyComplex(cArray + i * 2);
}
Ciao,
Nicola

 La 2 mi sembra una buona idea, provero' a lavorarci su.
provo a scrivere una prima implementazione "al volo", senza testing:
class ArrayOfComplex;
class ComplexProxy
{
private:
     float& real;
     float& imag;
     ComplexProxy(float& r, float& i): real(r), imag(i) { }
     friend class ArrayOfComplex;
public:
     operator Complex() { return Complex(real, imag); }
     ComplexProxy& operator=(const Complex& num)
     {
          real = num.real();
          imag = num.imag();
     }
};
class ArrayOfComplex
{
private:
    std::vector<float> array;
public:
    ArrayOfComplex(void* ptr, size_t n):
         array( (float*)ptr, (float*)ptr+n ) { }
    ArrayOfComplex(float* ptr, size_t n):
         array( ptr, ptr+n ) { }
    // altri ctor
   ComplexProxy operator[](size_t n)
   {
         return ComplexProxy(array[n], array[n+1]);
    }
   // altri metodi
};
cosi` dovrebbe andare, ma non l'ho testato.


 >
Prendendo spunto dal tuo suggerimento ho trovato una soluzione
abbastanza soddisfacente.
Semplicemente definisco per la mia classe vettore una specializzazione
per complex. Inoltre definisco opportunamente iterator, const_iterator,
reference e const_reference (ho dato alla mia classe una interfaccia
alla STL).
In questo modo non c'e' piu' nessun problema di portabilita'.
Resta la cosa importante di definire complex come sopra (poi se c'e'
o no del paddind prima(?) o dopo v[2] - ossia se sizeof(complex) != 2 *
sizeof(float) - non importa).
Tutto questo mi fa pensare comunque al perche' la commisione per lo Standard del
C++ non abbia pensato a questo problema e richiesto che std::complex sia
implementato in quel modo.
Stesso problema l'ho notato per std::valarray: cosi' come e' non e' molto
usabile.


 In questo modo non c'e' piu' nessun problema di portabilita'.
occhio che pero` cadi nella stessa "trappola" di std::vector<bool>: devi 
per forza usare un proxy come risultato di operator[]. Ecco cosa dice 
Meyers a proposito di vector<bool> (Effective STL, item 18):
"""
As an STL container, there are really two things wrong with vector<bool>. 
First, it's not an STL container. Second, it doesn't hold bools.
[...]
If vector<bool> is to be a container, this code must compile:
   vector<bool> v;
   bool *pb = &v[0];
But it won't compile. [...]
[...] they decided to develop vector<bool> as a demonstration of how the 
STL could support containers whose elements are accessed via proxies.
[...] Alas, what they discovered was thati it was *not* possible to 
create proxy-based containers that satisfy all the requirements of STL 
containers. [...]
"""
Quindi, occhio che vector<complex>, cosi` come vector<bool>, non sara` 
completamente compatibile con gli altri contenitori STL.



La specializzazione template parziale vector< complex<T> >
alloca un array di float (di dimensione 2 n).
In questo modo e' tutto perfettamente portabile (o almeno spero!)


 ha scritto nel messaggio
news:sbko1b...@anto.cuni.lan...

La mia implementazione e' abbastanza differente;
Innanzitutto la classe complex e' definita cosi':
class complex
{
    float dat_[2];
    // ...
};
Poi, siccome i problemi derivano dai vettori di complessi
ho specializzato la mia classe vector per complex.
In particolare ho definito una reference ed un iteratore
opportuni)
namespace impl // per non definirla dentro vector
{
    class complex_reference
    {
        float* dat_;
        // ...
    };
}
e cosi' via.


 ha scritto nel messaggio
news:m4uq1b...@anto.cuni.lan...

Grazie per l'avviso.
Lo so che ci sono questi problemi ma non importa.
Ho usato una interfaccia alla STL solo perche' e' 'funzionale'.
Del resto senza usare una classe proxy non e' possibile creare
del codice portabile.
Volendo essere pignoli, posso anche fare in modo che del codice tipo
    complex* pc = &v[0];
compili, non ci dovrebbero essere problemi.
Basta infatti ridefinire operator&.
Ci devo pensare perche' non so se vale veramente la pena.


 Basta infatti ridefinire operator&.
uhm... non e` cosi` semplice: l'implementazione banale porta ad un memory 
leak:
class ComplexProxy {
public:
    ....
    complex* operator&() {
          return new complex(...);
    }
};
Bisognerebbe trovare un modo per garantire che i complex* restituiti da 
operator& siano distrutti quando e` distrutto il vector<> originale. 
Penso si possa fare, ma mi pare un po' troppo astruso.
In ogni caso non credo ne valga la pena...



e se invece di usare complex * nudo e crudo si usassero gli smart pointers?
Maurus

 pointers?
il problema e` sempre lo stesso; se io scrivo:
complex* ptr = &array[0];
verrebbe creato uno smart pointer temporaneo che verrebbe poi convertito 
in complex*: pero` quando il temporaneo viene distrutto deve liberare la 
memoria, e ptr diviene un puntatore invalido.
