4

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.

Bentoy13
  • 4,886
  • 1
  • 20
  • 33
  • Read wikipages on [closure](https://en.wikipedia.org/wiki/Closure_%28computer_programming%29)s and [callback](https://en.wikipedia.org/wiki/Callback_%28computer_programming%29)s – Basile Starynkevitch Sep 22 '15 at 08:03
  • @BasileStarynkevitch It's exactly what I am talking about, but in C I only know two means to implement closures: either with static variables, or by passing an extra pointer on something (either a callback or some data). I expose these two solutions in my question; I wonder if there is another way to achieve that. – Bentoy13 Sep 22 '15 at 08:07
  • 1
    If it's open source, go for the latter part, add an extra parameter and try to get that into the upstream project. (Though you might need to keep the existing `makeSomeComputation` for compatibility reasons, and instead create a new makeSomeComputationEx that has the additional callback cookie) – nos Sep 22 '15 at 08:22
  • @nos Indeed, I think it would be the best solution. My reluctance about that is about keeping compliance with the next versions of the library, I will need to transfer my extra code each time I want to update. But I think I will to that, it's a minimum pain for a very nice feature. – Bentoy13 Sep 22 '15 at 08:25

3 Answers3

2

You can create set of functions - different only in a global(might be static) variable they use.

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

Having this - in every file you can create necessary set of wrapper to fn_implementation functions, e.g. by these macros:

#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); \
     }

And use like this:

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

}

Regarding many threads access - consider to use pthread_key. There are also some compiler extensions - like __thread in gcc

Community
  • 1
  • 1
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • So I need to write in code as many closures as needed ... It may be not very compliant with modular programming. However, it's a solution to my problem, thk. – Bentoy13 Sep 22 '15 at 08:26
  • @Bentoy13 yes, indeed, it is not perfect. Frankly - I just implemented in C a C++ way I know, i.e. to use function template with pointer to global variable as parameter: - `template fn(....) {}` – PiotrNycz Sep 22 '15 at 10:49
  • 1
    Interesting way of development, trying to translate high-level design into classic low-level C! – Bentoy13 Sep 22 '15 at 11:48
1

A pointer to a structure object, suitably converted, points to its initial member, so you can pass a pointer to int and then (in your callback) cast to your struct (containing more data):

#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;
}
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • I had this in mind, but in fact there is a problem about that: since `func` is intended to be called by another function (as a callback), I don't know where it takes the inputs, it may be different from the ones I give to the parent function. – Bentoy13 Sep 22 '15 at 08:10
  • @Bentoy13 You can add header to your custom struct and then check before to access it. – LPs Sep 22 '15 at 08:19
  • @LPs In the case the struct is not the good one, I won't be able to run my function. Adding a header is nice, I can even merely check the pointer (since all parameters must be pointers). – Bentoy13 Sep 22 '15 at 08:28
1

When you allocate space for x, allocate more than it needs.

Instead of x = malloc(sizeOfDataNeededBy3rdParty);, write: x = malloc(sizeOfDataNeededBy3rdParty + sizeOfExtraDataIWantToPassToMyFunc);

You can put whatever data you want into the extra space appended to data required by 3rd-party.

Be careful not to break strict aliasing rules. You probably need to use memcpy to copy extra data into x, and also to copy the extra data out from x.

cshu
  • 5,654
  • 28
  • 44
  • Same comment as for the answer of [Alter Mann](http://stackoverflow.com/a/32711623/1507014). The library may pass another pointer to my callback, and if it does, I cannot get back my extra data. – Bentoy13 Sep 22 '15 at 08:29
  • @Bentoy13 so all 5 arguments can be different from what you pass to `makeSomeComputation`? Not even one is guaranteed to be the same? – cshu Sep 22 '15 at 08:35
  • In theory yes. The inputs of `makeSomeComputation` are not specified to be passed directly to the callback in the doc. I can check if it's the case, but a future implementation may change this. – Bentoy13 Sep 22 '15 at 08:37