Недопустимое преобразование из 'char' в 'const char*' [-fpermissive] в строке

Я компилирую какой-то код, который не писал, и он взрывается сообщением об ошибке

invalid conversion from 'char' to 'const char*' [-fpermissive]

на линии

if (loginPassword == '\0') loginPassword = "";

Я не понимаю, почему эта строка отличается от предыдущих строк, и все ответы, которые я могу найти, связаны с переменными, начинающимися как char.

Что не так с этим кодом и как его исправить?

    void saveUnit(String tempUnit, String supportMode, String loginPassword, String minTemp, String maxTemp, String tempStep) {
      const size_t capacity = JSON_OBJECT_SIZE(6) + 200;
      DynamicJsonDocument doc(capacity);
      
      // если единица измерения температуры пуста, мы используем по умолчанию цельсий
      if (tempUnit == '\0') tempUnit = "cel";
      doc["unit_tempUnit"]   = tempUnit;
      
      // если minTemp пуст, мы используем значение по умолчанию 16
      if (minTemp == '\0') minTemp = 16;
      doc["min_temp"]   = minTemp;
      
      // если maxTemp пуст, мы используем значение по умолчанию 31
      if (maxTemp == '\0') maxTemp = 31;
      doc["max_temp"]   = maxTemp;
      
      // если tempStep пуст, мы используем значение по умолчанию 1
      if (tempStep == '\0') tempStep = 1;
      doc["temp_step"] = tempStep;
      
      // если режим поддержки пуст, мы используем режим по умолчанию все
      if (supportMode == '\0') supportMode = "all";
      doc["support_mode"]   = supportMode;
      
      // если пароль для входа пуст, мы используем пустой
      if (loginPassword == '\0') loginPassword = "";
      doc["login_password"]   = loginPassword;
      
      File configFile = SPIFFS.open(unit_conf, "w");
      if (!configFile) {
        // Serial.println(F("Не удалось открыть конфигурационный файл для записи"));
      }
      
      serializeJson(doc, Serial);
      serializeJson(doc, configFile);
      
      configFile.close();
    }

, 👍1

Обсуждение

Я предполагаю, что это было скопировано из контекста, где loginPassword был указателем, а не String, где его, вероятно, действительно следовало сравнить с nullptr или 0 в зависимости от эпохи C++., @timemage

@timemage https://github.com/gysmo38/mitsubishi2MQTT/blob/master/src/mitsubishi2mqtt/mitsubishi2mqtt.ino, @Ben Dauphinee

. Если это исходный контекст, то я понятия не имею, почему он когда - либо компилировался. Вы не получаете ту же ошибку для "supportMode" и других, которые следуют этому же шаблону?, @timemage

Правильно, это первая строка, которая обрывается. Я не могу понять, почему, когда он должен сломаться до этого., @Ben Dauphinee

Вполне вероятно, что все, что следует шаблону "if (X = = `\0') X = some_default;", должно было быть написано " if (X=="")"... или, может быть, более эффективно " if (X. length() == 0)"..., но это своего рода нонсенс для поля loginPassword. Если она уже пуста... Она пуста., @timemage

Хорошо, если я запущу компиляцию arduino-cli против esp32:esp32:esp32 FQBN с минимальными лесами вокруг вашего кода, например, включая ArduinoJson.h, SPIFFS.h, пустой цикл и настройку, он компилируется без ошибок. Ядро esp32, которое находится на моей машине, 1.0.4, вероятно, старое, но это то, что у меня есть. ArduinoJson был версии 6.18.0., @timemage

Та же версия ArduinoJson, компилируемая против esp8266, @Ben Dauphinee

Давайте [продолжим это обсуждение в чате](https://chat.stackexchange.com/rooms/126427/discussion-between-timemage-and-ben-dauphinee)., @timemage


1 ответ


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

2

Независимо от приведенных ниже результатов, вы действительно не хотите сравнивать строковый объект с нулевым символом таким образом (подробнее об этом ниже). На самом деле что-то вроде: if (someArduinoStringObj.isEmpty ()) {- лучшая идея, независимо от того, на каком ядре вы находитесь.

Поэтому для любого, кто попал сюда с этой проблемой с кодом, который работал и больше не работает, ответ в основном таков: не делайте этого; вероятно, не следовало этого делать в первую очередь.

Если вам интересно, что я нашел, то ниже есть материал на эту тему.


Так что у меня нет полного представления об этом, но мне есть о чем доложить. Когда мы начали проходить через это, я не думал, что в пакетах поддержки платы Arduino будет что-то, что повлияет на это, но оказалось, что это не так.

Мы выяснили, что в версии ESP8266 Arduino core 3.0.0 строки следующей формы не компилируются, а в версии 2.7.4 компилируются без ошибок. Здесь, под 3.0.0, он не был придирчив к конкретному loginPassword.

if (someArduinoStringObj == '\0') someArduinoStringObj = "some literal";

Это можно абстрагировать до несколько бессмысленного:

someArduinoStringObj == '\0';

Поскольку именно эта часть имеет значение. Я действительно думал, что в этом мире происходит что-то более умное. WString.h/WString.cpp код, дополнительный конструктор или неявный вызов оператора преобразования перед кодом для оператора==. Существует явный конструктор для String, который принимает один символ, но он просто явный; он не принимает участия в вычислении приведенного выше выражения==. И нет автоматического преобразования строки в const char *. Оказывается, в классе String нет ничего умного, когда дело доходит до этой проблемы. На самом деле вы можете свести это к чему-то подобному, так что сам класс String на самом деле вообще не задействован:

void func(const char *) {}

void setup() {
  func('\0');
}

// ...

Это также пройдет компиляцию в версии 2.7.4 и завершится неудачей с аналогичным сообщением об ошибке в версии 3.0.0. Таким образом, со строкой он просто пытается вызвать оператор==(const char *), даже если ему присваивается тип char. Прежде чем приступить к этому, я проверил теорию, которая заключалась в том, чтобы выяснить, что произойдет, если дать == '\1'; или == 'X') и т.д., Любое ненулевое значение, потому что 0 имел интересную связь с указателями в C++ и C. Это имеет больше смысла, как только вы поймете, что в String.h происходит что-то особенное. Мы в основном говорим о func('\0') против func('\1') или func('X') Ну, в 2.7.4 эти значения терпят неудачу там, где '\0' нет, но под 3.0.0 оба терпят неудачу. Поэтому я занялся изучением platform.txt файл на обеих основных версиях, думая, что, может быть, я замечу разницу в параметрах компилятора. Например, это имело бы "смысл", если бы в 2.7.4 был-fpermissive, а в 3.0.0-нет, но это не так. Основное различие заключается в том, что 3.0.0 имеет-std=g++17, а 2.7.4-std=g++11. Просто для того, чтобы, черт возьми, я украл командную строку из подробного вывода компиляции для .ino.cpp файл и заменил -std=g++17 на-std=g++11 и применил его к урезанному файлу; это не имело никакого значения. Итак, насколько я понимаю, существует некоторая разница в параметрах по умолчанию, указанных между двумя версиями компилятора, используемыми в двух версиях пакета esp8266.

Постоянные выражения, которые вычисляются до нуля типа int (по крайней мере), преобразуются в константы нулевого указателя. У меня не было привычки использовать символьные ('\0') константы с нулевым значением для нулевых указателей, и я не копался в различных изданиях стандарта C++, чтобы увидеть, что каждый из них может сказать о них; в любом случае это меня не удивило. Но в любом случае, это в основном то, что происходит здесь: строковый объект сравнивается с константой нулевого указателя, по крайней мере, когда он компилируется. И если это заставит тебя сойти с ума, что ж, я тоже. Если вы последуете этому примеру, то в конечном итоге окажетесь здесь:

unsigned char String::equals(const char *cstr) const {
    if (len() == 0)
        return (cstr == NULL || *cstr == 0);
    if (cstr == NULL)
        return buffer()[0] == 0;
    return strcmp(buffer(), cstr) == 0;
}

Таким образом, грубо говоря, someArduinoString == '\0', где он будет компилироваться, эквивалентен someArduinoString == nullptr, который имеет тот же эффект, что и someArduinoString == "". Итак, то, что вы видите, не совсем неявная форма someArduinoString == String('\0').

Я не могу представить, что хочу сказать someArduinoString == '\0' над someArduinoString.isEmpty (), но с точки зрения того, почему он не работает на 3.0.0, я все еще не уверен. Я могу сказать вам, что это произойдет, если вы посадите-fpermissive в platform.txt но на самом деле это ничего не объясняет. Когда-нибудь, когда мне будет очень скучно, я посмотрю, что на самом деле говорит языковой стандарт и какие версии g++ делали то, что под какими опциями.

Даже если бы я это понял, мой ответ все равно звучал бы так: не делай этого.

,