Как передать переменные в пользовательские функции обратного вызова

Я хочу использовать библиотеку Ticker ядра ESP8266 Arduino для (асинхронной) задержки переключения вывода в желаемое состояние, как показано ниже. Я не уверен в определениях функций «здесь», и мне трудно найти какую-либо документацию. Пожалуйста, взгляните на то, что я пробовал до сих пор:

#include <Ticker.h>

Ticker change_pin;

...

    uint8_t desired_state = 1;
    ...
    change_pin.attach_ms(100, [](uint8_t state_to_change_to){
        if( digitalRead(PIN) != state_to_change_to ) {
            digitalWrite(PIN, state_to_change_to);
        }
        else {
            change_pin.detach();
        }
    }, desired_state);

Но компилятор вопит:

espIKEA:145: error: no matching function for call to 'Ticker::attach_ms(int, toogle_light(uint8_t)::__lambda0, uint8_t&)'

   }, des_state);

               ^

...

C:\Users\USER\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\libraries\Ticker/Ticker.h:45:7: note:   candidate expects 2 arguments, 3 provided

C:\Users\USER\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\libraries\Ticker/Ticker.h:62:7: note: template<class TArg> void Ticker::attach_ms(uint32_t, void (*)(TArg), TArg)

  void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)

       ^

C:\Users\USER\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\libraries\Ticker/Ticker.h:62:7: note:   template argument deduction/substitution failed:

C:\Users\USER\Documents\Arduino\Scratch\pintest\pintest.ino:145:15: note:   mismatched types 'void (*)(TArg)' and 'toogle_light(u8)::__lambda0'

   }, des_state);

               ^

Итак, у меня слишком мало знаний, чтобы понять, что с этим делать. Я действительно хотел бы избежать отдельной функции для тела обратного вызова.

PS: важный исходный файл можно найти здесь: Ticker.h

, 👍4


2 ответа


Лучший ответ:

2

Функция, которую вы пытаетесь вызвать, является функцией шаблона:

template<typename TArg>
void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)
{
    ....
}

Итак, вам нужно указать, какой тип параметра вы передаете:

change_pin.attach_ms<uint8_t>(100, [](uint8_t state_to_change_to) {
//                                       ^- сообщаем компилятору о параметре
    ....
}, desired_state);

Чтобы передать более одного параметра: поскольку библиотека допускает только один параметр, я бы создал struct и передал бы на нее указатель.

struct callback_parameter {
    int i;
    char *p;
};

Затем создайте одну из этих структур и передайте ее адрес. Вам просто нужно убедиться, что структура действительна все время, пока работает таймер. Поэтому либо используйте new, static, либо сделайте его глобальным.

callback_parameter param; // глобальная переменная

...

change_pin.attach_ms<callback_parameter *>(100, [](callback_parameter * state_to_change_to) {
    ....
}, &param);
,

компилируется сейчас. Спасибо! как насчет передачи нескольких параметров? <uint8_t, char*> ?, @milkpirate

@milkpirate Я обновил ответ., @Johnny Mopp


1

Похоже, вы пытаетесь создать лямбда-функцию, и это слишком современно для компилятора Arduino.

Итак, у вас есть:

change_pin.attach_ms(100, ????, desired_state);

Это просто ???? нам нужно отсортировать, что у вас есть:

void DoStuff (state_to_change_to)
{
    if( digitalRead(PIN) != state_to_change_to ) 
    {
        digitalWrite(PIN, state_to_change_to);
    }
    else 
    {
        change_pin.detach();
    }
}

Теперь вы не можете просто определить собственную сигнатуру функции, она должна быть либо callback_t, либо callback_with_arg_t. Итак, чтобы ваша функция "DoSTuff" соответствовала callback_with_arg_t, вы должны определить ее как:

void DoStuff (void* argument);

Что означает, что внутри вашей функции вам придется сделать следующее:

void DoStuff (void* argument)
{
    if (argument != NULL)
    {
       const uint8_t state_to_change_to = *argument;
       if( digitalRead(PIN) != state_to_change_to ) 
       .... As above.

Имеет ли это смысл?

Библиотека определяет сигнатуру функции, которая действует как обратный вызов, иначе она не сможет предоставить все параметры. void* позволяет обойти любое притязание на безопасное поведение типов, и это нормально, если вы внимательно следите за тем, что может быть передано через эти параметры. Всегда помните, если вы работаете асинхронно, нет никакой гарантии, что данные все еще будут существовать, когда вы их обрабатываете, если их динамическая память могла быть освобождена/удалена или переменные могут выйти за пределы области видимости.

,