Is there a way to avoid use of static variable in a callback function?

Refresh

December 2018

Views

427 time

2

I need to define a function which will be called several times by a 3rd-party function. This external library has defined the prototype of the callback as:

typedef void (*fn) (const int *m, const int *n, const double *x, double *fvec, int *iflag );

First two args are size, 3rd is an input array, 4th is an output array, and last one is reserved for the parent function.

Now, my concern is that my function needs other data to perform the computation, and there is no way in this prototype to get user-data. Simply speaking, let's assume that the 3rd-party library exports such a function:

int makeSomeComputation(fn user_function, const int *m, const int *n, double *x, double *fvec, int *info, ...);

(There may be more arguments, let's pass over them). So in my code, I am supposed to do something like that:

void myFunc(const int *m, const int *n, const double *x, double *fvec, int *iflag) {
     //do something here
}
//and in another function
{ //...
    makeSomeComputation(myFunc, &m, &n, &x, &fvec, &info, ...);
}

As you see, there is no dedicated way to pass user-data (or user-callback).

I am quite reluctant to use static variables here because some day I may want to use my code in a multithreaded / distributed environment.

Is there another option?

Note: the 3rd-party is actually open-source, so I think I will change the library to add an extra parameter, but I don't like that neither.

3 answers

1

Указатель на объект структуры, соответствующим образом преобразуется, указует на его первоначальный член, так что вы можете передать указатель , intа затем (в вашем функции обратного вызова) приведение к вашему struct(содержащим больше данных):

#include <stdio.h>

struct cont {
    int iflag;
    char *more;
};

static void func(int *a, int *iflag)
{
    struct cont *b = (struct cont *)iflag;

    printf("%d\n", *a);
    printf("%d\n", b->iflag);
    printf("%s\n", b->more);
}

int main(void)
{
    int a = 5;
    struct cont b = {1, "Hello"};

    func(&a, (int *)&b);
    return 0;
}
2

Вы можете создать набор функций - разные только в глобальном (может быть статическими) переменными они используют.

void fn_implementation(const int *m, const int *n, const double *x, double *fvec, int *iflag, TheDataTypeYouNeed* theDataYouNeed)
{
 /* implementation */
}

Имея это - в каждом файле вы можете создать необходимый набор обертки fn_implementation функций, например с помощью этих макросов:

#define FN(global) fn_##global
#define DEFINE_FN(global) static void FN(global)(const int *m, const int *n, const double *x, double *fvec, int *iflag){ \
     fn_implementation(m, n, x, fvec, iflag, &global); \
     }

И использовать так:

static TheDataTypeYouNeed global1;
static TheDataTypeYouNeed global2;

DEFINE_FN(global1);
DEFINE_FN(global2);

int main() {
     global1.x = 7;
    callLibrary(&FN(global1));
     global2.x = 6;
    callLibrary(&FN(global2));

}

Что касается доступа большого количества потоков - рассмотреть возможность использования pthread_key . Есть также некоторые расширения компилятора - как __thread в НКУ

0

При выделении места для x, выделить больше , чем это необходимо.

Вместо того x = malloc(sizeOfDataNeededBy3rdParty);, написать:x = malloc(sizeOfDataNeededBy3rdParty + sizeOfExtraDataIWantToPassToMyFunc);

Вы можете поместить любые данные, которые вы хотите в дополнительное пространство, приложенное к данным, требуемых 3-партия.

Будьте осторожны , чтобы не нарушать строгие правила наложения спектров. Вы , вероятно , нужно использовать , memcpyчтобы скопировать дополнительные данные в x, а также копировать дополнительные данные из x.