Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++11

tr1

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++ ;)

like image 224
rubenvb Avatar asked Dec 23 '22 01:12

rubenvb


2 Answers

If you are just trying to pass a member function into a c-style callback, you can do that with out using std::t1::bind or std::tr1::function.

class myIntegrator
{
public:
   // getValue is no longer const.  but integrandF2 wasn't changed
   double getValue( double x, double Q2 )
   {
      m_x = x;
      m_Q2 = Q2;

      // these could be members if they need to change
      const double xMin[3] = {0.0};
      const double xMax[3] = {1.0,1.0,1.0};
      const unsigned maxEval = 0;
      double reqAbsError = 0.0;
      double reqRelError = 1e-4;

      double val;

      adapt_integrate( 1, &myIntegrator::fancy_integrand,
                       reinterpret_cast<void*>(this),
                       3, xMin, xMax,
                       maxEval, reqAbsError, reqRelError,
                       &val, &m_err);

      return val;
   }

   double get_error()
   { return m_error; }

private:
   // use m_x and m_Q2 internally
   // I removed the unused void* parameter
   double integrandF2( unsigned ndim, const double *x,
                       unsigned fdim, double *fval) const;

   static double fancy_integrand( unsigned ndim, const double* x, void* this_ptr,
                                  unsigned fdim, double* fval)
   {
      myIntegrator& self = reinterpret_cast<myIntegrator*>(this_ptr);
      self.integrateF2(ndim,x,fdim,fval);
   }

   double m_x
   double m_Q2;
   double m_err;
};
like image 82
deft_code Avatar answered Jan 13 '23 08:01

deft_code


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, ...);
like image 23
Georg Fritzsche Avatar answered Jan 13 '23 10:01

Georg Fritzsche