3

I want to call the following function and pass it a function with a parameter. The purpose of that is that it should call the function with my specified parameter so I know what triggered the function (in that case a gpio pin on the Raspberry Pi).

int wiringPiISR( int pin, int edgeType, void (*function)( void ) );

Currently I have:

for ( int i = 0; i < myValues.size(); ++i )
{
    int myValue = myValues[ i ];
    wiringPiISR( myValue, INT_EDGE_RISING, &myCallback( myValue ) );
}

Though this is giving me the following error:

error: lvalue required as unary ‘&’ operand

Which I can't really understand as to my understanding, myValue is an lvalue or is it not?

Is it what I want do even possible? If so how? The function wiringPiISR is from a library called wiringPi and I would like to avoid modifying it as much as possible.

3
  • Do you mean a function that takes a parameter, but have a predefined value for it so that it can be called without arguments? Commented Jun 12, 2015 at 21:23
  • Not exactly sure what you mean but I guess so. All I want to accomplish is to give the wiringPiISR a function to call with my parameter supplied so that wiringPiISR calls the function with the supplied parameter. Guess I used the wrong term here, edited the question. Commented Jun 12, 2015 at 21:29
  • 1
    &myCallback(myValue)) is the same as &(myCallback(myValue))) so you are taking the address on the return value of myCallback. since myValue is already the first parameter, you may only need to pass &myCallback. But we can't tell from the current context. Commented Jun 12, 2015 at 21:32

6 Answers 6

7

You could combine the answers from imreal and Ryan Haining something like this.

std::function<void()> cbfunc;

void myCallback()
{
  cbfunc();
}
void myWiringPiISR(int val, int mask, std::function<void()> callback)
{
  cbfunc = callback;
  wiringPiISR(val, mask, &myCallback);
}

... and then use it...

void myActualCallback(int v)
{
  ... do something...
}

myWiringPiISR(myValue, INT_EDGE_RISING, std::bind(myActualCallback, myValue));

No need to patch library, and you can use all the bind/function goodness. I'll leave you to find a way around the thread safety issues...

How does it work? Put simply 'std::bind' is binding together a function and it's parameters into a single std:function object which can then be 'called' from the myCallback function which acts as a shim around the callback that you pass. I'd given the callback function a confusing name before, but this edit has hopefully fixed that.

Sign up to request clarification or add additional context in comments.

7 Comments

@RyanHaining - Thanks. Globals are indeed lame, but I didn't want to clutter the answer further.
Could you explain a bit of what is going on here? It goes a little above my knowledge. How is the callback going to get the myValue argument? Your myCallback() doesn't take any arguments.
Thanks, the edit was quite helpful! It sounds a bit insane doing it this way but I happily take this as a solution.
It's not thread safe if you have two functions calling myWirinPiISR at the same time with different callbacks. The global 'cbFunc' object gets used by both. Obviously depends on what wiringPiISR actually does and how its meant to be used, so it may not be an issue.
I just realized this poses one big problem. It's not possible to call myWiringPiISR more than once, otherwise all my callbacks end up having the same argument. In my case shown in the answer it will always have the last iteration count of the for loop. So I don't really gain anything here, it would be the same as having a dedicated function for every possible outcome.
|
4

You can "vomit" the function. This doesn't require a user-defined mutable global variable and is thread-safe, unless you have a compiler that supports multiple threads but not per-thread exceptions which would be basically unusable.

myWiringPiISRWrapper(Value value, int edge, std::function<void()> func) {
    try {
        throw &func;
    } catch(...) {
        myWiringPiISR(value, edge, [] {
            try {
                throw;
            } catch(std::function<void()>* func) { 
                (*func)();
            }
        });
    }
}

It's disgusting and slow, but it's totally encapsulated which I think is a worthwhile upside. Note that this only works if the callback is never executed after the call to myWiringPiISR returns. In this case you can of course have a callback with whatever bound state you desire.

3 Comments

What do you mean I can never execute the callback after calling myWiringPiISR? The actual callback function is something I re-use quite a bit in my code at other points. Thread safe sound great though I have to be honest I don't understand how this solution works in any way. It goes massively above my head.
@ProfessorSparkles: It means that if myWiringPiISR stores the function pointer somewhere and then calls it at some random time in the future, very bad things will happen. It's perfectly legal for you to call the callback function whenever you want to. It's only bad if myWiringPiISR doesn't execute the callback right away, but stores it to be called back in response to some other thing which is not a call to myWiringPiISRWrapper.
Oh I see. Sadly that is exactly what wiringPiISR is doing in the background. It will call the callback at a totally random point in the future and will continue doing so. It handles user input and calls the callback every time there is user input.
3

If myValue is something you can decide at compile time, you could set it statically and use an intermediate function to pass in.

void myCallbackHelper() {
    static constexpr int myValue = 3;
    myCallback(myValue);
}

wiringPiISR(myValue, INT_EDGE_RISING, &myCallbackHelper);

If you need to determine myValue at run time, you could still accomplish this, but not really thread-safely.

int& getMyValue() {
    static int myValue;
    return myValue;
}

void setMyValue(int i) {
    getMyValue() = i;
}

void myCallbackHelper() {
    myCallback(getMyValue());
}

Then set it and call

setMyValue(3);
wiringPiISR(myValue, INT_EDGE_RISING, &myCallbackHelper);

5 Comments

Wouldn't that be the same as creating a dedicated function for every possible callback?
if your callback needs to take arguments, and you need to use a function pointer void(*)(void) then your options are going to be limited, and mildly painful
I would be interested in those mildly painful options, patching the library wiringPiISR every time I update will be a continuing mild pain, so I rather have that pain once.
Merge this answer with @imreals: Make you shim function call a std::function which is assigned in setmyvalue.
@ProfessorSparkles this is among the mildly painful
2

You need to use std::function and std::bind.

Change your function signature to

int wiringPiISR (int pin, int edgeType,  std::function<void()> func);

Inside you can call the callback simply using func()

And your call to:

int myValue = 3;
wiringPiISR(myValue, INT_EDGE_RISING, std::bind(myCallback, myValue));

What this does is create a std::function object (i.e. a callable) that wraps your function and keeps your desired value in its state.

This will only work on C++11 and newer.

3 Comments

So this is in no way possible without modifying the wiringPiISR function? I really would like to avoid that as it would mean I would need to patch the library its included in every time I update the library (which happens quite often).
It is necessary to change it.
Thanks, I give this a try if it's indeed my only option.
2

I looked up wiringPiISR and found that it is some sort of api call, so i am assuming you cannot change it.

Having said that, there is a reason most api-calls with a function-pointer-callback look sort of like this

void setCallback( void (*function)(void* data), void* userdata);

This allows people to cast their struct {blabla} data; to add some userdata, and when the function is called, it is passed along.

So basically, apart from hacking stuff with static variables, you can't pass any arguments.

Comments

0

If you have c++11, I suggest using std::function - it's quite a bit cleaner.

If not, your function signature is wrong. You want a callback with the type void(int) but your function takes a void()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.