Как определить лямбда-функцию, чтобы иметь возможность захватывать указатель this класса?

У меня есть класс времени, который отслеживает время.

Я хочу, чтобы другие мои классы могли зарегистрировать функцию обратного вызова, если день изменится.

У меня есть этот рабочий фрагмент:

#define MAX_CB_CHANGE_FUNCTIONS 50
typedef void (*dayChangeFunc)(int prevDay,int nowDay);

class TimeSystem {
    private:
        // array for the function pointers.
        dayChangeFunc dayChangeFunctions[MAX_CB_CHANGE_FUNCTIONS];
        // emit function if the day changes.
        void emitDayChange(int prev, int now);
    public:
        void regDayChange(dayChangeFunc);
};


/*
*   Registering a day change function.
*   Maximum function pointer count is MAX_CB_CHANGE_FUNCTIONS ( see timeSys.h )
*/
void TimeSystem::regDayChange(void (*dayChangeFunc)(int prevDay,int nowDay)){
    if ( currDayCbIndex >= MAX_CB_CHANGE_FUNCTIONS ){
        if (isDebugOn){
            debug.print(DEBUG_WARN,"[Time_sys] - Can not register an other day change function.\n");
            return;
        }
    }
    dayChangeFunctions[currDayCbIndex] = dayChangeFunc;
    currDayCbIndex++;
}

/*
*   Emitting day change to every registered function.
*/
void TimeSystem::emitDayChange(int prev, int now){
    // Preventing change on initialization.
    if ( prev == 0 ){ return; }
    for (size_t i = 0; i < MAX_CB_CHANGE_FUNCTIONS; i++){
        if (dayChangeFunctions[i]){
            dayChangeFunctions[i](prev,now);
        }
    }
}

Это работает, и другие классы могут использовать это следующим образом:

timeSys.regDayChange([](int prevDay,int nowDay){
    Serial.printf("Day changed from prev: %d to now: %d\n",prevDay,nowDay);
});

Теперь я хочу захватить указатель this в лямбде, чтобы иметь возможность вызывать некоторую внутреннюю функцию класса.

timeSys.regDayChange([this](int prevDay,int nowDay){
    callSomeInternalFunction();
});

Ошибка, которую я получаю:

no suitable conversion function from "lambda [](int prevDay, int nowDay)->void" to "dayChangeFunc"

Как я могу определить свою лямбду, чтобы иметь возможность захватить указатель «этот»?

******* Обновлено: РЕШЕНО ********

Вот модификации:

Мне пришлось определить тип моей лямбда-функции следующим образом:

using dayChangeFunc = std::function<void(int, int)>;

Определите функцию регистра указателя функции следующим образом:

void regDayChange(dayChangeFunc cb);

И перепишите объявление так:

/*
*   Registering a day change function.
*   Maximum function pointer count is MAX_CB_CHANGE_FUNCTIONS ( see timeSys.h )
*/
void TimeSystem::regDayChange( dayChangeFunc cb ){
    if ( currDayCbIndex >= MAX_CB_CHANGE_FUNCTIONS ){
        if (isDebugOn){
            debug.print(DEBUG_WARN,"[Time_sys] - Can not register an other day change function.\n");
            return;
        }
    }
    dayChangeFunctions[currDayCbIndex] = cb;
    currDayCbIndex++;
}

И теперь я могу использовать его в любом классе следующим образом:

class SampleClass(){
  private:
     int exampleCounter = 0;
  public:
    void init(){
      TimeSystem timeSys;
      timeSys.regDayChange([this](int prevDay,int nowDay){
        Serial.printf("Day changed from prev: %d to now: %d\n",prevDay,nowDay);
        exampleCounter++;
      });
    }
}

Каков ваш стандарт С++?

local-ninja 26.10.2022 12:38

Стандарт PlatformIO AFAIK - С++ 11

Dr.Random 26.10.2022 12:47

Измените тип обратного вызова на std::function<void(int, int)>.

molbdnilo 26.10.2022 12:49

Как это? typedef std::function<void(int, int)>;

Dr.Random 26.10.2022 12:51

Я получил следующую ошибку: объявление ничего не объявляет [-fpermissive]

Dr.Random 26.10.2022 12:51

Вы забыли назвать тип. using dayChangeFunc = std::function<void(int, int)>; или typedef std::function<void(int, int)> dayChangeFunc;. (Вряд ли кто-то использует typedef в наши дни.)

molbdnilo 26.10.2022 12:53

Как это будет выглядеть как параметр функции? Это не работает сейчас:

Dr.Random 26.10.2022 12:55

void TimeSystem::regDayChange(void (*dayChangeFunc)(int prevDay,int nowDay))

Dr.Random 26.10.2022 12:55

объявление несовместимо с "void TimeSystem::regDayChange(dayChangeFunc)"

Dr.Random 26.10.2022 12:57

Он будет выглядеть так же, как и любой другой тип; void TimeSystem::regDayChange(dayChangeFunc func);. (Вы запутались, используя то же имя для параметра, что и для псевдонима типа; обычно можно было бы написать именно это даже с вашим псевдонимом типа.)

molbdnilo 26.10.2022 12:57

Мое определение выглядит так: void regDayChange(dayChangeFunc); и мое объявление выглядит так: void TimeSystem::regDayChange(void (*dayChangeFunc)(int prevDay,int nowDay)){}, но это не работает. Массив этих указателей функций подходит для этого типа

Dr.Random 26.10.2022 12:59

О, он скомпилирован. Большое спасибо!! Можете ли вы опубликовать это как решение?

Dr.Random 26.10.2022 13:02
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
12
67
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Лямбда-функция, которая захватывает что-либо, не может быть преобразована в указатель на функцию. Вам нужно передать указатель вручную или иметь «пул» функций, каждая из которых хранит текущий указатель this глобально.

Могу ли я как-то использовать для этого std::function?

Dr.Random 26.10.2022 12:48

@Dr.Random Нет, если только вы не измените его и при обратном вызове. std::function также нужен указатель. То, о чем вы просите, просто не работает в С++.

lorro 26.10.2022 12:49

@lorro Какой указатель понадобится для this-захватывающей лямбды?

molbdnilo 26.10.2022 12:52

Весь класс, который его вызывает.

Dr.Random 26.10.2022 12:52

Я регистрирую функцию обратного вызова внутри различных классов и хочу делать вещи, связанные с классом, внутри обратного вызова.

Dr.Random 26.10.2022 12:53

Я решил это с помощью @molbdnilo

Dr.Random 26.10.2022 13:03
Ответ принят как подходящий

Измените тип обратного вызова на std::function<void(int, int)>

using dayChangeFunc = std::function<void(int, int)>;

или

typedef std::function<void(int, int)> dayChangeFunc;

и изменить прототип функции,

void TimeSystem::regDayChange(dayChangeFunc func).

(Если бы вы постоянно использовали псевдоним вашего типа, вам не нужно было бы менять прототип, а только сам псевдоним. Псевдонимы типов наиболее полезны, если вы их используете.)

Большое спасибо за ответ! Вы рекомендуете ключевое слово using или typedef?

Dr.Random 26.10.2022 13:15

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

molbdnilo 26.10.2022 13:18

Спасибо! Я буду читать об использовании немного больше.

Dr.Random 26.10.2022 13:21

Другие вопросы по теме