std::tr1::function and std::tr1::bind

rubenvb picture rubenvb · Jul 15, 2010 · Viewed 11.1k times · Source

I have a problem using a very complicated C function in a C++ class (rewriting the C function is not an option). C function:

typedef void (*integrand) (unsigned ndim, const double* x, void* fdata,
                           unsigned fdim, double* fval);
// This one:
int adapt_integrate(unsigned fdim, integrand f, void* fdata,
                    unsigned dim, const double* xmin, const double* xmax, 
                    unsigned maxEval, double reqAbsError, double reqRelError, 
                            double* val, double* err);

I need to supply a void function of type integrand myself, and adapt_integrate will calculate the n-dimensional integral. The code in calcTripleIntegral (below) works as a standalone function if func is a standalone function). I want to pass a (non-static!) class member function as the integrand, as this can be easily overloaded etc...

class myIntegrator
{
public:
    double calcTripleIntegral( double x, double Q2, std::tr1::function<integrand> &func ) const
    {
        //...declare val, err, xMin, xMax and input(x,Q2) ...//
        adapt_integrate( 1, func, input,
                         3, xMin, xMax,
                         0, 0, 1e-4,
                         &val, &err);
        return val;
    }
    double integrandF2( unsigned ndim, const double *x, void *, // no matter what's inside
                 unsigned fdim, double *fval) const;            // this qualifies as an integrand if it were not a class member
    double getValue( double x, double Q2 ) const
    {
        std::tr1::function<integrand> func(std::tr1::bind(&myIntegrator::integrandF2, *this);
        return calcTripleIntegral(x,Q2,func);
    }
}

On GCC 4.4.5 (prerelease), this gives me:

error: variable 'std::tr1::function func' has initializer but incomplete type

EDIT:What is the error in my code? I have now tried compiling with GCC 4.4, 4.5 and 4.6, all resulting in the same error. Either no work has been done on this, or I did something wrong /EDIT

Thanks very much! If I'm not clear enough, I'll gladly elaborate.

PS: Could I work around this without tr1 stuff by using a function pointer to a function defined somewhere in myIntegrator.cpp?

FINAL UPDATE: ok, I was mistaken in thinking TR1 provided a one/two-line solution for this. Bummer. I'm "converting" my classes to namespaces and copypasting the function declarations. I only need one base class and one subclass which reimplemented the interface. C function pointer + C++ class = bad news for me. Thanks anyways for all the answers, you've shown me some dark corners of C++ ;)

Answer

Georg Fritzsche picture Georg Fritzsche · Jul 16, 2010

You have three problems... first you want a std::tr1::function<R (Args..)>, but yours boils down to std::tr1::function<R (*)(Args...)> - so you need two typedefs:

typedef void (integrand) (unsigned ndim, const double *x, void *,
                       unsigned fdim, double *fval);
typedef integrand* integrand_ptr;

... so the first allows you a compilable function<integrand>. adapt_integrate has to be fixed accordingly:

int adapt_integrate(unsigned fdim, integrand_ptr f, ...);

Next your bind syntax is off, it should be:

std::tr1::bind(&myIntegrator::integrandF2, *this, _1, _2, _3, _4, _5);

The remaining problem is that tr1::function<T> isn't convertible to a function pointer, so you would have to go through a wrapper function, using the void* fdata argument to pass the context. E.g. something like:

extern "C" void integrand_helper (unsigned ndim, const double *x, void* data,
                                  unsigned fdim, double *fval)
{
    typedef std::tr1::function<integrand> Functor;
    Functor& f = *static_cast<Functor*>(data);
    f(ndim, x, data, fdim, fval);
}

// ...
adapt_integrate(1, &integrand_helper, &func, ...);

This is of course assuming that the void* parameter is passed through to the function, if not it would get ugly.

On the other hand, if void* fdata allows to pass context, all that tr1::function stuff is unnecessary and you could just go directly through a trampoline function - just pass this through as the context argument:

extern "C" void integrand_helper (unsigned ndim, const double *x, void* data,
                                  unsigned fdim, double *fval)
{
    static_cast<myIntegrator*>(data)->integrandF2(ndim, ...);
}

// ...
adapt_integrate(1, &integrand_helper, this, ...);