Derek Nheiley
Derek Nheiley

Reputation: 41

lambdas as instance function pointers

my type aButton has a function pointer so i can define custom actions for each button, i though the easiest way to do this would be to create a lambda and dereference it and pass it to the function pointer of that aButton instance, since i need non-static access to objects outside of the scope of the button class

but i'm stuck trying to figure out how to cast it to the right type and how to call it without getting the errors below... i haven't see many people have luck with this, and using functional doesn't look like i can pass in context???

    // Example program
    #include <iostream>
    #include <string>

    int global1 = 0;
    int global2 = 5;

    class aButton {
    public:
        int status, oldStatus;
        aButton(int initStatus) { oldStatus = status = initStatus; }
        int (aButton::*action)();
    };

    class Thingy {
    private:
        int mode  = 1;
        int value = 0;
    public:
        void reset() { value = 0; }
        void setMode(int newMode) { mode = newMode; }
        void increment() { value = value + global2; }
        //accessors & mutators
    };

    void specialFunction(Thingy *thingyToWorkOn) {
        //do stuff...
    }
    void anotherSpecialFunction(Thingy *firstThingy, Thingy *secondThingy) {
        //more stuff...
    }

    int main() {

        Thingy one;
        Thingy two;

        aButton *on = new aButton(0);
        aButton *speedUp = new aButton(0);

        on->action = &(     //error: taking address of temporary [-fpermissive]
            [&]() {         //error: cannot convert 'main()::<lambda()>*' to 'int (aButton::*)()' in assignment

                //some specific stuff....
                global1 = 1;
                if (global2 < 10) {
                    global2++;
                }
                one.reset();
                two.reset();
                anotherSpecialFunction(&one, &two);
                std::cout << "on action \n";
                return 1;
            }
        );

        speedUp->action = &(    //error: taking address of temporary [-fpermissive]
            [&]() {             //error: cannot convert 'main()::<lambda()>*' to 'int (aButton::*)()' in assignment

                //some specific stuff....
                if (global1) {
                    one.setMode(global2);
                    two.setMode(global2);
                    specialFunction(&one);
                    specialFunction(&two);
                    std::cout << "speedUp action \n";
                    return 1;
                }
                return 0;
            }
        );


        for(int i=0; i<5; i++) {
            //if on pushed
            (on->(on->action))();           //error: expected unqualified-id before '(

            //if speedUp pushed
            (speedUp->(speedUp->action))(); //error: expected unqualified-id before '(
        }

    }

Upvotes: 1

Views: 154

Answers (3)

Derek Nheiley
Derek Nheiley

Reputation: 41

Thanks guys, i guess functional was what i was looking for after all... this seems to do what i want on http://cpp.sh/8ll i guess i just got confused cause a lot of the functional examples had them as arguments for callbacks instead

    // Example program
    #include <iostream>
    #include <string>
    #include <functional>

    int global1 = 0;
    int global2 = 5;

    class aButton {
    public:
        int status, oldStatus;
        aButton(int initStatus) { oldStatus = status = initStatus; }
        std::function<int()> action;
    };

    class Thingy {
    private:
        int mode  = 1;
        int value = 0;
    public:
        void reset() { value = 0; }
        void setMode(int newMode) { mode = newMode; }
        void increment() { value = value + global2; }
        //...
    };

    void specialFunction(Thingy *thingyToWorkOn) {
        //do stuff...
    }
    void anotherSpecialFunction(Thingy *firstThingy, Thingy *secondThingy) {
        //more stuff...
    }

    int main() {

        Thingy one;
        Thingy two;

        aButton *on = new aButton(0);
        aButton *speedUp = new aButton(0);

        on->action = std::function<int()>(
            [&]() -> int{

                //some specific stuff....
                global1 = 1;
                if (global2 < 10) {
                    global2++;
                }
                one.reset();
                two.reset();
                anotherSpecialFunction(&one, &two);
                std::cout << "on action \n";
                return 1;
            }
        );

        speedUp->action = std::function<int()>(
            [&]() -> int{

                //some specific stuff....
                if (global1) {
                    one.setMode(global2);
                    two.setMode(global2);
                    specialFunction(&one);
                    specialFunction(&two);
                    std::cout << "speedUp action \n";
                    return 1;
                }
                return 0;
            }
        );


        for(int i=0; i<5; i++) {
            //if on pushed
            on->action();

            //if speedUp pushed
            speedUp->action();
        }

    }

Upvotes: 0

Wojtek Surowka
Wojtek Surowka

Reputation: 20993

You cannot assign pointer to lambda to pointer to member function. Pointers to member functions may point only to member functions.

You can indeed use std::function from <functional> instead. Declare your action as

std::function<int()> action;

so instead of using member functions you will use global functions. Of course you need to get rid of & operators for lambdas. And you need to modify the way of calling action.

Upvotes: 0

dlf
dlf

Reputation: 9383

I believe that you want aButton::action to be of type std::function<int()> (read: function that takes nothing and returns int) rather than int (aButton::*). This requires the <functional> header. With that change, your assignments could stay the same (minus the leading address-of operator), though as you figured out, you'll need to explicitly state the return type with -> int. The calls would simply take the form (e.g.):

on->action();

One other note: be very careful about capturing local variables (one, two) by reference. If the function really is main then I suppose it's ok since main won't return until the program ends, but otherwise it would be asking for trouble.

Upvotes: 2

Related Questions