undefined reference

D

dr.krabbe

Jungspund
Hallo! Ich bin neu im c++-programmieren und lerne (immer noch) nach einem Buch. Ich habe eine Datei, mit einer main und eine .gcc und .h gleichen Namens. Und es geht irgendwie nicht.
So sieht die test_queue.cpp aus:

#include <iostream>
#include "tqueue.h"

using namespace std;

int main(void)
{
cout << "START!!"<<endl;
TQueue<int> tq1(9);

return(0);
}



so die tqueue.h:

#ifndef __TQUEUE_H
#define __TQUEUE_H

template<class Typ>
class TQueue
{
public:
explicit TQueue(unsigned long);
~TQueue(void);
bool Enqueue(Typ);
Typ Dequeue(void);
bool isEmpty(void);

private:
Typ *data;
unsigned long anz;
unsigned long maxanz;
unsigned long inpos, outpos;
};
#endif __TQUEUE_H


und so die tqueue.cpp:

#include "tqueue.h"

template<class Typ>
TQueue<Typ>::TQueue(unsigned long s)
{
data = new(Typ);
if(data)
{
anz=inpos=outpos=0;
maxanz = s;
}
else
{
anz=inpos=outpos=0;
}
}

template<class Typ>
TQueue<Typ>::~TQueue(void)
{
if(data)
delete[](data);
}

template<class Typ>
bool TQueue<Typ>::Enqueue(Typ w)
{
if(anz<maxanz)
{
anz++;
data[inpos++]=w;
if(inpos == maxanz)
inpos = 0;
return(true);
}
else
{
return(false);
}
}

template<class Typ>
Typ TQueue<Typ>::Dequeue(void)
{
if(anz > 0)
{
unsigned long aktpos = outpos;
if((++outpos) == maxanz)
outpos=0;
anz--;
return(data[aktpos]);
}
else
{
return(0);
}
}

template<class Typ>
bool TQueue<Typ>::isEmpty(void)
{
return(anz == 0);
}



und so ist beim aufruf von g++ die Fehlermeldung:


g++ test_queue.cpp
In Datei, eingefügt von test_queue.cpp:2:
tqueue.h:20:8: Warnung: mehrere Token am Ende der Direktive #endif
/tmp/cct0FJqL.o(.text+0x43): In function `main':
: undefined reference to `TQueue<int>::TQueue[in-charge](unsigned long)'
/tmp/cct0FJqL.o(.text+0x4e): In function `main':
: undefined reference to `TQueue<int>::~TQueue [in-charge]()'
collect2: ld returned 1 exit status



Wäre toll, wenn ihr mir sagt, woran das liegt.
Danke schon mal...
 
Probiers mal mit
Code:
# gcc -c tqueue.cpp
# gcc -o test test_queue.cpp tqueue.o
Das erstellt erst den Assembler Code für tqueue.cpp. Dannach wird der für test_tqueue.cpp erstellt, der von tqueue wird diesem zu Verfügung gestellt und das Ergebnis wird gelinkt.
-c ist dafür, dass nur der Assemblercode erstellt wird und nicht gelinkt wird.
-o bennennt die fertige Executable in "test".

Eine kostenlose und sehr gute Anleitung für den Umgang mit gcc findet man auch unter http://www.oreilly.de/german/freebooks/linux_install/kap061.html .

Btw.: Es ist besser, wenn du hier die code tags beim Source posten benutzt
 
Zuletzt bearbeitet:
OK. Ich habe diese beiden befehle ausgeführt. Fehlermeldung ist für mich noch unverständlicher geworden:

Code:
gcc -c tqueue.cpp

In Datei, eingefügt von tqueue.cpp:1:
tqueue.h:20:8: Warnung: mehrere Token am Ende der Direktive #endif

gcc -o test test_queue.cpp tqueue.o

In Datei, eingefügt von test_queue.cpp:2:
tqueue.h:20:8: Warnung: mehrere Token am Ende der Direktive #endif
/tmp/cczJsN0W.o(.text+0x1b): In function `main':
: undefined reference to `std::cout'
/tmp/cczJsN0W.o(.text+0x20): In function `main':
: undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
/tmp/cczJsN0W.o(.text+0x28): In function `main':
: undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)'
/tmp/cczJsN0W.o(.text+0x30): In function `main':
: undefined reference to `std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))'
/tmp/cczJsN0W.o(.text+0x43): In function `main':
: undefined reference to `TQueue<int>::TQueue[in-charge](unsigned long)'
/tmp/cczJsN0W.o(.text+0x4e): In function `main':
: undefined reference to `TQueue<int>::~TQueue [in-charge]()'
/tmp/cczJsN0W.o(.text+0x77): In function `__static_initialization_and_destruction_0(int, int)':
: undefined reference to `std::ios_base::Init::Init[in-charge]()'
/tmp/cczJsN0W.o(.text+0xa8): In function `__tcf_0':
: undefined reference to `std::ios_base::Init::~Init [in-charge]()'
/tmp/cczJsN0W.o(.eh_frame+0x11): undefined reference to `__gxx_personality_v0'
collect2: ld returned 1 exit status


was ist das jetzt?
 
Hallo,

ich hab auch nicht soooo viel Ahnung, aber ich glaube die ganzen letzten Fehlermeldungen kommen daher, dass du ein c++ Programm mit dem C Kompiler (gcc) kompilierst bzw. kompilieren willst =). Tausch mal das gcc gegen g++ aus.

Andrea
 
Ich hab grade mal ne Weile gebastelt und einige Verbesserungsvorschläge:
1. Mit g++ compilieren (wie konnte ich das vergessen?)
2. An das Ende von tqueue.cpp schreiben:
Code:
template class TQueue<int>
Hier scheint (warum auch immer) explizite Instanziierung notwendig zu sein.
3. Bei tqueue.h die Zeile
Code:
 explicit TQueue(unsigned long);
durch
Code:
 TQueue(unsigned long s);
ersetzen, weil es einfach besser aussieht (finde ich).
4. Bei tqueue.h am Ende
Code:
#endif __TQUEUE_H
durch
Code:
#endif //__TQUEUE_H
ersetzen, damit die Warnmeldung weggeht.
Oh und du kannst übrigens auch mit
Code:
# g++ -o test test_queue.cpp tqueue.cpp
compilieren, das passt in eine Zeile :) .

Btw.: Irgendwie hab ich bei c++ noch nie Templates gebraucht. Ich weiß zwar, dass es sowas gibt, aber lasse die Finger möglichst davon, weil das so komplex und fehleranfällig ist.
 
Zuletzt bearbeitet:
gcc ist für C, g++ ist für C++, gcj ist für Java, g77 ist für Fortran, etc...

C++ und C sind zwei verschiedene Programmiersprachen.

auf bald
oenone
 
Hallo,
dein/das Problem ist das der gcc das "export" Keyword von C++ nicht kennt und dadurch die Deklaration und Definition der Template-Klasse in einer Datei sein müssen.
Das heißt alles aus tqueue.cpp in tqueue.h packen.

Alternativ kannst du aber auch einfach ein ' #include "tqueue.cpp" ' vor dem #endif in die tqueue.h schreiben. Dann darfst/brauchst du die tqueue.cpp aber nicht mehr extra compilen/mitlinken.
g++ test_queue.cpp -o test_queue
würde dann reichen.
 
Konrad hatte Recht!!!!
Ich weiß zwar nicht warum und was es eigentlich bedeutet, aber durch ein zusätzliches include der .cpp-datei hats geklappt.

Ich habe vorher natürlich schon g++ benutzt, das war wohl nur ein Fehler meinerseits.sorry.

Aber was soll ich jetzt tun, damit mein g++ export versteht? Ist doch nicht normal, oder?


Danke für die vielen Lösungen!!
 
Es gibt eigentlich keine Möglichkeit dem gcc "export" beizubringen.
Das ist einfach ein C++ Keyword welches es ermöglichen soll das bei Templates dieselbe Aufteilung von Schnittstelle (.h) und Definition (.cpp) wie bei "normalen" Klassen möglich ist.
Bisher kenn Ich nur einen Compiler der das kann und zwar Comeau.

Unter normaler Aufteilung verstehe Ich das hier:
Code:
//main.cpp:

#include "klasse.h"

int main() { /* Klasse benutzen ... */ }

//klasse.h
/*
 * Klassen-Schnittstelle
 * /

//klasse.cpp
#include "klasse.h"
/*
 * Implementierung der Klasse
 */
In dem Fall kann Ich "Klasse" in meiner main() verwenden, die Aufrufe werden beim kompilieren mit Platzhaltern versehen und später, beim Linken, mit dem richtigen "Inhalt" aus klasse.cpp ersetzt.

Das Problem bei den Templates ist das sie zur Compile-Zeit "ausgeführt" werden und zu der Zeit verständlicherweise dann auch die vollständige Implementierung der Klassen/Funktionen vorhanden sein müssen.
Da die C++ Compiler aber jede .cpp Datei separat compilen und das Programm erst anschließend vom Linker "zusammengebaut" wird, würde der Compiler die Template-Definitionen beim Compile-Vorgang nicht kennen. (Da sie ja in einer separaten .cpp Datei ist)

"export" sollte einen ähnlichen Effekt wie bei "normalen" Klassen zur Folge haben. Es ist nur ein wenig schwieriger da der Compiler bei main.cpp das Template nicht mit seinen Parametern bearbeiten kann (Da Implementierung unbekannt...) und die klasse.cpp das Problem mitsich bringt das der Compiler nicht weiß für welche Parameter das Template instanziiert werden soll.

So long..
hoffe du hast's verstanden :)
 
Sry, aber mal ehrlich: Sowas vermeidet man doch, wenns irgendwie geht beim Programmieren. Wenn ich das was ich durch das Template an Zeit / Aufwand etc. einspare wieder für solche Überlegungen draufgehen lassen muss, brauch ich auch kein Template mehr.
Das soll jetzt nicht gegen Templates im Allgemeinen sein, aber das hier ist schon ein arg krummer Fall (der auch nicht so oft vorkommt denke ich)...
 
MrFenix schrieb:
Sry, aber mal ehrlich: Sowas vermeidet man doch, wenns irgendwie geht beim Programmieren.
Was meinst du genau?

MrFenix schrieb:
Wenn ich das was ich durch das Template an Zeit / Aufwand etc. einspare wieder für solche Überlegungen draufgehen lassen muss, brauch ich auch kein Template mehr.
Solche Überlegungen sind ja gar nicht nötig/wichtig. Der Header der Template-Klasse muss einfach auch die Implementierung beinhalten. Mehr nicht.

MrFenix schrieb:
Das soll jetzt nicht gegen Templates im Allgemeinen sein, aber das hier ist schon ein arg krummer Fall (der auch nicht so oft vorkommt denke ich)...
Naja, was heißt krummer Fall.. Der Compiler braucht halt das komplette Template zur Übersetzungszeit, "export" ist zwar im Standard vorgesehen wird aber bisher nur von Comeau angeboten.
Und "krumm" ist es auch nicht. ;) Das Problem ist grundsätzlich vorhanden wenn es um Templates geht.
 
Erstmal: Wo war eigentlich das Problem bei meinem Vorschlag? (das ist jetzt wirklich nicht beleidigt oder provozierend zu verstehen, es interessiert mich nur wirklich, warum man das export noch zusätzlich braucht)
Und vermeiden würde ich persönlich alles, was mich zu sehr auf einen Compiler einschränkt. Es beißt sich etwas mit meinen Vorstellungen Dinge möglichst einfach zu halten...

Btw.: Ich befürchte, dass das hier noch in einen Glaubenskrieg ausartet.. Das ist aber wirklich nicht meine Absicht, denn es wäre leicht lächerlich sich über Klassentemplates zu streiten ^^
 
MrFenix schrieb:
Erstmal: Wo war eigentlich das Problem bei meinem Vorschlag? (das ist jetzt wirklich nicht beleidigt oder provozierend zu verstehen, es interessiert mich nur wirklich, warum man das export noch zusätzlich braucht)
Naja, Ich möchte nicht erst alles mit -c in Assembler Code umwandeln und anschließend mit -o die executable bauen. Jetzt, auf der Console ist das nicht sooo das Problem. Aber stell dir mal vor Ich benutze Visual Studio (blöde Idee ;)), dann könnte Ich ja auch nicht mehr einfach "Build" benutzen sonder müsste da noch in den makefiles rumschreiben damit erst der Assemblercode erstellt wird.
Bei größeren Projekten wird sowas auch schnell unübersichtlich.

Zu deinem 2ten Post, sehe Ich gerade erst:
Du instanziierst ja TQueue<Int> schon in der tqueue.cpp. Was wäre aber wenn er in main.cpp auf einmal TQueue<std::string> oder was weiß Ich braucht? Soll er dann immer in TQueue.cpp das template instanziieren?
Ist (imo) irgendwie nicht so toll und widerspricht auch ein bisschen dem Prinzip der Wiederverwendbarkeit und auch dem Sinn der Templates.
Ich kann ja erstmal nur TQueue<int> benutzen und dann bräuchte Ich die Templates gar nicht sonder könnte gleich die Klasse "normal" mit int programmieren und würde mir das hier alles sparen.

MrFenix schrieb:
Und vermeiden würde ich persönlich alles, was mich zu sehr auf einen Compiler einschränkt.
Sehe Ich genauso. Ich wollte auch niemandem dazu raten "export" zuverwenden, da es wie gesagt zwar im Standard ist aber nicht umgesetzt wird (leider).

MrFenix schrieb:
Es beißt sich etwas mit meinen Vorstellungen Dinge möglichst einfach zu halten...
Ich finde es nicht komplizierter als deine Variante mit dem Assembler-Code (soll kein Angriff sein).
Ist doch wirklich nicht schwer in "klasse.h" noch einmal "klasse_impl.h"
zu includen um die Implementationen parat zuhaben, oder?

MrFenix schrieb:
Btw.: Ich befürchte, dass das hier noch in einen Glaubenskrieg ausartet.. Das ist aber wirklich nicht meine Absicht, denn es wäre leicht lächerlich sich über Klassentemplates zu streiten ^^
Ist/war auch nicht meine Absicht. Ich wollte nur dr.krabbe das Problem, was ja eigentlich keins ist, mit den Templates erklären:

dr.krabbe schrieb:
Konrad hatte Recht!!!!
Ich weiß zwar nicht warum und was es eigentlich bedeutet, aber durch ein zusätzliches include der .cpp-datei hats geklappt.
 

Ähnliche Themen

Nginx als Reverse Proxy für Nextcloud und Emby

Unix Webserver mit HTML Seite erstellen

Funktion nicht gefunden

Rollei Mini Wifi Camcorder

CentOS 6.3 RADIUS - Keine Verbindung möglich

Zurück
Oben