Search code examples
c++g++unix-ar

Undefined reference to (g++ compilation with static library)


The project files is as this:

source
  parser
    parser.cpp
    parser.hpp
  brain
    brain.cpp
    brain.hpp

I first ran these two commands (pwd source/brain/):

g++ -c brain.cpp -o brain.o
ar rvs brain.a brain.o

I copied both brain.a and brain.hpp to source/parser/. Then I ran this command (pwd source/parser):

g++ parser.cpp brain.a -o parser

And I got this error:

/tmp/cceGRLZn.o: In function `main':
parser.cpp:(.text+0x1cc): undefined reference to `std::brain<long long>::brain()'
parser.cpp:(.text+0x205): undefined reference to `std::brain<long long>::init(int)'
parser.cpp:(.text+0x26b): undefined reference to `std::brain<long long>::work()'
parser.cpp:(.text+0x2a4): undefined reference to `std::brain<long long>::clear()'
parser.cpp:(.text+0x2ec): undefined reference to `std::brain<long long>::~brain()'
parser.cpp:(.text+0x322): undefined reference to `std::brain<long long>::~brain()'
/tmp/cceGRLZn.o: In function `int parser_extract_args<long long>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, int, short&, std::brain<long long>&)':
parser.cpp:(.text._Z19parser_extract_argsIxEiRSsiRsRSt5brainIT_E[int parser_extract_args<long long>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, int, short&, std::brain<long long>&)]+0x17b): undefined reference to `std::brain<long long>::push_back(long long)'
parser.cpp:(.text._Z19parser_extract_argsIxEiRSsiRsRSt5brainIT_E[int parser_extract_args<long long>(std::basic_string<char, std::char_traits<char>, std::allocator<char> >&, int, short&, std::brain<long long>&)]+0x37a): undefined reference to `std::brain<long long>::push_back(long long)'
collect2: ld returned 1 exit status

Source files:

brain.cpp [http://ideone.com/GNUxmH][1]
brain.hpp [http://ideone.com/M2IFAI][2]
parser.cpp [http://ideone.com/fJRzhD][3]
parser.hpp [http://ideone.com/mj6dST][4]

What should I do?


Solution

  • I bet you implemented the member functions of your class template brain in the .cpp file. You need to provide template definitions in the header file so that the compiler can generate the appropriate code when it sees a template instantiation. So move the contents of brain.cpp up to brain.h.

    As an example, consider these three files:

    • test.h

      template <typename T>
      struct test
      {
        void foo(T);
      };
      
    • test.cpp

      #include "test.h"
      
      template <typename T>
      void test<T>::foo(T x)
      {
        // do something with x
      }
      
    • main.cpp

      #include "test.h"
      
      int main()
      {
        test<int> t;
        t.foo(5);
      }
      

    Each .cpp is compiled separately and then linked together. Imagine you are the compiler, and you're trying to compile main.cpp. You see that the code wants to use the test template instantiated with T as int. So now you need to generate the appropriate code for test<int>::foo, but to do that you need to have seen the function's definition. Unfortunately, you haven't seen it, so you can't compile this program.

    Instead, the definition of foo should be moved into the header file to result in this two file program:

    • test.h

      template <typename T>
      struct test
      {
        void foo(T);
      };
      
      // Alternatively, you can define this up in the class definition
      void test<T>::foo(T x)
      {
        // do something with x
      }
      
    • main.cpp

      #include "test.h"
      
      int main()
      {
        test<int> t;
        t.foo(5);
      }
      

    Note that you should not be adding your own declarations to the std namespace:

    The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified.