0

I have a function which takes a callback as parameter, its signature is:

typedef void(*callback_type)(const* uint32_t data, unint8_t size);
void set_callback(callback_type callback);

The two parameters taken by the callback function are used by a struct so I would like to bind the callback to the struct. C++ has tools to deal with this kind of problems (member functions, lambdas, std::bind, ...). For example, the code I'm trying to achieve in C is equivalent to the C++ code:

set_callback([&](const uint32_t* data, uint8_t size) -> void {
    use_data(&the_struct, data, size);
});

The solution I'm looking for has some requirements:

  • Language: C (or assembly if anyone is willing to)
  • If possible with no imports (these including c standard library)
  • Be independent of the compiler (ie. no blocks from GCC or CLang)

Through my researches, I have found the following code, but it does not work in my case and does not really match requirements as it is dependent on non standard features provided by gcc (nested functions and statement expressions):

#define lambda(lambda$_ret, lambda$_args, lambda$_body)\
({\
lambda$_ret lambda$__anon$ lambda$_args\
lambda$_body\
&lambda$__anon$;\
}) 

Also note that it is possible to modify code in order to take a void pointer (as user data) in a callback which would solve the problem. This is the best solution I have so far and might be the cleanest overall. However, I'd like a solution that does not requires such trick.

Even if you only have a partial solution, I'd be happy to hear about it.

Thanks for taking the time for reading the question and have a good day!

Edit 1: The c++ code involving a lambda does not work (because the lambda captures...). For this to work, std::function or templated arguments must be used. But the point is still clear I think. Thanks to tstanisl for pointing this out.

Edit 2: When I wrote "used by a struct" this effectively makes no sense. What I meant was : a function will use the data provided (data and size parameters) to update members of the structure and do some other computation. I hope this is more clear. Thanks to '4386427' and 'Gaurav Pathak' for their comments.

Sacrefeu
  • 25
  • 3
  • 1
    Bind the callback to struct? Do you want to update some members of struct using the callback, but you do not want to use an extra argument in the callback to pass reference of the structure? – Gaurav Pathak Dec 30 '21 at 12:55
  • 1
    "The two parameters taken by the callback function are used by a struct... " That makes no sense... Some code can use a struct but a struct can't use anything – Support Ukraine Dec 30 '21 at 13:04
  • Actually C++ does not solve this problem either. `set_callback()` must be a template function to be able of accepting a lambda as a parameter – tstanisl Dec 30 '21 at 13:26

1 Answers1

0

Based on the understanding and per your requirement, as you mentioned that you cannot change the function prototype to add extra argument for passing reference of the structure, you can use a global structure variable, and then you can use that global variable inside the Callback function (hope you can edit the Callback function or provide a custom Callback function) to update the structure members (you need to take care of Synchronization issues).

To explain the above statement, I am providing a very simple example below:

#include<stdio.h>
#include<stdint.h>

typedef struct _the_struct {
    int data;
    int size;
}TheStruct;

TheStruct structVar;

typedef void(*callback_type)(const uint32_t *data, uint8_t size);

void set_callback(callback_type callback);

void my_callback(const uint32_t *data, uint8_t size);

void set_callback(callback_type callback) {
    int a = 5;
    callback(&a, 4);
}

void my_callback(const uint32_t *data, uint8_t size) 
{
    structVar.data = *data;
    structVar.size = size;
    
    printf("Data: %d Size: %d\n", *data, size);
}

int main(void)
{
    set_callback(my_callback);
    
    printf("StructData: %d StructSize: %d\n", structVar.data, structVar.size);
}

I hope the explanation provides some helpful information to you.

Gaurav Pathak
  • 1,065
  • 11
  • 28
  • That's the solution I have so far. But I don't really like global variable ^^'. – Sacrefeu Dec 30 '21 at 13:31
  • I am sorry, my dear, I have no idea how to change the value of a struct member without passing it as a parameter or without declaring it globally. – Gaurav Pathak Dec 30 '21 at 13:38
  • 1
    @GauravPathak, it could be done only with a help of a trampoline. GCC puts trampolines on stack. It would be nicer to allows having them on heap – tstanisl Dec 30 '21 at 13:40
  • @tstanisl In that case, I believe the callback function should contain a nested function having pointer to struct as argument, which in turn manipulates the struct member values. Please correct me If I am wrong, I read this Answer, https://stackoverflow.com/a/8179587/2805824 – Gaurav Pathak Dec 30 '21 at 13:49
  • 1
    @GauravPathak, there must be way to transform a function pointer into a pointer to the context. Trampoline is one way when every instance of function pointer has a pointer to context stored within the same object. GCC creates a trampoline on stack with implicit resolving of the context. That is very limiting. Using global variable is fine until mutlithreading or recursion is not involved and the lifetime of this "lambda" is fully controlled. – tstanisl Dec 30 '21 at 13:54