Рефакторинг примера кода в класс не вызывает экземпляра перегруженной функции - PullRequest
0 голосов
/ 18 февраля 2019

Я новичок в CPP и пытаюсь «рефакторинг» этого примера кода https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/WPS/WPS.ino в класс с именем ApiClient`.Затем я хочу сделать что-то вроде этого:

apiClient = ApiClient(myUrl);
apiClient.sendValue(key, value);

Все компилируется, кроме вызова функции wifi.onEvent(WiFiEvent);.Когда я копирую, вставляю весь пример кода в мой файл main.cpp, пример работает.Когда я использую свой «рефакторированный» подход, жалуется wifi.onEvent(WifiEvent).

Точное сообщение об ошибке:

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

Я знаю, что функция onEvent обычно принимает два аргумента, но почему это работает в коде примера?И как я могу решить это?

Это то, что я до сих пор:

main.cpp



#include "WiFi.h"
#include <esp_wps.h>
#include <Hythe22.h>
#include <ApiClient.h>

#define DHTPIN 14
// ?accessKey=ist_NJu3tjPIBCYeJd6DGGBxzq14LvungHoK&bucketKey=B37GHBNK5HL3
#define API_URL "https://groker.init.st/api/events";

Hythe22 dht(DHTPIN);
ApiClient apiClient;
uint64_t chipid;
#define ESP_DEVICE_NAME String(chipid)


void setup()
{
  String apiUrl = "https://myApi.Endpoint.com/event";
  Serial.begin(115200);
  delay(100);
  Serial.println();
}

void loop()
{

  String temp = String(dht.temperature);
  Serial.println("TEMP:" + temp);
  apiClient.sendValue("temperature", temp);

  String hum = String(dht.humidity);
  Serial.println("HUM:" + hum);
  apiClient.sendValue("humidity", hum);
}

ApiClient.h

/*
===========================================================
*/

#ifndef WebClient_h
#define WebClient_h

#include <Arduino.h>
#include <WiFi.h>
#include "esp_wps.h"
#include <HTTPClient.h>

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:
};

#endif

ApiClient.cpp


/*
===========================================================
*/

#include <Arduino.h>
#include <ApiClient.h>
#include <WiFi.h>
#include <esp_wps.h>
#include <HTTPClient.h>

int chipid;

#define ESP_WPS_MODE WPS_TYPE_PBC
#define ESP_MANUFACTURER "ESPRESSIF"
#define ESP_MODEL_NUMBER "ESP32"
#define ESP_MODEL_NAME "ESPRESSIF IOT"
#define ESP_DEVICE_NAME "ESP STATION"

String _apiUrl;
HTTPClient http;
String requestUrl;
WiFiClass wifi;

static esp_wps_config_t config;

ApiClient::ApiClient(String apiUrl)
{
    Serial.begin(115200);
    delay(10);

    Serial.println();

    wifi.onEvent(WiFiEvent);
    wifi.mode(WIFI_MODE_STA);

    Serial.println("Starting WPS");

    wpsInitConfig();
    esp_wifi_wps_enable(&config);
    esp_wifi_wps_start(0);
}

void sendValue(String key, String value)
{
    HTTPClient http;
    Serial.println("key:" + key);
    Serial.println("value:" + value);
    requestUrl = _apiUrl + "?" + key + "=" + value;
    // Serial.println(apiUrl);
    http.begin(requestUrl);

    int httpCode = http.GET();

    if (httpCode > 0)
    {
        Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        //file found at server --> on unsucessful connection code will be -1
        if (httpCode == HTTP_CODE_OK)
        {
            String payload = http.getString();
            Serial.println(payload);
        }
    }
    else
    {
        Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }
    http.end();
}

void ApiClient::wpsInitConfig()
{
    config.crypto_funcs = &g_wifi_default_wps_crypto_funcs;
    config.wps_type = ESP_WPS_MODE;
    strcpy(config.factory_info.manufacturer, ESP_MANUFACTURER);
    strcpy(config.factory_info.model_number, ESP_MODEL_NUMBER);
    strcpy(config.factory_info.model_name, ESP_MODEL_NAME);
    strcpy(config.factory_info.device_name, ESP_DEVICE_NAME);
}

String wpspin2string(uint8_t a[])
{
    char wps_pin[9];
    for (int i = 0; i < 8; i++)
    {
        wps_pin[i] = a[i];
    }
    wps_pin[8] = '\0';
    return (String)wps_pin;
}

void WiFiEvent(WiFiEvent_t event, system_event_info_t info){
  switch(event){
    case SYSTEM_EVENT_STA_START:
      Serial.println("Station Mode Started");
      break;
    case SYSTEM_EVENT_STA_GOT_IP:
      Serial.println("Connected to :" + String(WiFi.SSID()));
      Serial.print("Got IP: ");
      Serial.println(WiFi.localIP());
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      Serial.println("Disconnected from station, attempting reconnection");
      WiFi.reconnect();
      break;
    case SYSTEM_EVENT_STA_WPS_ER_SUCCESS:
      Serial.println("WPS Successfull, stopping WPS and connecting to: " + String(WiFi.SSID()));
      esp_wifi_wps_disable();
      delay(10);
      WiFi.begin();
      break;
    case SYSTEM_EVENT_STA_WPS_ER_FAILED:
      Serial.println("WPS Failed, retrying");
      esp_wifi_wps_disable();
      esp_wifi_wps_enable(&config);
      esp_wifi_wps_start(0);
      break;
    case SYSTEM_EVENT_STA_WPS_ER_TIMEOUT:
      Serial.println("WPS Timedout, retrying");
      esp_wifi_wps_disable();
      esp_wifi_wps_enable(&config);
      esp_wifi_wps_start(0);
      break;
    case SYSTEM_EVENT_STA_WPS_ER_PIN:
      Serial.println("WPS_PIN = " + wpspin2string(info.sta_er_pin.pin_code));
      break;
    default:
      break;
  }
}

Заранее спасибо

1 Ответ

0 голосов
/ 18 февраля 2019

Я посмотрел на пример , связанный в вопросе 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:

  1. WiFiEventCb ... указатель функции на(бесплатная) функция, которая возвращает void и имеет один параметр типа system_event_id_t
  2. WiFiEventFuncCb ... объект std::function для всего, возвращающего void и имеющего два параметра типов system_event_id_tи system_event_info_t
  3. WiFiEventSysCb ... указатель на функцию (свободную), которая возвращает void и имеет один параметр типа system_event_id_t*.

Очевидно, в примере использовалось значение 2 nd onEvent(), поскольку оно единственное, принимающее функции с двумя параметрами.

Поддержка std::function очень хороша, потому чтоон принимает все вызываемые с соответствующей сигнатурой:

  • свободные функции,
  • функторы,
  • лямбды (которые на самом деле ничего не значаткроме одного из первых).

Итак, чтобы сделать ApiClient::WiFiEvent() совместимым, есть два варианта:

  1. объявить ApiClient::WiFiEvent() как статическую функцию-член
  2. связать 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 для этой цели.)

...