Wow, un sacco di letture e nessuna risposta.
Temo che con questo codice ho beccato le sfige peggiori del C++.
A parte che ci sarebbe da frustare a sangue le mani del programmatore che ha scritto la parte degli errori del compilatore (perchè, per zeus, se non va un template mi dai un errore di linking generico?) poi non sono stato con le mani in mano.
Ho scoperto tante belle cose, anzitutto il problema riguarda i template in generale e non la ridefinizione dell'operatore come pensavo io.
Poi il problema è che separare dichiarazione e definizione dei template provoca disastri immani, catastrofi naturali, nausea, vomito e diarrea.
Bene, secondo C++ Faq Lite (vedi post precedente) e B.Stroustrup separare i template in due files (header e file cpp) provoca problemi in quanto il compilatore non sa bene come gestire un template perchè per poterlo compilare e linkare a dovere ha bisogno di conoscere la sua implementazione.
Le soluzioni quindi sono varie: fare un unico e quindi usare dichiarazione e definizione senza separarli in un header e in un file cpp sembra essere la più adatta. Lo svantaggio è che chi usa il file vede l'implementazione, la compilazione rallenta e sopratutto l'header diventa mostruoso.
Mettiamo che ci sia qualcuno testardo (tipo me) che vuole comunque provare a separare dichiarazione e definizione.
Bene, per i template esiste una keyword ovvero
export che serve proprio per la separazione dichiarazione/definizione dei template. Fantastico no? No: la keyword non è supportata da quasi nessun compilatore e il comitato dello standard C++ non la ha mai definito per bene e quindi è inutile (ho letto che forse introdurranno un sistema simile nel prossimo standard C++ 0x).
Secondo Stroustrup una soluzione è quella di inserire una dichiarazione di specializzazione di template nel file cpp per forzare il compilatore a inserire header e file cpp in una unica unità di compilazione... ovvero aggiungere, per esempio nella mio file ArrayMatrix.cpp, la dichiarazione:
template class ArrayMatrix<int>;
In questo modo gli errori sui template sono spariti.
Daccordo, c'erano una marea di altri errori (che ho corretto) e credo di aver perso un bello scivolone col metodo
pop() (mi sa che quel che volevo fare non si può fare, non così almeno).
Tralasciando questi inconvenienti ho rimodificato i vari files togliendo alcune cose e rendendo il codice più simile a quello del C++ Faq-Lite e facendo volare il comportamento di
pop() che per ora non mi serve.
Bene, adesso non ho più tutti gli errori su ArrayMatrix!
Aleeee!
Adesso me ne spuntano invece altri su Matrix.h -_-
Decisamente io e i template abbiamo litigato.
Vi posto l'errore e i files modificati:
Building target: testVari
Invoking: GCC C++ Linker
g++ -o"testVari" ./src/ArrayMatrix.o ./src/TestSingleton.o ./src/VectorMatrix.o ./src/testVari.o
./src/testVari.o: In function `~ArrayMatrix':
/mnt/data/opt/workspace/testVariC++/Debug/../src/ArrayMatrix.cpp:60: undefined reference to `Matrix<Prova>::~Matrix()'
/mnt/data/opt/workspace/testVariC++/Debug/../src/ArrayMatrix.cpp:60: undefined reference to `Matrix<Prova>::~Matrix()'
./src/testVari.o: In function `ArrayMatrix':
/mnt/data/opt/workspace/testVariC++/Debug/../src/ArrayMatrix.cpp:23: undefined reference to `Matrix<Prova>::~Matrix()'
./src/testVari.o:(.rodata._ZTV6MatrixI5ProvaE[vtable for Matrix<Prova>]+0x8): undefined reference to `Matrix<Prova>::~Matrix()'
./src/testVari.o:(.rodata._ZTV6MatrixI5ProvaE[vtable for Matrix<Prova>]+0xc): undefined reference to `Matrix<Prova>::~Matrix()'
collect2: ld returned 1 exit status
make: *** [testVari] Error 1
Matrix.h:
#ifndef MATRIX_H_
#define MATRIX_H_
#include "Eccezione.h"
template<typename T> class Matrix {
public:
Matrix(unsigned int rows, unsigned int cols) throw (Eccezione)
: _righe(rows), _colonne(cols) {}
virtual ~Matrix();
virtual T& operator()(unsigned int row, unsigned int col) throw (Eccezione) = 0;
virtual void purge(unsigned int row, unsigned int col) throw (Eccezione) = 0;
virtual T& pop(unsigned int row, unsigned int col) throw (Eccezione) = 0;
private:
const T& operator[](int);
protected:
unsigned int _righe;
unsigned int _colonne;
};
class BadSize : public Eccezione
{
public:
BadSize(const std::string& messaggio) : Eccezione(messaggio) {}
};
class IndexOutOfBound : public Eccezione
{
public:
IndexOutOfBound(const std::string& messaggio) : Eccezione(messaggio) {}
};
#endif /* MATRIX_H_ */
(Eccezione.h è rimasto inalterato)
ArrayMatrix.h:
/*
* ArrayMatrix.h
*
* Created on: 15/ago/2009
* Author: blueangel
*/
#ifndef ARRAYMATRIX_H_
#define ARRAYMATRIX_H_
#include "Matrix.h"
template<typename T> class ArrayMatrix : public Matrix<T>
{
public:
inline ArrayMatrix(unsigned int rows, unsigned int cols) throw (Eccezione);
inline ~ArrayMatrix();
inline T& operator()(unsigned int row, unsigned int col) throw (Eccezione);
inline void purge(unsigned int row, unsigned int col) throw (Eccezione);
inline T& pop(unsigned int row, unsigned int col) throw (Eccezione);
private:
T* _data;
};
ArrayMatrix.cpp:
[code]
#include "ArrayMatrix.h"
template<typename T>ArrayMatrix<T>::ArrayMatrix(unsigned int rows, unsigned int cols)
throw (Eccezione) : Matrix<T>(rows, cols)
{
if (rows == 0 || cols == 0)
throw BadSize("Impossile istanziare una Matrice di 0 righe o colonne.\n");
_data = new T[Matrix<T>::_righe * Matrix<T>::_colonne];
}
template<typename T> T& ArrayMatrix<T>::operator()(unsigned int row, unsigned int col) throw (Eccezione)
{
if (row > Matrix<T>::_righe || col > Matrix<T>::_colonne)
throw IndexOutOfBound("Indice di riga o colonna non valido");
return _data[row * Matrix<T>::_colonne + col];
}
template<typename T> void ArrayMatrix<T>::purge(unsigned int row, unsigned int col) throw (Eccezione)
{
T p = this->operator ()(row, col);
delete &p;
}
template<typename T> T& ArrayMatrix<T>::pop(unsigned int row, unsigned int col) throw (Eccezione)
{
if (row > Matrix<T>::_righe || col > Matrix<T>::_colonne)
throw IndexOutOfBound("Indice di riga o colonna non valido");
T& retval = _data[row * Matrix<T>::_colonne + col];
//_data[row * Matrix<T>::_colonne + col] = NULL;
return retval;
}
template<typename T> ArrayMatrix<T>::~ArrayMatrix()
{
delete[] _data;
}
#endif /* ARRAYMATRIX_H_ */
[/code]
ed infinte testVari.cpp:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include "ArrayMatrix.h"
#include "ArrayMatrix.cpp"
using namespace std;
static const string test = "test";
string stringhifica(int valore);
class Prova
{
public:
Prova()
{
static int i = 0;
myI = i++;
string test = "Test n.: ";
cout << test << stringhifica(myI) << "\n";
}
inline const int getMyI() const { return myI; }
private:
int myI;
};
int main()
{
ArrayMatrix<Prova> matrice(5,4);
Prova p1 = matrice(0,0);
Prova p2 = matrice(2,3);
cout << "Ok, ora li riprendo:\n";
int valore = p2.getMyI();
//cout << "E il valore ritornato è: " << stringhifica(valore) << "\n";
return 0;
}
string stringhifica(int valore)
{
ostringstream o;
o << valore;
return o.str();
}
Potete vedere dal codice che ho unito un po' di soluzioni: quella di Stroustroup (non ho il libro per mano, comunque capitolo 16: Templates) e quella del C++ Faq Lite.
Stroustroup dice di inserire una dichiarazione di specializzazione del template (
template class ArrayMatrix<int>) mentre C++ Faq Lite di rendere i metodi inline.
Temo pure di aver travisato: mi sa che Stroustroup intendesse di inserire la dichiarazione di specializzazione per OGNI utilizzo possibile del template... spero non sia così altrimenti a ******* i template, usati così non servono a una mazza.
Altrettanto col C++ Faq Lite: ho reso inline tutto.
Vedete l'include a ArrayMatrix.cpp? Beh questa l'ho letta in un forum (perso il link, mi dispiace), ebbene con questa
include l'errore lo da su Matrix mentre se lo tolgo mi da gli errori che vi ho scritto nel post di prima.
In sintesi: come diamine uso i template?
Mi piace ancora il C++, è grandioso, ma non ho così tanto tempo da perdere in seghe mentali.