c++boostlinkerinlineblitz++

Will not link, unless inlining method


I am facing a strange error at linking time.

The headers:

global.h

#include <cmath>
#include <iostream>
#include <vector>

#include <blitz/tinyvec2.h>
typedef blitz::TinyVector<double,3> vettore;

#include "animal.h"

animal.h

#ifndef __ANIMAL_H
#define __ANIMAL_H


//! basic data structure for an animal -------------------------
struct Animal
{
   int age;
public:
   Animal(int _age) : age(_age) {}
};


//! contains info about a pair of animals ----------------------
struct AnimalPairs
{
  vettore distance;

  AnimalPairs( const vettore& _distance ) : distance(_distance) {}
};
typedef std::vector<AnimalPairs> pair_list;


//! data structure for a group of animals ----------------------
class AnimalVector
{
private:
  std::vector<Animal> animals;
  pair_list pairs;

public:
  AnimalVector( const  AnimalVector &other );

};

#endif

And here are the *cpp files

main.cpp

#include "global.h"
int main ()
{
   std::cout<< "Hello" << std::endl;
}

animal.cpp

#include "global.h"

AnimalVector::AnimalVector( const AnimalVector &other )
{
  pairs = other.pairs;
}

To compile I use g++ main.cpp animal.cpp -I/usr/include/boost -I/fs01/ma01/homes/matc/local/blitz/include

here is the error I get:

/tmp/ccGKHwoj.o: In function `AnimalPairs::AnimalPairs(AnimalPairs const&)':
animal.cpp:(.text._ZN11AnimalPairsC2ERKS_[_ZN11AnimalPairsC5ERKS_]+0x1f):
undefined reference to \`blitz::TinyVector<double,
3>::TinyVector(blitz::TinyVector<double, 3> const&)'
collect2: error: ld returned 1 exit status

For some reasons, if I set the AnimalVector constructor to be inline, the code will work. Can someone explain me why?

Edit: Here is the link to blitz/tinyvec2.h https://github.com/syntheticpp/blitz/blob/master/blitz/tinyvec2.h


Solution

  • tinyvec2.cc is the file you need to include when you use methods declared in tinyvec2.h

    I dislike that naming (an include file named .cc) and would have overlooked it myself before getting the error you got.

    But if you look at the contents of that .cc file, the intended usage is clear.

    In similar situations (with very different naming rules for the pairs of files) I use the following coding idiom:

    The first include file (their .h) is included by any other .h file that needs any of this. The second include file is never included by other .h files.

    When you get a build error indicating some of it is missing, add an include of the second include file to any .cpp file that gets the build error.

    That plan is very effective to keep build speeds fast in mega projects and to minimize circular dependence among complicated interacting template classes. But it might not be exactly what whoever split tinyvec2 into two include files had in mind.

    Bottom line: any module getting the link error you saw needs to have had the .cc file included at compile time, but directly vs. indirectly is up to you. Including the .cc in your global.h is simpler, and will at worst slow down your build speed (because this .cc does not have circular relationships with your own .h files).