-
Undefined Reference
This problem has me absolutely stumped. As far as I can tell it's an include issue, but I don't know why.
perimosMain.cpp
Code:
1 #include "perimosDatabase.hpp"
2
3 class Client
4 {
5 public:
6 Client();
7 ~Client();
8 Window parentID;
9 Window windowID;
10 };
11
12 Client::Client(){}
13 Client::~Client(){}
14
15 int main()
16 {
17 perimos::database<Client>* pDB = new perimos::database<Client>;
18 Client c;
19
20 for (int a=1;a<10;++a) {
21 c.windowID = a;
22 pDB->add_entry(c.windowID,c);
23 }
24
25 Window wid = 3;
26 pDB->remove_entry(wid);
27
28 delete pDB;
29 return 0;
30 }
perimosDatabase.hpp
Code:
1 #include <iostream>
2 #include <map>
3
4 /*extern "C" {
5 #include <X11/Xlib.h> // Window definition
6 }*/
7 typedef unsigned long int Window;
8 namespace perimos
9 {
10 template<class T> // T is the client type.
11 class database
12 {
13 private:
14 std::map<Window,T> db;
15 typename std::map<Window,T>::iterator pEntry;
16 bool locate(Window&);
17 public:
18 database();
19 ~database();
20 bool add_entry(Window&,T&);
21 bool remove_entry(Window&);
22 T& get_entry(Window&);
23 void print();
24 };
25 } // namespace
perimosDatabase.cpp
Code:
1 #include "perimosDatabase.hpp"
2
3 template<class T>
4 perimos::database<T>::database()
5 {
6 }
7 template<class T>
8 perimos::database<T>::~database()
9 {
10 }
11 /*********************************************************
12 ** Prototype: locate(Window&)
13 ** Parameters: 1
14 *** Window&: The window identity of the client.
15 ** Returns: True if the entry was found and false otherwise.
16 ** Comments: Private function.
17 */
18 template<class T>
19 bool perimos::database<T>::locate(Window& wID)
20 {
21 pEntry = db.find(wID);
22 if (pEntry != db.end())
23 return true;
24 return false;
25 }
26 /*********************************************************
27 ** Prototype: add_entry(Window&,T&)
28 ** Parameters: 2
29 *** Window&: The window identity of the client.
30 *** T&: The client object.
31 ** Returns: True if the entry was added and false otherwise.
32 ** Comments: If false is returned, then the window identity was more than
33 likely a duplicate.
34 */
35 template<class T>
36 bool perimos::database<T>::add_entry(Window& wID,T& c)
37 {
38 if (db.insert(std::make_pair(wID,c)).second)
39 return true;
40 std::cout<<""<<std::endl;
41 return false;
42 }
43 /*********************************************************
44 ** Prototype: remove_entry(Window&)
45 ** Parameters: 1
46 *** Window&: The window identity of the client
47 ** Returns: True if the entry was removed and false otherwise.
48 ** Comments: If false is returned, then the window identity didn't exist.
49 */
50 template<class T>
51 bool perimos::database<T>::remove_entry(Window& wID)
52 {
53 if (locate(wID)) {
54 db.erase(pEntry);
55 return true;
56 }
57 return false;
58 }
59 /*********************************************************
60 ** Prototype: get_entry(Window&)
61 ** Parameters: 1
62 *** Window&: The window identity of the client.
63 ** Returns: A reference to the client object stored in the database.
64 ** Comments: If the window identity does not exist, then the `windowID' of
65 returned object will equal zero (0). Ensure this is checked!
66 */
67 template<class T>
68 T& perimos::database<T>::get_entry(Window& wID)
69 {
70 locate(wID);
71 return pEntry->second;
72 }
73 /*********************************************************
74 ** Prototype: print()
75 ** Parameters: 0
76 ** Returns: void
77 ** Comments: For debugging purposes only.
78 */
79 template<class T>
80 void perimos::database<T>::print()
81 {
82 for (pEntry = db.begin();pEntry!=db.end();++pEntry)
83 std::cout<<pEntry->second.windowID<<":"<<&pEntry->second<<std::endl;
84 }
The problem is: when I compile, below is the error I get:
Command:
Code:
g++ perimosMain.cpp perimosDatabase.cpp -o perimos
Error:
Code:
/var/tmp//ccFSVViv.o: In function `main':
/var/tmp//ccFSVViv.o(.text+0x4c): undefined reference to `perimos::database<Client>::database()'
/var/tmp//ccFSVViv.o(.text+0xbf): undefined reference to `perimos::database<Client>::add_entry(unsigned long&, Client&)'
/var/tmp//ccFSVViv.o(.text+0xdf): undefined reference to `perimos::database<Client>::remove_entry(unsigned long&)'
/var/tmp//ccFSVViv.o(.text+0xf9): undefined reference to `perimos::database<Client>::~database()'
However, if in perimosMain.cpp I change #include "perimosDatabase.hpp" to #include "perimosDatabase.cpp" it compiles.
Could some please shed some light on my problem here?
-
Your perimosDatabase.cpp doesn't use the namespace used in the header file?
-
Sure it does:
Code:
3 template<class T>
4 perimos::database<T>::database()
5 {
6 }
7 template<class T>
8 perimos::database<T>::~database()
9 {
10 }
-
Templates must be completely implemented in the headers, otherwise the compiler can't access them.
-
I not long ago worked out the problem was with the template, yet I'm still not quite sure what. Could you explain it some more please?
-
Do you mean that what's in perimosDatabase.cpp should be merged into perimosDatabase.hpp?
-
Templates aren't real code that is compiled. Instead, the compiler creates real code by substituting the template parameters with the actual types and constants.
Now, this instantiation only happens when it is actually needed. So, if you have three files:
a.cpp
b.hpp
b.cpp
with b.hpp containing a template class b, b.cpp its implementation and a.cpp using it then the following happens when compiling:
b.cpp is compiled. No instantiations are necessary, no code is generated, no code is compiled. b.o is practically empty.
a.cpp is compiled. b<int> (for example) is required and instantiated from all the code the compiler knows at that moment - which is only b.hpp, because only that file is included from a.cpp. b.hpp only contains declarations. That's enough for the compiler, it creates and compiles code that uses b<int>. a.o contains code and references to members of b<int>.
Final step, the linker. It gets a.o and b.o as input. It finds the references to b<int>::b, b<int>::do_stuff etc and looks for implementations. a.o doesn't contain them: the compiler didn't have access to the implementation code. b.o doesn't contain them either: there was no need to instantiate b<int> while compiling b.cpp. So the linker creates a load of unresolved reference errors.
The solution is, as I said, to transfer all code from b.cpp to b.hpp (or a file included from there, e.g. bimpl.hpp). This way, the compiler has access to the implementation when instantiating b<int> and all is well.
-
I understand. Thanks, CornedBee.