Я посмотрел на пример , связанный в вопросе OP.
Соответствующая часть:
void setup(){
// contents skipped
WiFi.onEvent(WiFiEvent);
// contents skipped
}
Тем самым WiFiEvent
- это бесплатная функция, котораяопределено выше:
void WiFiEvent(WiFiEvent_t event, system_event_info_t info){
switch(event){
// some cases to handle various events
default:
break;
}
}
ОП хочет преобразовать этот обработчик событий в свой class ApiClient
:
class ApiClient
{
public:
ApiClient(String apiUrl);
void sendValue(String key, String value);
void wpsInitConfig();
void WiFiEvent(WiFiEvent_t event, system_event_info_t info);
String wpspin2string(uint8_t a[]);
String requestUrl;
String _apiUrl;
int chipid;
private:
};
Существенное отличие состоит в том, что WiFiEvent()
становится функцией-членом из-за этогои OP получил сообщение об ошибке
no instance of overloaded function "WiFiClass::onEvent" matches the argument list -- argument types are: (void (system_event_id_t event, system_event_info_t info)) -- object type is: WiFiClass
Из любопытства я немного покопался в проекте github и наконец нашел объявление WiFiClass::onEvent()
- оно унаследовано от class WiFiGenericClass
:
class WiFiGenericClass
{
public:
WiFiGenericClass();
wifi_event_id_t onEvent(WiFiEventCb cbEvent, system_event_id_t event = SYSTEM_EVENT_MAX);
wifi_event_id_t onEvent(WiFiEventFuncCb cbEvent, system_event_id_t event = SYSTEM_EVENT_MAX);
wifi_event_id_t onEvent(WiFiEventSysCb cbEvent, system_event_id_t event = SYSTEM_EVENT_MAX);
// a lot more - skipped
};
Таким образом, на самом деле существует три объявления onEvent()
с двумя параметрами, поэтому каждый из параметров 2 nd имеет аргумент по умолчанию.(Следовательно, вызов WiFi.onEvent(WiFiEvent);
только с одним аргументом в примере был в порядке.)
Чтобы полностью разобраться в этом, я искал WiFiEventCb
, WiFiEventFuncCb
и WiFiEventSysCb
и нашел их втот же самый файл заголовка выше class WiFiGenericClass
:
typedef void (*WiFiEventCb)(system_event_id_t event);
typedef std::function<void(system_event_id_t event, system_event_info_t info)> WiFiEventFuncCb;
typedef void (*WiFiEventSysCb)(system_event_t *event);
Это то, что означают три typedef
s:
WiFiEventCb
... указатель функции на(бесплатная) функция, которая возвращает void
и имеет один параметр типа system_event_id_t
WiFiEventFuncCb
... объект std::function
для всего, возвращающего void
и имеющего два параметра типов system_event_id_t
и system_event_info_t
WiFiEventSysCb
... указатель на функцию (свободную), которая возвращает void
и имеет один параметр типа system_event_id_t*
.
Очевидно, в примере использовалось значение 2 nd onEvent()
, поскольку оно единственное, принимающее функции с двумя параметрами.
Поддержка std::function
очень хороша, потому чтоон принимает все вызываемые с соответствующей сигнатурой:
- свободные функции,
- функторы,
- лямбды (которые на самом деле ничего не значаткроме одного из первых).
Итак, чтобы сделать ApiClient::WiFiEvent()
совместимым, есть два варианта:
- объявить
ApiClient::WiFiEvent()
как статическую функцию-член - связать
ApiClient::WiFiEvent()
с экземпляром, с которым он вызывается.
Первый параметр ограничивает удобство использования ApiClient::WiFiEvent()
, так как static
функции-члены вызываются без экземплярасоответственноучебный класс.Недостаток - в функции-члене нет экземпляра (т. Е. Явный или неявный доступ к this
запрещен), который может быть или не быть приемлемым.
Второй вариант можно легко реализовать с помощью лямбда в качестве адаптера.Для этого необходимо изменить регистрацию события в ApiClient::ApiClient()
:
//ERROR: wifi.onEvent(WiFiEvent);
//Instead:
wifi.onEvent(
[this](WiFiEvent_t event, system_event_info_t info) {
this->WiFiEvent(event, info);
});
Это эффективно регистрирует функтор с принятой подписью, которая захватывает this
из ApiClient
, так что действительный вызов членаФункция с экземпляром может быть сделано.Возвращаемый тип лямбды неявно объявляется void
, поскольку в теле лямбды нет return
.
Наконец, я хотел бы отметить, что захват в лямбдах - это то, что должно бытьсделано тщательно.Если wifi
переживает this
(т. Е. Экземпляр ApiClient
), то он может вызвать ApiClient::WiFiEvent()
без действительного this
-точки.
Чтобы сделать его "пуленепробиваемым", деструкториз ApiClient
может вызвать removeEvent()
, используя wifi_event_id_t
, который возвращается onEvent()
.(Это должно быть сохранено в ApiClient
для этой цели.)