简体   繁体   中英

Problems with C callback function with C++ non-static member function

I have a C library that requires a callback function.

void func( int *a, double *b, double *c, double *d )

a, b, c are input while 'd' is output. My class looks like:

class Test{
public:
    Test()
    {
        a   = 2;               
        ip  = new int[128];     
        kd  = new int[a];   
        c   = new double[a];
        dp  = new double[26];
        ja  = NULL;         
        h   = 1.0e-7;       
        hm  = 1.0e-14;  
        ep  = 1.0e-8;   
        tr  = 1.0e-3;   

        for (int i = 0; i < 128; ++i)
            ip[i] = 0;

        b     = 0.0;
        t_e   = 160.e0;

        c[0]    = 2.e0;
        c[1]    = 0.e0;
    }

    void my_func( int *a, double *b, double *c, double *d ) {
       d[0] = some_value;
       d[1] = some_other_value;
    }

    operator()() 
    {
        auto member_func = std::bind( &Test::my_func, *this, _1, _2, _3, _4 );
        external_function( ip, &a, &b, &t_e, c, &member_func, ja, &h, &hm, &ep, &tr, dp, kd, &ie );
    }

private: 
    int *ip, *kd;
    int a, ie;
    double b, t_e;
    double *c, *dp;
    double h, hm, ep, tr;
    void *ja;
};

It compiles fine but when run, it gives "Segmentation fault (core dumped)". I don't know why this occurs.

Without using the member function (my_func), ie, if I declare, define and use "func" directly in the global scope, it works fine and produces the correct result.

The problem is then I can't have several instances of the Test class running simultaneously in parallel as they each need to modify and use class data members. Unfortunately, "func" takes no argument, eg, (void * data) that allows a cast to a 'Test'.

Could I have some help. Many thanks.

Because my_func is a memberfunction, it has a different calling convention to C function, which accomodates a hidden this pointer. if you want to include a callback for C in a class, declare it static extern "C" .

Are you sure the proper prototype for external_function(...) is visible in you compilation unit?

Some examples:

extern "C" int f1(char(*)(void*,int,int), void* ctx);
extern "C" int f2(char(*)(int,int));
static callbacks* global; // Maybe thread local for thread safety...

extern "C" char cb1_helper(void* ctx, int a, int b) {
    return ((callbacks*)ctx)->cb(a, b);
}
extern "C" char cb2_helper(int a, int b) {
    return global->cb(a, b);
}
class callbacks {
    char cb(int, int) {}
}

int main() {
    callbacks A();
    f1(cb1_helper, &A);
    global = &A;
    f2(cb2_helper);
    return 0;
}

std::bind will not help you because you need a context to call a class method. If callback gets some context from its arguments you can use a free function or static method as a callback:

typedef void (*Callback)(void* context, int arg1);

class A {
public:
    void method(int arg1);

private:
    static void callback(void* context, int arg1)
    {
        A* a = getObjectFromContext(context);
        if (a)
            a->method(arg1);
    }
    // getObjectFromContext may be implemented different ways
    // simpliest case, just cast to A*
    static A* getObjectFromContext(void* context)
    {
        return reinterpret_cast<A*>(context);
    }
};

This works with a single thread and with multiple threads:

// C function
void func( int *a, double *b, double *c, double *d );

// Forward declaration of Test class
class Test;

// Thread local for thread safety
thread_local Test *p_global;

class Test{
public:
Test()
{
    a   = 2;               
    ip  = new int[128];     
    kd  = new int[a];   
    c   = new double[a];
    dp  = new double[26];
    ja  = NULL;         
    h   = 1.0e-7;       
    hm  = 1.0e-14;  
    ep  = 1.0e-8;   
    tr  = 1.0e-3;   

    for (int i = 0; i < 128; ++i)
        ip[i] = 0;

    b     = 0.0;
    t_e   = 160.e0;

    c[0]    = 2.e0;
    c[1]    = 0.e0;
}

void assign_this_pointer_to_global_and_dostuff()
{
    p_global = this;
    dostuff();
}

dostuff() 
{
    external_function( ip, &a, &b, &t_e, c, &func, ja, &h, &hm, &ep, &tr, dp, kd, &ie );
}

private: 
    int *ip, *kd;
    int a, ie;
    double b, t_e;
    double *c, *dp;
    double h, hm, ep, tr;
    void *ja;
};

// C function
void func( int *a, double *b, double *c, double *d ) {
   d[0] = some_value;
   d[1] = some_other_value;
}


int main()
{
    Test test;
    std::thread t1( &Test::assign_this_pointer_to_global_and_dostuff, &test );

    //Join the thread with the main thread
    t1.join();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM