Linker errors
Error messages provided by g++, the C++ compiler, are usually reasonably easy to understand. Those produced by the linker, however, are sometimes more cryptic. This section attempts to make linker messages more comprehensible.
First, some background. What is the linker? Consider the framework of a typical C++ program shown below. The main program in main.cpp uses a class called Type1, declared in the file type1.h-no executable code appears here-and defined, with executable code, in the file type1.cpp. A private class variable in the Type1 class is an object of Type2, similarly declared in the file type2.h and defined in the file type2.cpp.
#include <iostream> #include "type1.h" ... int main () { Type1 xyz; ... xyz.F(21); ... }#ifndef _TYPE1 #define _TYPE1 #include "type2.h" class Type1 { public: Type1 (); ... void F(int); ... private: Type2 myType2; ... }; #endif#ifndef _TYPE2 #define _TYPE2 class Type2 { public: Type2 (); ... void G(int); ... private: int myInt; ... }; #endif#include "type1.h" #include "type2.h" Type1::Type1 () { ... } ... void Type1::F (int n) { ... }#include "type2.h" Type2::Type2 () { ... } ... void Type2::G (int k) { ... }Omitting one of the #include statements will produce a compiler error message that says essentially that a type or function has not been declared. For instance, if the #include "type1.h" line is deleted from main.cpp, the type Type1 and the member function F aren't declared, and lines where they are used are flagged with error messages. This happens even if all the .cpp files are compiled together. Similarly, omitting or mistyping the #include <iostream> line produces error messages indicating that cout and cin are undeclared and that no appropriate meanings for the << and >> operators have been found.
To construct an executable program, one may compile all the files together with a command like
g++ -g -Wall main.cpp type1.cpp type2.cppThe files, however, may be compiled separately; a user of the make program will be accustomed to doing this. The -c (compile-only) option to g++ is used to do this. (The -c may appear anywhere in the command line.) The output is a file whose name ends in ".o". Three commands that compile the files main.cpp, type1.cpp, and type2.cpp separately are
g++ -c main.cpp g++ -c type1.cpp g++ -c type2.cppExecuting these three commands creates the files main.o, type1.o, and type2.o. None of these files is executable. (This makes sense, since each was created knowing only the bare minimum-the information in the corresponding .h files-about the others.)
How are these three files combined into a single executable file? Using the linker. What the linker does is to "link" function calls from one of the .o files to the function bodies defined in one of the other .o files.
Unfortunately (at least from the point of view of recognizing the difference between the compiling and the linking phases) the command is the same:
g++ main.o type1.o type2.oThis command creates an executable file named a.out. Should we want a file with a different name, we use the -o option. (The -o and the name may appear together anywhere in the command line.) The command below, for instance, names the executable file main.
g++ main.o type1.o type2.o -o mainOne may now wonder what happens if one of the .o files is missing, so that there is no function body to go with a function call. Just as the compiler would issue an error message for an undeclared function, the linker also produces an error message for functions it can't find. For C++ member functions, however, the message is not very intelligible. Omitting the type2.o argument produces
Undefined first referenced symbol in file __5Type2 type1.o ld: fatal: Symbol referencing errors. No output written to a.outThe cryptic format has to do with the internal format used by the compiler and linker to keep track of member function names.
One solution for the poor programmer is to get good at recognizing member function names amid the gobbledy-gook of the error messages. Another solution is to use the c++filt program to translate the error message to something easier to read. The linker error messages appear on "standard error", which one pipes to c++filt using the |& operator:
g++ main.o type1.o |& c++filt Undefined first referenced symbol in file Type2::Type2(void) type1.o ld: fatal: Symbol referencing errors. No output written to a.outMake users should pipe the output of the command that creates the executable file through c++filt.
A somewhat more complete explanation of the compile/link/execute process is available among Owen Astrachan's Web pages:
http://www.cs.duke.edu/~ola/book/compiling.htmlIt includes discussion of the use of projects in common PC and Macintosh programming environments as well. (Note: it has apparently not yet been updated to use newer conventions for #include directives.)