Emre Turkoz
Emre Turkoz

Reputation: 868

How do I pass a C++ callback to a C library function?

I'm developing my code using C++ and want to use MPFIT nonlinear curve fitting library, which is developed in C but allows to compile in C++.

For example I have a class named "myClass", and this class has a function myClass::Execute()

I include "mpfit.h" to myClass.h file. And try to call a function called mpfit from Execute().

int status = mpfit(ErrorFunction, num1, num2, xsub_1D, 0, 0, (void *) &variables, &result);

The problem is ErrorFunction is a function of myClass. So compiler gives error when I try to use this. I tried to carry the ErrorFunction out of the class object, but this time I take the error given below:

Error when the ErrorFunction is outside of the class:

Error 4 error C2664: 'mpfit' : cannot convert parameter 1 from 'int (__cdecl *)(int,int,double *,double,double *,void *)' to 'mp_func'

Error when the ErrorFunction is inside the class:

Error   3   error C3867: 'myClass::ErrorFunction': function call missing argument list; use '&myClass::ErrorFunction' to

Definition of error function:

int ErrorFunction(int dummy1, int dummy2, double* xsub, double *diff, double **dvec, void *vars)

How can I call this function and parse it into mpfit, which is a C function?

mp_func is defined as:

/* Enforce type of fitting function */
typedef int (*mp_func)(int m, /* Number of functions (elts of fvec) */
               int n, /* Number of variables (elts of x) */
               double *x,      /* I - Parameters */
               double *fvec,   /* O - function values */
               double **dvec,  /* O - function derivatives (optional)*/
               void *private_data); /* I/O - function private data*/

Upvotes: 1

Views: 3406

Answers (5)

n. m. could be an AI
n. m. could be an AI

Reputation: 119847

Your callback must be declared extern "C" for this to work.

Edit: I see people are having hard time grasping this fact. The standard says (7.5/1):

Two function types with different language linkages are distinct types even if they are otherwise identical.

Upvotes: 1

syplex
syplex

Reputation: 1167

Make sure that your calling conventions match. C libraries use the C calling convention, or cdecl (__cdecl). If you're using the mp_func typedef within C++, it could be defaulting to the compiler's standard calling convention, or stdcall (__stdcall). Either make a new typedef or change it to the following:

typedef int __cdecl (*mp_func)(int m, /* Number of functions (elts of fvec) */
               int n, /* Number of variables (elts of x) */
               double *x,      /* I - Parameters */
               double *fvec,   /* O - function values */
               double **dvec,  /* O - function derivatives (optional)*/
               void *private_data); /* I/O - function private data*/

And when you declare ErrorFunction, also declare it as __cdecl:

int __cdecl ErrorFunction(int, int, double*, double *, double **, void *);

If the compiler still complains when calling the mpfit function, you can try casting your function pointer to the mp_func typedef with cdecl:

int status = mpfit((mp_func)ErrorFunction, num1, num2, xsub_1D, 0, 0, (void *) &variables, &result);

Upvotes: 4

djechlin
djechlin

Reputation: 60748

There's a standard idiom for C++ - to - C, using the pimpl idiom:

foo_c.h:

#ifdef __cplusplus
extern "C" {
#endif
//forward declaration.  clients of foo_c.h should only hold pointers to Foo_c
typedef struct Foo_c Foo_c;

int someMethod(Foo_c* foo);
#ifdef __cplusplus
}
#endif

foo_c.cpp:

#include <foo.h>
struct Foo_c {
       Foo foo;
}
int someMethod(Foo_c* foo) { 
     try {
         foo->foo.someMethod();
         return 0; //no error
     }
     catch(...) {
         return 1; //error
     }
}

(Edited for extern "C"'s per below answer.)

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 595602

Given the definitions of mpfit() and mp_func that you have shown, you would need to use the private_data parameter of mp_func to pass your class's this pointer around. You are currently using that parameter to pass your variables item around instead. Make variables be a member of your class (if it is not already) and then pass this to mpfit() instead:

class MyClass
{
private:
    TheDataType variables;
    static int ErrorFunction(int m, int n, double *x, double *fvec, double **dvec, MyClass *pThis);
public:
    void DoIt();
};

void MyClass::DoIt()
{
    // ...
    int status = mpfit((mp_func)&ErrorFunction, num1, num2, xsub_1D, 0, 0, this, &result); 
    // ...
}

int MyClass::ErrorFunction(int m, int n, double* x, double *fvec, double **dvec, MyClass *pThis)
{
    // use pThis->variables as needed ...
} 

Or:

class MyClass
{
private:
    static int MPFitErrorFunction(int m, int n, double *x, double *fvec, double **dvec, MyClass *pThis);
    int MyErrorFunction(int m, int n, double *x, double *fvec, double **dvec);
public:
    void DoIt();
};

void MyClass::DoIt()
{
    // ...
    int status = mpfit((mp_func)&MPFitErrorFunction, num1, num2, xsub_1D, 0, 0, this, &result); 
    // ...
}

int MyClass::MPFitErrorFunction(int m, int n, double* x, double *fvec, double **dvec, MyClass *pThis)
{
    return pThis->MyErrorFunction(m, n, x, fvec, dvec);
} 

int MyClass::MyErrorFunction(int m, int n, double* x, double *fvec, double **dvec)
{
    // use this->variables as needed ...
} 

Upvotes: 3

James M
James M

Reputation: 16718

Looks like instead of:

int ErrorFunction(int dummy1, int dummy2, double* xsub, double diff, double *dvec, void *vars)

it should be:

int ErrorFunction(int dummy1, int dummy2, double* xsub, double *diff, double **dvec, void *vars)

to match your

typedef int (*mp_func)(int m, /* Number of functions (elts of fvec) */
               int n, /* Number of variables (elts of x) */
               double *x,      /* I - Parameters */
               double *fvec,   /* O - function values */
               double **dvec,  /* O - function derivatives (optional)*/
               void *private_data); /* I/O - function private data*/

Upvotes: 1

Related Questions