Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for lambda functions. #112

Closed
reifiedsteve opened this issue Nov 23, 2022 · 3 comments
Closed

Add support for lambda functions. #112

reifiedsteve opened this issue Nov 23, 2022 · 3 comments

Comments

@reifiedsteve
Copy link

reifiedsteve commented Nov 23, 2022

In particular, being able to use this useful little class with callbacks that are lambdas-with-context would make this much more useful for use with object member functions. For example:

class MyClass
{
public:

    MyClass()  : _button(7) {
        _button.attachClick([this]() { this->_myFunc(); });
    }

private:

    void _myFunc() {
        // ... do stuff when single clicked.
    }

    OneButton _button;
};

Something along the lines of what is already there, but just extrapolating it...

#include <functional>
...

class OneButton
{
public:
    ...
    typedef std::function<void()> callbackLambda;   // <== ADDED.
    ...
    callbackLambda _clickLambda;   // <== ADDED.
    ...
    void attachClick(callbackLambda newLambda) {    // <== ADDED new method.
        _clickLambda = newLambda;       
    }    

    ...
    void OneButton::Tick(bool activeLevel) 
    {
        ...
        case OneButton::OCS_COUNT:
            ...
            if (_nClicks == 1) {
                // this was 1 click only.
                if (_clickFunc) _clickFunc();
                if (_paramClickFunc) _paramClickFunc(_clickFuncParam);
                if (_clickLambda) _clickLambda();         // <== ADDED.

etc..

@gregismotion
Copy link

gregismotion commented Dec 28, 2022

EDIT: see my next comment for A solution.

Yes, this would be useful for my usage too.

#define LEFT_PIN 4
#define RIGHT_PIN 5
#define OK_PIN 6
class BtnHandler {
        OneButton leftBtn = OneButton(LEFT_PIN, true, true);
        OneButton rightBtn = OneButton(RIGHT_PIN, true, true);
        OneButton okBtn = OneButton(OK_PIN, true, true);

        State *state;
        public:
                BtnHandler(State *state_p) {
                        state = state_p;
                }

        OneButton *btns[3] = { &leftBtn, &rightBtn, &okBtn };

        void tickBtns() {
                for (unsigned int i = 0; i < sizeof(btns)/sizeof(btns[0]); i++) {
                        btns[i] -> tick();
                }
        }
        void setupBtns() {
                leftBtn.attachClick([](){addToTarget(-1);});
                rightBtn.attachClick([](){addToTarget(1);});
                okBtn.attachClick([](){
                        switch (*state) {
                                case SETUP: {
                                        if (cur_mult == (long) 1000 * 60 * 60) {
                                                *state = RUNNING;
                                        } else {
                                                cur_mult *= 60ul;
                                        }
                                        break;
                                }
                                case RUNNING: {
                                        *state = IDLE;
                                        break;
                                }
                                case IDLE: {
                                        *state = RUNNING;
                                        break;
                                }
                                case ALARM: {
                                        reset();
                                        break;
                                }
                        }
                });
        }
};

This sadly breaks with error: no matching function for call to 'OneButton::attachClick(void (BtnHandler::*)())'.

Though there could be a solution to this without changing the library as: note: candidate: void OneButton::attachClick(parameterizedCallbackFunction, void*) void attachClick(parameterizedCallbackFunction newFunction, void *parameter);. This means that we could use a workaround I've seen mentioned, but I'm not sure how to apply it for example to my particular use-case.

@gregismotion
Copy link

gregismotion commented Dec 28, 2022

EDIT: PR to docs in #114.

Okay, I made it work:

#define LEFT_PIN 4
#define RIGHT_PIN 5
#define OK_PIN 6
class BtnHandler {
        OneButton leftBtn = OneButton(LEFT_PIN, true, true);
        OneButton rightBtn = OneButton(RIGHT_PIN, true, true);
        OneButton okBtn = OneButton(OK_PIN, true, true);

        State *state;
        public:
                BtnHandler(State *state_p) {
                        state = state_p;
                }

        OneButton *btns[3] = { &leftBtn, &rightBtn, &okBtn };

        void tickBtns() {
                for (unsigned int i = 0; i < sizeof(btns)/sizeof(btns[0]); i++) {
                        btns[i] -> tick();
                }
        }
        void setupBtns() {
                leftBtn.attachClick([](){addToTarget(-1);});
                rightBtn.attachClick([](){addToTarget(1);});
                okBtn.attachClick([](void *ctx){
                        switch (*(((BtnHandler*)(ctx)) -> state)) {
                                case SETUP: {
                                        if (cur_mult == (long) 1000 * 60 * 60) {
                                                *(((BtnHandler*)(ctx)) -> state) = RUNNING;
                                        } else {
                                                cur_mult *= 60ul;
                                        }
                                        break;
                                }
                                case RUNNING: {
                                        *(((BtnHandler*)(ctx)) -> state) = IDLE;
                                        break;
                                }
                                case IDLE: {
                                        *(((BtnHandler*)(ctx)) -> state) = RUNNING;
                                        break;
                                }
                                case ALARM: {
                                        reset();
                                        break;
                                }
                        }
                }, this);
        }
};

Basically, we pass the context (so, this or rather, the object we're in) to the library, and it will give it back to the lambda. Thus the capturing issue was worked around. It's kinda ugly, but I think the issue can even be closed: if it can't be made prettier.

@mathertel
Copy link
Owner

The existing solution was added to the docu. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants