boost::function doesn't like templates

I have a function template that takes a boost::function as an argument. In simpler terms (without use of boost):

1
2
template <class T>
T g(T (*f)(T), T a, T b, ...) {...}


In order to make this easier to use (e.g with boost::bind), I overloaded g with a similar definition using a boost::function object. As in:

1
2
template <class T>
T g(boost::function<T(T)> f, T a, T b, ...) {...}


When I call this (with T = double), I get an undefined reference error. The error goes away, however, if I overload the above definition with:

 
double g(boost::function<double(double)> f, double a, double b, ...) {...}


Have I misunderstood the use of boost::function, am I trying to do something impossible, or do I have a syntax error? The code compiles fine.
Last edited on
Works for me:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <boost/function.hpp>

template <class T>
T g(boost::function<T(T)> f, T a, T b)
{
    std::cout << f(a) << " " << f(b) << '\n';
}

double f(double x) { return 1+x; }

int main()
{
    g<double>(f, 1.1, 2.2);
}

online demo: http://ideone.com/fcJGX

Do you have a complete test case?
It's a set of a few files. I've removed the content of main, the meta-information, and the name of the project to make it easier to read (it just returns 0). I have also changed the content of the integrator to a simple Reimann sum to make it shorter.

integrate.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef PROJECT_INTEGRATE
#define PROJECT_INTEGRATE

#include <boost/function.hpp>

template <class T>
T integrate(boost::function<T(T)>,T,T,unsigned int);

//double integrate(boost::function<double(double)>,double,double,unsigned int);

template <class T>
T integrate(T(*)(T),T,T,unsigned int);

#endif 


integrate.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include "integrate.h"

template <class T>
T integrate(boost::function<T(T)> f, T min, T max, unsigned int nsteps = 2000) {

  T current = min, sum = 0, delta = (max-min)/((T)nsteps);
  for (unsigned int i=0; i < nsteps; i++) {
    sum += f(delta*current);
    current += delta;
  }
  return sum;
}

/* double integrate(boost::function<double(double)> f, double min, double max, unsigned int nsteps = 2000) {
  
  double current = min, sum = 0, delta = (max-min)/((double)nsteps);
  for (unsigned int i=0; i < nsteps; i++) {
    sum += f(delta*current);
    current += delta;
  }
  return sum;
} */

template <class T>
T integrate(T (*f)(T), T min, T max, unsigned int nsteps = 2000) {

  T current = min, sum = 0, delta = (max-min)/((T)nsteps);
  for (unsigned int i=0; i < nsteps; i++) {
    sum += f(delta*current);
    current += delta;
  }
  return sum;
}


cosmology.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef PROJECT_COSMOLOGY
#define PROJECT_COSMOLOGY

// Hubble Distance
double Dh(double);

double E(double,double,double,double);
double reciprocalE(double,double,double,double);

// Comoving Distance
double Dc(double,double,double,double,double);

// Transverse Comoving Distance
double Dm(double,double,double,double,double);

// Comoving Volume
double Vc(double,double,double,double,double);

#endif 


cosmology.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include "integrate.h"
#include "cosmology.h"
#include <cmath>
#include <boost/bind.hpp>
#include <boost/function.hpp>

#define SPEED_OF_LIGHT 2.998e8 //m s^(-1)
#define PI 3.14159265

double Dh(double Ho) {
  return SPEED_OF_LIGHT/Ho;
}

double E(double z, double omegaM, double omegaK, double omegaL) {
  return std::sqrt(omegaM*std::pow(1.0+z,3) + omegaK*std::pow(1.0+z,2) + omegaL);
}

double reciprocalE(double z, double omegaM, double omegaK, double omegaL) {
  return 1.0/E(z,omegaM,omegaK,omegaL);
}

double Dc(double z, double Ho, double omegaM, double omegaK, double omegaL) {
  boost::function<double(double)> F = boost::bind(reciprocalE,_1,omegaM,omegaK,omegaL);
  return Dh(Ho) * integrate<double>(F,0.0,z,2000);
}

double Dm(double z, double Ho, double omegaM, double omegaK, double omegaL) {
  if (omegaK>0) {
    return Dh(Ho) * 1.0/sqrt(omegaK) * sinh(sqrt(omegaK) * Dc(z,Ho,omegaM,omegaK,omegaL)/Dh(Ho));
  } else if (omegaK==0) {
    return Dc(z,Ho,omegaM,omegaK,omegaL);
  } else {
    return Dh(Ho) * 1.0/sqrt(-omegaK) * sin(sqrt(-omegaK) * Dc(z,Ho,omegaM,omegaK,omegaL)/Dh(Ho));
  }
}

double Vc(double z,double Ho, double omegaM, double omegaK, double omegaL) {
  if (omegaK>9) {
    return (4.0*PI*pow(Dh(Ho),3)/(2.0*omegaK)) * ((Dm(z,Ho,omegaM,omegaK,omegaL)/Dh(Ho)) * sqrt(1+omegaK*pow(Dm(z,Ho,omegaM,omegaK,omegaL)/Dh(Ho),2)) - 1/sqrt(omegaK)*asinh(sqrt(omegaK)*(Dm(z,Ho,omegaM,omegaK,omegaL)/Dh(Ho))));
  } else if (omegaK==0) {
    return 4.0*PI/3.0*pow(Dm(z,Ho,omegaM,omegaK,omegaL),3);
  } else {
    return (4.0*PI*pow(Dh(Ho),3)/(2.0*omegaK))*(Dm(z,Ho,omegaM,omegaK,omegaL)/Dh(Ho) * sqrt(1+omegaK*pow(Dm(z,Ho,omegaM,omegaK,omegaL),2)/pow(Dh(Ho),2)) - 1/sqrt(-omegaK)*asin(sqrt(-omegaK)*Dm(z,Ho,omegaM,omegaK,omegaL)/Dh(Ho)));
  }
}


The main loop will obviously be much larger in the future, but it suffices to have an empty one (main.cpp):
1
2
3
4
5
#include "integrate.h"
#include "cosmology.h"
#include <cmath>

int main(int argc, char* argv[]) { return 0; }


The problem occurs during linking with the error:
1
2
3
cosmology.o: In function `Dc(double, double, double, double, double)':
cosmology.cpp:(.text+0x1eb): undefined reference to `double integrate<double>(boost::function<double (double)>, double, double, unsigned int)'
collect2: error: ld returned 1 exit status

after running the commands (the last one fails):
1
2
3
4
g++ -c -Wall -lboost main.cpp -o main.o
g++ -c -Wall -lboost cosmology.cpp -o cosmology.o
g++ -c -Wall -lboost integrate.cpp -o integrate.o
g++  main.o cosmology.o integrate.o -o integrator
when g++ compiled integrate.cpp to produce integrate.o, it had no idea that there exists a cosmology.cpp somewhere that is going to instantiate integrate<double>. So no such function was compiled.

To fix, move the source of the integrate template to integrate.h. Templates belong to headers (look at the boost library you're using, for example -- function.hpp and bind.hpp are both header-only, no -lboost needed)

PS: you could just use Dh(Ho) * integrate(F,0.0,z,2000); there, since F is already defined as boost::function<double(double)>
Last edited on
Isn't it sloppy to include real code in a header?
Last edited on
For anyone interested, Cubbi's method works and is more commonly used. Another way of doing this is to use explicit instantiation as covered here:
http://drdobbs.com/184403420
or here:
http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fexplicit_instantiation.htm

This way deals with dependency problems of large projects and can greatly reduce build time. It also makes templates behave more like everything else, which is wonderful for readability.
Template code is not "real code" in that sense.
At one time people thought that it would indeed be "sloppy" and invented the C++ keyword export, but only one major compiler ever implemented it (five years after the fact), and it was eventually deprecated because it didn't do much good even when implemented.
Topic archived. No new replies allowed.