0

I'm using an API call from a library that takes a void function(void) pointer as a parameter

 static inline void attach(void(*userFunc)(void));

And I have a class Button with a member method void buttonClicked().

What I want is to basically make a static function dynamically at runtime that will be like:

void functionForThisSpecificObject(){
    theSpecificObject.buttonClicked();
}

I tried with a lambda like this:

 Button* self = this;
 attach([self](){self->buttonClicked();});

But then it can't be used as a static function pointer because it is a capturing lambda...

I don't want to create a static function for every object that I will be using manually, so how can this be done dynamically?

Thank you in advance for any help.

Lake
  • 21
  • 1
  • 4
  • 2
    Are you sure that the API does not include a `void* userData` somewhere? Well-designed callback interfaces do. – molbdnilo Oct 28 '20 at 18:45
  • @molbdnilo No, it simply doesn't, here is the real function, it's an arduino interrupt `static inline void attachPinChangeInterrupt(const uint8_t pcintNum, void(*userFunc)(void), const uint8_t mode)`. Besides, this is a simple thing to do in every other OOP language I know of, so there must be a way to do it in C++, right? I don't understand why a capturing lambda isn't a static function. But again, I'm not familiar with c++ so I could be completely wrong about everything I said. – Lake Oct 28 '20 at 18:51
  • 1
    A capturing lambda has to store what it captures somewhere, and a function pointer can't store any "extra data". If you can live with one callback per interrupt number (and/or mode), you can have a table that maps them to objects. – molbdnilo Oct 28 '20 at 19:02
  • @molbdnilo can't the value of something captured by value in the lambda be assigned to a local variable inside the said static function? How can I assign a value inside the lambda to a local variable? (I don't need to change this value ever since I'll create a lambda for each method I want to convert to a function pointer) – Lake Oct 28 '20 at 19:08
  • Local variables are not stored *in* the function; they don't exist until the function is called. – molbdnilo Oct 28 '20 at 19:12
  • @RationalFragile the problem is not that c++ would not provide the tools to do that, but that the API you use uses a c style function pointer instead of a `std::function`. It isn’t a language restriction of c++ but a problem of the API. – t.niese Oct 28 '20 at 19:16
  • @t.niese is there at least a way to turn a function with a parameter to a dynamically aliased/named function that is without parameters? Like `void f(1)` to a `void f1()` and `void f(2)` to `void f2()`? – Lake Oct 28 '20 at 19:46
  • @RationalFragile c-style function pointer is just an address to a function location. The c++ standard does not provide a way to create new functions at runtime. You could do that manually by creating cpu instructions (and maybe there is a library that supports that) but with c++ and the std alone there is no way to achieve that. – t.niese Oct 28 '20 at 20:34
  • @t.niese Just wanted to add that writing and trying to execute instructions like that will make the executable look like malware and not work on systems with executable-space protection enabled. – ypnos Oct 28 '20 at 22:36
  • @ypnos That is a different problem, but it is not an uncommon thing, there are many applications that use it, basically every JIT Compiler does that, like Java Runtime, Browsers (JavaScript Engine, WebAssembly), any application based on Electron (like Discord, Slack, Microsoft Teams, VSCode, …). – t.niese Oct 29 '20 at 07:31

2 Answers2

1

What I want is to basically make a static function dynamically at runtime

There is no way to "make" functions dynamically at runtime in C++.

What you maybe could do is meta-programming: Write a program that generates the C++ source that defines the functions that you want. Due to lacking reflection features, this generation cannot be done within the language.

I don't want to create a static function for every object

You could potentially avoid this by using a variable in static storage:

extern Button* theVariableObject; // don't forget do define

void functionForThisSpecificObject(){
    theVariableObject->buttonClicked();
}

// somewhere
theVariableObject = &theSpecificObject1; // function will now use this object

theVariableObject = &theSpecificObject2; // and now another

Yes, global state is bad, but the API that you describe doesn't allow access to any other state so if you need state, then this is it.

Probably the ideal solution is to improve the library so that you aren't limited to only global state.


P.S.

Button* self = this;
[self](){self->buttonClicked();}

This could be simplified to:

[this](){buttonClicked();}
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I can't use a static variable because I have many objects of the same class that will get called with interrupts at random times so I have no way of switching between the correct values for that static variable. I need to be able to somehow at least convert a `void f(p)` to `void f(void)` dynamically, so basically like "aliasing" f(1) as f1() and f(2) as f2()... – Lake Oct 28 '20 at 19:43
  • @RationalFragile You said you want to use the function with `attachPinChangeInterrupt`. Why can't you have a single static function that dispatches the interrupt event? The static variable could be/point to a set of receiving objects that need be notified. – ypnos Oct 28 '20 at 20:54
  • @ypnos how will the static function dispatch the call to a class when it (the static function dispatcher) will have no way of knowing to what button to dispatch the call? The whole issue is that the API callback doesn't have any parameters and only accepts static functions, so how can I possibly decide which button caused the interrupt? My current dumb solution is to literally write 21 static functions and keep track of all pins which negates the whole purpose of the library. – Lake Oct 28 '20 at 21:11
  • @RationalFragile create a static/global list of object method pointers, store the button click method in that list, then have the static dispatcher run through that list calling each method. – Remy Lebeau Oct 28 '20 at 21:13
  • @RationalFragile Yes, you will need a single static method for each pin you are interested in. Lucky for you it is a fixed number. It is stupid but you can hide it away from the rest of your code with a dispatcher. Or you write a ticket to the library author or even provide a pull request. – ypnos Oct 28 '20 at 22:29
0

A capturing lambda won't work, as it is not convertible to a C-style function pointer. Only a non-capturing lambda can be converted.

Since the library in question does not allow you to pass a user-defined value to the callback function, there are basically only 2 ways to solve this:

  • store the object pointer in a global variable, and then use a standalone callback function to act on that variable.

    Button *btn;
    
    void clickTheButton() {
        btn->buttonClicked();
    }
    
    ...
    
    {
        btn = this;
        attach(&clickTheButton);
    }
    

    Obviously, this will only work if you have 1 Button.

  • use a thunk for the callback.

    Dynamically allocate a block of memory at runtime, store some CPU instructions in it to CALL a function in your app's code, and store the value of the Button* pointer in the appropriate bytes so that value gets passed to that function. Grant the memory block execution rights, and pass it to the library.

    When the library calls the thunk, its instructions will be executed like a normal function, and they can then call into your normal code passing it the correct Button* pointer to act on.

    For instance, the thunk could CALL the Button::buttonClicked() method directly, passing the stored Button* in the this parameter, according to the calling convention of buttonClicked.

    This is a very advanced, platform/CPU-specific technique, but it is a powerful one. I would not suggest trying to code this yourself, unless you have experience in assembly coding. Otherwise, use a pre-existing library that implements this for you. There is nothing provided natively in C++ to handle this.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thank you. Could you provide a very simple example for writing a dynamic function (the second solution you suggested) that will allow wrapping a function with one parameter in a parameterless function pointer? – Lake Oct 28 '20 at 21:24
  • The code needed is different depending on which CPU and OS you are targetting, which is why I didn't provide an example. There are plenty of questions on StackOverflow related to working with thunks, for instance [How to thunk a function in x86 and x64? (Like std::bind in C++, but dynamic)](https://stackoverflow.com/questions/12136309/), [How to bind this pointer to static member function using thunk in c++](https://stackoverflow.com/questions/40318570/), [Can we implement c++ thunk in linux?](https://stackoverflow.com/questions/7378772/), etc – Remy Lebeau Oct 28 '20 at 21:35