Add support for lambda functions.
reifiedsteve opened this issue · 3 comments
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..
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.
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.
The existing solution was added to the docu. Thanks