4

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

Marc Mutz - mmutz
  • 24,485
  • 12
  • 80
  • 90
rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • What are the `fdata` and `input` parameters about? Why doesn't the `void*` parameter in the integrand type have a name? – deft_code Jul 15 '10 at 22:22
  • Caspin, you don't need to name the parameters of a function in a type declaration. You don't even need to name them in the actual definition of the function if you don't need to use them in the function. People usually write this if their function needs to match a certain signature for use with other code, but some of the parameters will never be used in their implementation. – A. Levy Jul 15 '10 at 22:58
  • @A.Le: Caspin knows that. The problem is that we don't know wether `void* fdata` is passed through - if the `void*` parameter for `integrand` was aptly named we wouldn't have to guess/ask. – Georg Fritzsche Jul 15 '10 at 23:01
  • Ah! Silly me! I mistook a leading question for actual ignorance. Sorry Caspin! – A. Levy Jul 15 '10 at 23:33
  • I knew when I asked it someone was going to explain to my why they aren't needed. Maybe I should have prefixed my question with "I know that ...". – deft_code Jul 16 '10 at 00:13
  • I added the void* name, it boils down to the fdata argument in `integrand`, being user-provided parameters for the mathematical function in question. – rubenvb Jul 16 '10 at 09:26
  • @georg: yes, c++0x is allowed, as long as GCC 4.4+ can compile it right. – rubenvb Jul 16 '10 at 09:38
  • Ah, ok. There is a convenient lambda-to-function-pointer conversion, but it is only (re-)implemented in GCC 4.5. – Georg Fritzsche Jul 16 '10 at 10:14

7 Answers7

3

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, ...);
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
3

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;
};
deft_code
  • 57,255
  • 29
  • 141
  • 224
  • isn't this fooling the compiler in accessing non-static members from a static function, which is bad and might crash? – rubenvb Jul 16 '10 at 14:47
  • @rubenvb: No, this is perfectly safe, portable, and good C++ style. I'm only using the static function to get access to `myIntegrator`'s internals. If I wished `fancy_integrand` could be a stand alone function, and all of `myIntegrator`'s members could be made public. I like the static method approach as it's more encapsulated. – deft_code Jul 16 '10 at 15:18
  • Although the title may not lend itself to an answer without bind and/or function, it does solve the problem at hand. Thank you for sticking with it :) – rubenvb Jul 17 '10 at 08:30
2

I want to pass a (non-static!) class member function as the integrand...

You can't. If you search SO for using member functions as callbacks you'll be bound to find useful information including the fact that what you're trying to do, the direct approach anyway, is not possible.

Edit: BTW, one of the problems in your code (there's more of course since what you're trying to do is simply not possible) is that you've passed a function pointer type to function<> when what it expects is a signature. The function template is implemented something like so:

template < typename Signature >
struct function;

// for each possible number of arguments:
template < typename R, typename Arg1, typename Arg2 >
struct function<R(Arg1,Arg2)>
{
   ... body ...
};

As you can see, passing a function pointer to this kind of thing is simply not going to be understood by the compiler. It's going to try to instantiate the forward declaration and get nowhere. This is of course what the compiler error you're getting means but it doesn't address your fundamental problem, which is that what you're doing will never work.

In a fully C++0x compiler this can be done differently but boost::function and the MSVC one has to be like this. Furthermore, the C++0x version is going to have the same problem that you currently are facing.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125
  • That's exactly why I'm using `function` and `bind` to work around this, see for example http://stackoverflow.com/questions/2374847/passing-member-function-pointer-to-member-object-in-c (second answer is what inspired me) – rubenvb Jul 15 '10 at 20:35
  • @rub: The second answer doesn't have to pass it through a C-function / as a plain function pointer - thats where the problems come from. – Georg Fritzsche Jul 15 '10 at 22:31
  • This answer is correct. I'm looking at the void* argument- is that a userdata-style argument? Many C callbacks provide a void* for whatever context you need. If your C function pointer doesn't have a spare void*, then you can't do this. – Puppy Jul 15 '10 at 22:47
2

Since std::tr1::bind and c-style function pointers don't get along, try this instead. It will work, except that myIntegrator::getValue is not longer thread-safe. If calcTripleIntegral were removed from the interface, this would be even simpler and wouldn't need to use std::tr1::bind or std::tr1::function.

class myIntegrator
{
public:
   double getValue( double x, double Q2 ) const
   {
       return calcTripleIntegral(x,Q2,std::tr1::bind(&Integrator::integrandF2,this));
   }

   double calcTripleIntegral( double x, double Q2, const std::tr1::function<integrand>& func ) const
   {
      assert( s_integrator == NULL );
      s_integrator = this;
      m_integrand = func;

      //...declare val, err, xMin, xMax and input(x,Q2) ...//
      adapt_integrate( 1, &myIntegrator::fancy_integrand, input,
                       3, xMin, xMax,
                       0, 0, 1e-4,
                       &val, &err);

      assert( s_integrator == this);
      s_integrator = NULL;

      return val;
   }
private:
   double integrandF2( unsigned ndim, const double *x, void *,
                unsigned fdim, double *fval) const;

   static double fancy_integrand( unsigned ndim, const double* x, void* input,
                                  unsigned fdim, double* fval)
   {
      s_integrator->integrateF2(ndim,x,input,fdim,fval);
   }

   std::tr1::function<integrand> m_integrand;
   static const myIntegrator* s_integrator;
};
deft_code
  • 57,255
  • 29
  • 141
  • 224
  • Looks like what i'm thinking, though i'd be surprised if that `void*` parameter isn't passed through :) – Georg Fritzsche Jul 15 '10 at 22:53
  • How would removing a convenience function like calcTripleIntegral (which just defines/declares the val, err, input and xMin and xMax parameters) make std::tr1::bind and function superfluous? I can remove it, but wouldn't know how it simplifies things. I've never used this tr1 functionality and haven't found a good (not-boost) explanation of it yet :( – rubenvb Jul 16 '10 at 09:27
  • If I try this, I get an error about m_integrand having an incomplete type :( – rubenvb Jul 16 '10 at 09:48
  • @rub: As i and others pointed out, `function` doesn't take a function *pointer* type - `function` - but a function type - `function`. Using the first gives you exactly that error. – Georg Fritzsche Jul 16 '10 at 10:18
  • @rubenvb: removing `caldTripleIntegral` from interface, also removes `std::tr1::bind` and `std::tr1::function` from the interface. `getValue` doesn't say anything about how it's implemented, two doubles in one double out. Internally I can (indirectly) pass a member function to `updapt_integrate` without using `std::tr1::bind`. – deft_code Jul 16 '10 at 14:04
  • @caspin: and that won't work, because adapt_integrate expects a normal standalone function, and not a member function with an implicit this reference. That's my problem. Only static functions can do this, and static functions can't access non-static member variables, which I need to do :( – rubenvb Jul 16 '10 at 14:44
1

Making the assumption that the C-API allows passing a type-agnostic (in the sense that the C-API function doesn't have to know its type but relies on the callback function to know what it requires) context parameter (this is usually the case with callback functions; in this case I suspect the fdata parameter to be something along these lines), pass the function object as part of this context parameter.

It should then look something like this:

#include <iostream>
#include <tr1/functional>

typedef void (*callback_function_t)(void *input, int arg);

struct data_type { 
  int x;
};

struct context_type {
  std::tr1::function<void(data_type const &, int)> func;
  data_type data;
};

void callback(data_type const&data, int x) {
  std::cout << data.x << ", " << x << std::endl;
}

void callback_relay(void *context, int x) {
  context_type const *ctxt = reinterpret_cast<context_type const*>(context);
  ctxt->func(ctxt->data, x);
}

void call_callback(callback_function_t func, void *context, int x) {
  func(context, x);
}

int main() {
  context_type ctxt = { callback, { 1 } };

  call_callback(callback_relay, &ctxt, 2);
}

Where call_callback is the C-API function. This way, you can assign anything you want that supports function call syntax to context_type::func, including std::tr1::bind expressions. Also, even though (I feel morally obligated to mention this) it is not, strictly speaking, defined in the standard that calling conventions for C and C++ functions are the same, in practice you could make context_type a class template and callback_relay a function template to make context_type::data more flexible and pass anything you like this way.

0

That error message makes it sound like you're missing an include for one of the types involved. At least try double-checking your integrand and tr1 includes?

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • I've included and , what else could I include? `integrand` is defined in Cubature.h, which I included. This code in standalone classless form works as it should, so `integrand` is not the problem – rubenvb Jul 15 '10 at 21:08
0

bind works a bit different than you assume I think. You either need to provide a value, or a placeholder for every argument.

For your example this boils down to (with placeholders)

std::tr1::function<integrand> func(std::tr1::bind(&myIntegrator::integrandF2, *this, _1, _2, _3, _4, _5));

Since you're binding a member function, you got an extra (implicit) argument, i.e. the object you call the member function on, so you have six.

For the first you bind the this object, for the other arguments you simply pass placeholders.

On a side note, your member function returns double, while the function declaration returns void.

(for the record, I'm still using an older compiler with little tr1 support, so I only have bind and function experience from using boost, maybe things changed a little for tr1...)

Pieter
  • 17,435
  • 8
  • 50
  • 89