PDA

Visualizza la versione completa : [C++] Undefined symbols for architecture x86_64


RooccoXXI
01-06-2012, 18:22
Ciao a tutti.
Sto provando a scrivere un template di classi che rappresenterebbero dei vettori nel senso matematico del termine. Questa classe eredita direttamente da std::vector<T>.
Tutto sembra andare per il meglio quando compilo con un main() vuoto, ma appena cerco di instanziare ed utilizzare la mia classe ottengo un errore strano:


Undefined symbols for architecture x86_64:
"Vector<int>::Vector(unsigned long)", referenced from:
_main in testVector.o
"std::basic_ostream<char, std::char_traits<char> >& operator<< <int>(std::basic_ostream<char, std::char_traits<char> >&, Vector<int> const&)", referenced from:
_main in testVector.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make: *** [testVector] Error 1
macbook-di-rocco-meli:Simulation roccomeli$ make clean
rm -f *.o
macbook-di-rocco-meli:Simulation roccomeli$ make
g++gcc-4.6.x -std=c++0x -c -o testVector.o testVector.cc
g++gcc-4.6.x -std=c++0x -c -o Vector.o Vector.cc
g++gcc-4.6.x testVector.o Vector.o -o testVector
Undefined symbols for architecture x86_64:
"Vector<int>::Vector(unsigned long)", referenced from:
_main in testVector.o
"std::basic_ostream<char, std::char_traits<char> >& operator<< <int>(std::basic_ostream<char, std::char_traits<char> >&, Vector<int> const&)", referenced from:
_main in testVector.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make: *** [testVector] Error 1

È come se il compilatore non trova l'implementazione della classe ma davvero non capisco come mai.

Ecco il mio codice:

CC = g++gcc-4.6.x
CXX = g++gcc-4.6.x
CXXFLAGS = -std=c++0x

all: testVector

# --- TEST VECTOR ---
testVector: testVector.o Vector.o
testVector.o: testVector.cc Vector.h

# --- CLASSES ---
Vector.o: Vector.h Vector.cc

clean:
rm -f *.o


#include "Vector.h"

#include <iostream>
using std::cout;
using std::endl;

#include <vector>
using std::vector;

#include <initializer_list>
using std::initializer_list;

int main()
{
vector<double> V({0,1,2,3,4,5});
initializer_list<double> I({5,4,3,2,1,0});

// --- TEST CONSTRUCTEURS ---

Vector<int> v1(6);
cout << v1 << endl;
/*
Vector<int> v2(0.1,1.1,1.2);
cout << v2 << endl;

Vector< > v3(V);
cout << v3 << endl;

Vector< > v4(I);
cout << v4 << endl;

Vector< > v5(v3);
cout << v5 << endl;

// --- TEST OPERATEURS ---
cout << v2[0] << ' ' << v2[1] << ' ' << v3[2] << endl;

v5 = v4;
cout << v5 << endl;

if(v5 == v4){cout << v5 << " == " << v4 << endl;}
else{cout << v5 << " != " << v4 << endl;}

if(v5 != v3){cout << v5 << " != " << v4 << endl;}
else{cout << v5 << " == " << v4 << endl;}
*/

return 0;
}


#include "Vector.h"

#include<stdexcept>
#include<algorithm>
#include<iterator>

// *** PUBLIC ***
// --- CONSTRUCTORS ---

// Empty vector
template<typename T>
Vector<T>::Vector(size_t size)
: std::vector<T>(size, 0)
{}

// 3D vector
template<typename T>
Vector<T>::Vector(T x, T y, T z)
: std::vector<T>({x, y, z})
{}

template<typename T>
Vector<T>::Vector(const std::vector<T> & V)
: std::vector<T>(V)
{}

template<typename T>
Vector<T>::Vector(const std::initializer_list<T>& V)
: std::vector<T>(V)
{}

template<typename T>
Vector<T>::Vector(const Vector<T>& V)
{
if(*this != V)
{
*this = V;
}
}

// --- INNER OPERATORS ---
template<typename T>
bool Vector<T>::operator==(const Vector<T>& V) const
{
bool b(true);

range_exception(V, "");

for(auto i : *this)
{
if(*this[i] != V[i])
{
b = false;
break;
}
}

return b;
}

template<typename T>
bool Vector<T>::operator!=(const Vector<T>& V) const
{
range_exception(V, "");

return !(*this == V);
}

// --- EXTERNAL OPERATORS ---

template<typename T>
std::ostream& operator<<(std::ostream& out, const Vector<T>& V)
{
std::copy(V.begin(), V.end(), std::ostream_iterator<T>(out, " "));

return out;
}

// *** PRIVATE ***
template<typename T>
void Vector<T>::range_except(const Vector<T>& V, const std::string& s) const
{
if(*this.size() != V.size())
{
throw std::range_error(s);
}
}


#ifndef VECTOR_H
#define VECTOR_H

#include <vector>
#include <initializer_list>
#include <iostream>
#include <string>

template<typename T = double>
class Vector : public std::vector<T>
{
public:
// --- CONSTRUCTORS ---
Vector(size_t);
Vector(T, T, T);
Vector(const std::vector<T> &);
Vector(const std::initializer_list<T>&);
Vector(const Vector<T>&);

// --- INNER OPERATORS ---
bool operator==(const Vector<T>&) const;
bool operator!=(const Vector<T>&) const;

private:
// --- METHODS ---
void range_except(const Vector<T>&, const std::string&) const;
};

// --- EXTERNAL OPERATORS
template<typename T>
std::ostream& operator<<(std::ostream&, const Vector<T>&);

#endif

Who am I
01-06-2012, 18:41
Prova ad aggiungere il flag -m32 .

shodan
01-06-2012, 18:52
Il vero problema è che hai definito il tuo Vector in un file .cpp.
Il codice del template deve stare in un .h, altrimenti il linker non trova i metodi istanziati dal compilatore.

RooccoXXI
01-06-2012, 19:24
Originariamente inviato da shodan
Il vero problema è che hai definito il tuo Vector in un file .cpp.
Il codice del template deve stare in un .h, altrimenti il linker non trova i metodi istanziati dal compilatore.

È proprio questo il problema. Grazie mille! Molto strano che non sia stato scritto sulle diapositive del corso...

Ma allora che ne è della separazione interfaccia/implementazione?

shodan
01-06-2012, 19:59
I template sono un'eccezione al paradigma, insieme alle funzioni dichiarate inline.

RooccoXXI
01-06-2012, 20:15
Originariamente inviato da shodan
I template sono un'eccezione al paradigma, insieme alle funzioni dichiarate inline.

Ok. Grazie mille! =)

Ho modificato come dici mettendo le definizioni nel file .h. Ottengo però gli errori seguenti, che non credo di capire... Ho già fatto questa classe senza templare ed era filato tutto liscio... =(


In file included from testVector.cc:1:0:
Vector.h: In member function ‘bool Vector<T>::operator==(const Vector<T>&) const [with T = double]’:
testVector.cc:41:11: instantiated from here
Vector.h:82:3: error: array subscript is not an integer
Vector.h: In member function ‘void Vector<T>::range_except(const Vector<T>&, const string&) const [with T = double, std::string = std::basic_string<char>]’:
Vector.h:78:2: instantiated from ‘bool Vector<T>::operator==(const Vector<T>&) const [with T = double]’
testVector.cc:41:11: instantiated from here
Vector.h:114:2: error: request for member ‘size’ in ‘this’, which is of non-class type ‘const Vector<double>* const’
make: *** [testVector.o] Error 1

Il codice é il seguente:


CC = g++gcc-4.6.x
CXX = g++gcc-4.6.x
CXXFLAGS = -std=c++0x

all: testVector

# --- TEST VECTOR ---
testVector: testVector.o Vector.o
testVector.o: testVector.cc Vector.h

# --- CLASSES ---
Vector.o: Vector.h

clean:
rm -f *.o


#include "Vector.h"

#include <iostream>
using std::cout;
using std::endl;

#include <vector>
using std::vector;

#include <initializer_list>
using std::initializer_list;

int main()
{
vector<double> V({0,1,2,3,4,5});
initializer_list<double> I({5,4,3,2,1,0});

// --- TEST CONSTRUCTEURS ---

Vector<int> v1(6);
cout << v1 << endl;

Vector<double> v2(0.1,1.1,1.2);
cout << v2 << endl;

Vector< > v3(V);
cout << v3 << endl;

Vector< > v4(I);
cout << v4 << endl;

Vector< > v5(v3);
cout << v5 << endl;

// --- TEST OPERATEURS ---
cout << v2[0] << ' ' << v2[1] << ' ' << v3[2] << endl;

v5 = v4;
cout << v5 << endl;

if(v5 == v4){cout << v5 << " == " << v4 << endl;}
else{cout << v5 << " != " << v4 << endl;}

if(v5 != v3){cout << v5 << " != " << v4 << endl;}
else{cout << v5 << " == " << v4 << endl;}

return 0;
}


#ifndef VECTOR_H
#define VECTOR_H

#include <vector>
#include <initializer_list>
#include <iostream>
#include <string>
#include<stdexcept>
#include<algorithm>
#include<iterator>

template<typename T = double>
class Vector : public std::vector<T>
{
public:
// --- CONSTRUCTORS ---
Vector(size_t);
Vector(T, T, T);
Vector(const std::vector<T> &);
Vector(const std::initializer_list<T>&);
Vector(const Vector<T>&);

// --- INNER OPERATORS ---
bool operator==(const Vector<T>&) const;
bool operator!=(const Vector<T>&) const;

private:
// --- METHODS ---
void range_except(const Vector<T>&, const std::string&) const;
};

// --- EXTERNAL OPERATORS
template<typename T>
std::ostream& operator<<(std::ostream&, const Vector<T>&);

#endif

// *** PUBLIC ***
// --- CONSTRUCTORS ---

// Empty vector
template<typename T>
Vector<T>::Vector(size_t size)
: std::vector<T>(size, 0)
{}

// 3D vector
template<typename T>
Vector<T>::Vector(T x, T y, T z)
: std::vector<T>({x, y, z})
{}

template<typename T>
Vector<T>::Vector(const std::vector<T> & V)
: std::vector<T>(V)
{}

template<typename T>
Vector<T>::Vector(const std::initializer_list<T>& V)
: std::vector<T>(V)
{}

template<typename T>
Vector<T>::Vector(const Vector<T>& V)
{
if(*this != V)
{
*this = V;
}
}

// --- INNER OPERATORS ---
template<typename T>
bool Vector<T>::operator==(const Vector<T>& V) const
{
bool b(true);

range_except(V, "");

for(auto i : *this)
{
if(*this[i] != V[i])
{
b = false;
break;
}
}

return b;
}

template<typename T>
bool Vector<T>::operator!=(const Vector<T>& V) const
{
range_except(V, "");

return !(*this == V);
}

// --- EXTERNAL OPERATORS ---

template<typename T>
std::ostream& operator<<(std::ostream& out, const Vector<T>& V)
{
std::copy(V.begin(), V.end(), std::ostream_iterator<T>(out, " "));

return out;
}

// *** PRIVATE ***
template<typename T>
void Vector<T>::range_except(const Vector<T>& V, const std::string& s) const
{
if(*this.size() != V.size())
{
throw std::range_error(s);
}
}

Ps: cosa ne pensate del codice? Lo utilizzereste o...? =)

shodan
02-06-2012, 00:16
Il problema dovrebbe essere qui. In questo modo recuperi il valore di *this, non l'indice. Per cui non puoi accedere a una locazione del vector tramite un double.
In altre parole alla prima iterazione hai il valore di (*this)[0], alla seconda (*this[1]) etc.



for(auto i : *this)


Se vuoi effettuare una comparazione degli elementi meglio usare std::equal
http://en.cppreference.com/w/cpp/algorithm/equal
avendo cura che i due range siano si uguale lunghezza, altrimenti va da se che sono diversi.



Ps: cosa ne pensate del codice? Lo utilizzereste o...? =)

o... :ecco:

RooccoXXI
02-06-2012, 00:48
Originariamente inviato da shodan
Il problema dovrebbe essere qui. In questo modo recuperi il valore di *this, non l'indice. Per cui non puoi accedere a una locazione del vector tramite un double.
In altre parole alla prima iterazione hai il valore di (*this)[0], alla seconda (*this[1]) etc.



for(auto i : *this)


Se vuoi effettuare una comparazione degli elementi meglio usare std::equal
http://en.cppreference.com/w/cpp/algorithm/equal
avendo cura che i due range siano si uguale lunghezza, altrimenti va da se che sono diversi.


o... :ecco:

Ho cambiato il codice dalla funzione in questo:

template<typename T>
bool Vector<T>::operator==(const Vector<T>& V) const
{
bool b(true);

if(*this.size() != V.size())
{
b = false;
}
else
{
b = equal(*this.begin(), *this.end(), V.begin());
}

return b;
}

Ma mi da questo errore:

In file included from testVector.cc:1:0:
Vector.h: In member function ‘bool Vector<T>::operator==(const Vector<T>&) const [with T = double]’:
testVector.cc:41:11: instantiated from here
Vector.h:78:2: error: request for member ‘size’ in ‘this’, which is of non-class type ‘const Vector<double>* const’
Vector.h:84:3: error: request for member ‘begin’ in ‘this’, which is of non-class type ‘const Vector<double>* const’
Vector.h:84:3: error: request for member ‘end’ in ‘this’, which is of non-class type ‘const Vector<double>* const’
Vector.h: In member function ‘void Vector<T>::range_except(const Vector<T>&, const string&) const [with T = double, std::string = std::basic_string<char>]’:
Vector.h:93:2: instantiated from ‘bool Vector<T>::operator!=(const Vector<T>&) const [with T = double]’
testVector.cc:44:11: instantiated from here
Vector.h:112:2: error: request for member ‘size’ in ‘this’, which is of non-class type ‘const Vector<double>* const’
make: *** [testVector.o] Error 1

Sembra come se non ha ereditato correttamente i metodi di std::vector... In pratica é l'ereditarietà che mi da un po' di problemi a quanto pare (la prima volta che ho fatto la classe avevo semplicemente messo un std::vector come attributo privato)...

Comunque perché non utilizzeresti il codice? Poco efficiente? Mal concepito? :spy:
xD

RooccoXXI
02-06-2012, 01:00
Ho eseguito le seguenti modifiche e ora compila.

Makefile:

CC = g++gcc-4.6.x
CXX = g++gcc-4.6.x
CXXFLAGS = -std=c++0x

all: testVector

# --- TEST VECTOR ---
testVector: testVector.o
testVector.o: testVector.cc Vector.h

# --- CLASSES ---


clean:
rm -f *.o

Ho cambiato la vecchia istruzione che dava problemi in

this->size() != V.size()
(Perché la precedente versione non funzionava?).

Però ora ho il seguente problema, quando viene chiamato il costruttore di copia:

terminate called after throwing an instance of 'std::range_error'
what():
Abort trap: 6

shodan
02-06-2012, 01:27
Io eviterei quando possibile di usare *this.begin() et similia . Meglio usare this->begin(), più elegante e non soggetto a eventuali precedenze di operatori. E l'errore:


Vector.h:78:2: error: request for member ‘size’ in ‘this’, which is of non-class type ‘const Vector<double>* const’

sembra indicare che prima venga richiesto il metodo size() e solo dopo venga dereferenziato this.
In caso di dubbio usa sempre (*this).qualcosa() e vai sul sicuro (fermo restando che this->qualcosa() rimane migliore per leggibilità.

Gli iteratori poi dovrebbero essere costanti, quindi usa cbegin(), cend() all'interno di metodi const. Terza cosa quel bool dovrebbe essere mutable bool dato che lo modifichi all'interno di un metodo const. Tuttavia il metodo mi pare macchinoso.


template<typename T>
bool Vector<T>::operator==(const Vector<T>& V) const
{
if (this->size() == V.size()) {
return equal(this->cbegin(),this->cend(),V.cbegin());
}
return false;
}

mi pare più lineare.



Comunque perché non utilizzeresti il codice? Poco efficiente? Mal concepito?

Le classi STL non hanno distruttore virtuale pertanto non si dovrebbe corretto ereditare da esse, ma se non allochi dati in Vector non hai problemi immediati.
Il fatto è che non capisco la scelta implementativa. Voglio dire: qui


// 3D vector
template<typename T>
Vector<T>::Vector(T x, T y, T z)
: std::vector<T>({x, y, z})
{}

acquisisci tre valori che intuisco essere tre coordinate. Ma un vector<> contiene un solo punto per locazione. Questo comporta un po' di acrobazie per ricavare, calcolare e salvare i dati. Io avrei optato per un vector di struct point3D (una tupla di 3 elementi in C++11), eventualmente incapsulando tale vector in una classe che mi semplichi eventuali calcoli e facendo un forward degli iteratori per usare la classe contenitrice con gli algoritmi standard.
Comunque al momento non mi serve una classe Vector Matematico per questo non "grabbo" il codice :D

Loading