У меня есть класс времени, который отслеживает время.
Я хочу, чтобы другие мои классы могли зарегистрировать функцию обратного вызова, если день изменится.
У меня есть этот рабочий фрагмент:
#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++;
});
}
}
Стандарт PlatformIO AFAIK - С++ 11
Измените тип обратного вызова на std::function<void(int, int)>
.
Как это? typedef std::function<void(int, int)>;
Я получил следующую ошибку: объявление ничего не объявляет [-fpermissive]
Вы забыли назвать тип. using dayChangeFunc = std::function<void(int, int)>;
или typedef std::function<void(int, int)> dayChangeFunc;
. (Вряд ли кто-то использует typedef
в наши дни.)
Как это будет выглядеть как параметр функции? Это не работает сейчас:
void TimeSystem::regDayChange(void (*dayChangeFunc)(int prevDay,int nowDay))
объявление несовместимо с "void TimeSystem::regDayChange(dayChangeFunc)"
Он будет выглядеть так же, как и любой другой тип; void TimeSystem::regDayChange(dayChangeFunc func);
. (Вы запутались, используя то же имя для параметра, что и для псевдонима типа; обычно можно было бы написать именно это даже с вашим псевдонимом типа.)
Мое определение выглядит так: void regDayChange(dayChangeFunc); и мое объявление выглядит так: void TimeSystem::regDayChange(void (*dayChangeFunc)(int prevDay,int nowDay)){}, но это не работает. Массив этих указателей функций подходит для этого типа
О, он скомпилирован. Большое спасибо!! Можете ли вы опубликовать это как решение?
Лямбда-функция, которая захватывает что-либо, не может быть преобразована в указатель на функцию. Вам нужно передать указатель вручную или иметь «пул» функций, каждая из которых хранит текущий указатель this
глобально.
Могу ли я как-то использовать для этого std::function?
@Dr.Random Нет, если только вы не измените его и при обратном вызове. std::function
также нужен указатель. То, о чем вы просите, просто не работает в С++.
@lorro Какой указатель понадобится для this
-захватывающей лямбды?
Весь класс, который его вызывает.
Я регистрирую функцию обратного вызова внутри различных классов и хочу делать вещи, связанные с классом, внутри обратного вызова.
Я решил это с помощью @molbdnilo
Измените тип обратного вызова на 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?
Используйте using
. typedef
в наши дни почти никто не использует, а бывают даже ситуации, когда нельзя. Быть последовательным — это хорошо.
Спасибо! Я буду читать об использовании немного больше.
Каков ваш стандарт С++?