Problem mit templates
-
hallo,
ich habe mich gerade an meinem ersten C++ Template versucht, mit einer Struktur hab ichs ohnen Probleme hingebracht, nur sobald ich von meinem Klassen Template ein Objekt erzeugen will gibts einen Linker Error (Zeiger auf Klassen Template funktioniert....)
Ich habe keine Ahnung was nicht passt, hier der Code :
dictionary.h
#ifndef documentH // um header-files nicht mehrfach zu inkludieren #define documentH // um header-files nicht mehrfach zu inkludieren #ifdef HAS_EXPORT #define EXPORT export #else #define EXPORT #endif #include <string> #include <vector> EXPORT template<typename T> struct key_value { std::string key; T value; }; EXPORT template<typename T> class Dictionary{ public: Dictionary(); key_value<T> get_pair(int pos); void add_pair(std::string key, T value); void add_value(std::string key){} private: std::vector<key_value<T> > *keys_values; }; #ifdef NEED_TEMPLATE_DEFINITIONS #include "dictionary.cpp" #endif #endif // um header-files nicht mehrfach zu inkludierendictionary.cpp
#include "dictionary.h" #include <iostream> EXPORT template<typename T> Dictionary<T>::Dictionary(){ key_value<T> new_pair; keys_values = new std::vector<key_value<T> >(); } EXPORT template<typename T> key_value<T> Dictionary<T>::get_pair(int pos){ // Returns the struct key_value at $pos, // throws std::out_of_range when $pos is invalid return keys_values->at(pos); } EXPORT template<typename T> void Dictionary<T>::add_pair(std::string key, T value){ // adds a struct with $key and $value to the dictionary key_value<T> pair; pair.key = key; pair.value = value; return keys_values->push_back(pair); }und der Code zum "testen" (CPPUNIT)
.... #include "../../core/dictionary.h" class DictionaryTest : public CPPUNIT_NS :: TestFixture{ CPPUNIT_TEST_SUITE (DictionaryTest); CPPUNIT_TEST (testDictionaryConstructor); CPPUNIT_TEST (testDictionaryAdd_pair); CPPUNIT_TEST_SUITE_END (); public: Dictionary<std::string > *dict1; void setUp(){ dict1 = new Dictionary<std::string >(); //->fuehrt zu dem Linkerfehler } .... };und zu guter letzt noch die Compilermeldung (gcc v4.3)
g++ -Wall -o run_tests run_tests.o test_document.o test_dictionary.o dictionary.o document.o sqlite3_backend.o test_sqlite3_backend.o `pkg-config sqlite3 --cflags --libs` `pkg-config cppunit --cflags --libs` test_dictionary.o: In function `DictionaryTest::setUp()': test_dictionary.cpp:(.text._ZN14DictionaryTest5setUpEv[DictionaryTest::setUp()]+0x22): undefined reference to `Dictionary<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::Dictionary()' collect2: ld returned 1 exit status make: *** [test] Error 1hoffe, dass mir jemand helfen kann
mfg
-
Code scheint in Ordnung.
Versuch es einfach ohne die #ifdef-Orgie, nur Inclusionsmodel statt diesem Mix aus Inclusion und Separation; möglicherweise löst sich dann dein Problem von selbst.
-
Templates müssen zur Instanzierung vollständig bekannt sein. Deshalb müssen die Templates aus dictionary.cpp in die dictionary.h, da ich zunächst mal davon ausgehe, dass Dein Compiler HAS_EXPORT nicht definiert.
EDIT:
Ifdef unten nicht gesehen. Ist denn dieses Define gesetzt, was die cpp includiert?
-
also ich hab jetzt die Methoden Definition in die Headerdatei gegeben, funktioniert tadellos.
Wie erkenne ich ob mein compiler die HAS_EXPORT definiert?
Was meinst du mit "Inclusionsmodel" ?
mfg
-
d3f3nd3r schrieb:
Wie erkenne ich ob mein compiler die HAS_EXPORT definiert?
Was meinst du mit "Inclusionsmodel" ?
Wenn du das alles nicht weißt, solltest du die ganzen #ifdef-Schlachten einfach weglassen. Offenbar hast du das von irgendwem abgeschrieben der ganz gewitzt sein wollte und die export-Funktionalität nutzen wollte, wenn es sie denn gibt (es gibt ein oder zwei Compiler die das überhaupt können).
Machs also einfach so:
#ifndef documentH // um header-files nicht mehrfach zu inkludieren #define documentH // um header-files nicht mehrfach zu inkludieren #include <string> #include <vector> template<typename T> struct key_value { std::string key; T value; }; template<typename T> class Dictionary{ public: Dictionary(); key_value<T> get_pair(int pos); void add_pair(std::string key, T value); void add_value(std::string key){} private: std::vector<key_value<T> > *keys_values; }; #include "dictionary.cpp" #endif // um header-files nicht mehrfach zu inkludierenund die .cpp wie gehabt
-
jup, hatte ich aus c++ in a nutshell

aber mit dem include passiert folgendes :
g++ -Wall -g -c src/core/dictionary.cpp src/core/dictionary.cpp:9: error: redefinition of ‘Dictionary<T>::Dictionary()’ src/core/dictionary.cpp:9: error: ‘Dictionary<T>::Dictionary()’ previously declared here src/core/dictionary.cpp:15: error: redefinition of ‘key_value<T> Dictionary<T>::get_pair(int)’ src/core/dictionary.cpp:15: error: ‘key_value<T> Dictionary<T>::get_pair(int)’ previously declared here src/core/dictionary.cpp:23: error: redefinition of ‘void Dictionary<T>::add_pair(std::string, T)’ src/core/dictionary.cpp:23: error: ‘void Dictionary<T>::add_pair(std::string, T)’ previously declared here make: *** [dictionary.o] Fehler 1die Quelltexte :
dictionary.h#ifndef dictionary_H // header file #define dictionary_H // header file /* $Id: dictionary.h 18 2008-09-24 08:18:48Z d3f3nd3r@bytegeist.org $ */ #include <string> #include <vector> template<typename T> struct key_value { std::string key; T value; }; template<typename T> class Dictionary{ public: Dictionary(); key_value<T> get_pair(int pos); void add_pair(std::string key, T value); void add_value(std::string key){} private: std::vector<key_value<T> > *keys_values; }; #include "dictionary.cpp" #endif // header filedictionary.cpp
/* $Id: dictionary.cpp 18 2008-09-24 08:18:48Z d3f3nd3r@bytegeist.org $ */ #include "dictionary.h" template<typename T> Dictionary<T>::Dictionary(){ key_value<T> new_pair; keys_values = new std::vector<key_value<T> >(); } template<typename T> key_value<T> Dictionary<T>::get_pair(int pos){ // Returns the struct key_value at $pos, // throws std::out_of_range when $pos is invalid return keys_values->at(pos); } template<typename T> void Dictionary<T>::add_pair(std::string key, T value){ // adds a struct with $key and $value to the dictionary key_value<T> pair; pair.key = key; pair.value = value; keys_values->push_back(pair); }
-
die dictionary.cpp kannst du nicht gesondert kompilieren. Das ist ein Klassen-template das innerhalb der .cpp nicht instantiiert wird. Du bindest lediglich dort wo du Dictionary mit einem expliziten Typ benutzen willst die .h ein.
Oft wird bei templates die Datei mit den Definitionen mit einer anderen Endung versehen, um von einer echten .cpp zu unterscheiden. Einige nennen sie z.B. .imp (für implementation) oder .tpl (für template) oder auch .def (für definition)
Dass die Fehlermeldung kommt ist ja eigentlich klar, wenn du nachvollziehst was der compiler mit den includes anstellt:Dictionary.cpp Anfang #include Dictionary.h Dictionary.h Anfang #ifdef includeguard... Dictionary Klassendefinition #include Dictionary.cpp Dictionary.cpp Anfang #include Dictionary.h Dictionary.h Anfang #ifdef includeguard... -> sprung zum endif #endif Dictionary.h Ende Dictionary::Dictionary() {/*...*/} //erste Ctor-Definition Dictionary.cpp Ende #endif Dictionary.h Ende Dictionary::Dictionary() {/*...*/} //zweite Ctor-Definition, Aua! Dictionary.cpp Endein der template-definitionsdatei wird normalerweise der header nicht mit eingebunden, da der header ja andersrum die .tpl (oder .imp oder .def) einbindet.
Das ist übrigens das inclusion-modell: im header wird erst die klassendefinition mit methodendeklarationen gemacht, danach wird per #include die source-datei mit den methodendefinitionen eingebunden.
-
Vielen Dank, jetzt verstehe ich, was der Compiler mit Templates macht!
-
d3f3nd3r schrieb:
Vielen Dank, jetzt verstehe ich, was der Compiler mit Templates macht!
Zumindest was er mit dem Sourcecode und den includes macht

-
exakt
