简体   繁体   中英

Calling a function from function pointer in struct

I want to write a wrapper for the callback function for progress information of libcurl in C. This wrapper should calculate some data which are supposed to be encapsulated in a library.

Here's a description of the basic idea: (I hope this clarifies things)

The user should implement a callback function xFunc and initialize a data structure to store data for the callback function (called xData). Then he should call a wrapper function aFunc. As argument he provides some connection related data and a pointer to xFunc and xData. In aFunc some things are calculated which are required to call a second function bFunc. The xFunc and xData pointer are also passed to this function. In this function, a second data structure YData is initialized. This structure holds a pointer to the user provided xFunc and xData and some other data which are required for calculations. Now the libcurl related things get initialized. As a callback for the upload progress, a second callback function yFunc is implemented in the library. In this function the user provided xFunc should be called with xData and other calculated values.

Now here's the code of my current implementation (I stripped all the calculation code and renamed the functions and structures to math the description above so it looks a bit strange. I hope it isn't too abstract now):

main.c

int xFunc(void *xData, long int param1, long int param2) {
    //do something with the data
    return 0;
}

int main(void){

    xpData *xData;
    xData->value = 0;

    aFunc(xFunc, p);

    return 0;
}

curlutils.h

typedef struct {
    int value;
} xpData;

int xFunc(void *xData, long int param1, long int param2);
int aFunc(int (*xFunc)(void*, long int, long int), void xData);
int bFunc(int (*xFunc)(void*, long int, long int), void xData);

curlutils.c

typedef struct {
    int (*xFunc)(void*, long int, long int);
    void *data;    
} ypData;


int yFunc(void *p, double dltotal, double dlnow, double ultotal, double ulnow){
    ypData *pd;
    pd = (ypData*) p;    
    return pd->xFunc(pd->data);
}

int bFunc(int (*xFunc)(void*, long int, long int), void xData){
    CURL *curl;
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if (curl){ //stripped many curl initialization stuff
        ypData *yData;
        yData->data = xData;
        yData->xFunc = xFunc;
        curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, yFunc);
        curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &yData);
        curl_easy_perform(curl);
    }
    return 0;
}

int aFunc(int (*xFunc)(void*, long int, long int), void *xData){
    //calculate something useful
    bFunc(xFunc, xData);
    return 0;
}

The problem is in yFunc where I want to call xFunc. The program crashes with the error:

Illegal instruction

Why is this code not working?

The problem is that when you pass pointers as void pointers, you don't get any warnings if you pass the wrong kind of pointer. Then when you cast the pointer, you cast it to the wrong type and probably jump off to an invalid address.

You have:

    ypData *yData;
    yData->data = xData;
    yData->xFunc = xFunc;
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, yFunc);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &yData);

This declares a locat pointer var yData but doesn't initialize it. You then promptly dereference the pointer, which will likely crash (and should give a compile time warning). If it doesn't, you then pass &yData (which is a ypData ** ) to yFunc (indirectly via curl_easy_setopt ), which is expecting a ypData * as its argument.

You might be able to fix it by using:

    ypData yData;
    yData.data = xData;
    yData.xFunc = xFunc;
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, yFunc);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &yData);
    curl_easy_perform(curl);

this has the potential danger if one of the curl functions stash away the pointer to yData and a later curl call tries to call yFunc after this function has returned (and the pointer is dangling). If that can happen, you need something like:

    ypData *yData = malloc(sizeof *yData);
    yData->data = xData;
    yData->xFunc = xFunc;
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, yFunc);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, yData);

which has the problem that you don't know when to free the allocated yData , so is subject to memory leaks.

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