Какова цель F() и FPSTR() в ESP8266WebServer -> FSBrowser?
Я нашел этот пример кода из примера FSBrowser (Flash File System) в библиотеке ESP8266WebServer:
replyServerError(FPSTR(FS_INIT_ERROR));
replyBadRequest(F("DIR ARG MISSING"));
Я был немного смущен тем, что делают F()
и FPSTR ()
, поэтому я посмотрел источник (WString.h
) и нашел следующее:
#define FPSTR(pstr_pointer) (reinterpret_cast<const __FlashStringHelper *>(pstr_pointer))
#define F(string_literal) (FPSTR(PSTR(string_literal)))
Что вызвало еще больше вопросов. Как работает этот код и что дает эта дополнительная сложность по сравнению с простым использованием char*
или String
как есть?
EDIT: Добавлены метрики
Вот использование памяти до и после того, как я удалил все 25 экземпляров F() из класса файловой системы, измеренное PlatformIO Project Inspector:
С F() | Без F() | Разница | |
---|---|---|---|
Используемая оперативная память | 29 488 байт (36,0%) | 29 792 байта (36,4%) | +304 байт |
Используемая вспышка | 355 312 байт (34%) | 355 188 байт (34,0%) | -124 байт |
Итого (несжато) | 359 472 байт | 359,344 байт | -128 байт |
Добавление F() уменьшает использование оперативной памяти на 1% и увеличивает объем флэш-памяти примерно вдвое. Это для одного класса с 25 экземплярами F(). Для программы, содержащей 20-кратный этот код, разница может быть значительной. Однако у вас закончится оперативная память, прежде чем вы туда доберетесь. Экономия пространства будет ограничена примерно 5-8%. Все равно не ничего.
@DV82XL, 👍2
Обсуждение1 ответ
Лучший ответ:
Цель макроса PSTR()
- настроить строку для использования непосредственно из флэш-памяти. Затем строка не загружается в динамическую память во время выполнения, как это было бы без спецификатора PROGMEM, установленного макросом.
Цель макроса FPSTR()
- привести строку к типу 'dummy' FlashStringHelper, чтобы помочь компилятору выбрать правильную перегруженную функцию, если доступна динамическая память и версия функции во флэш-памяти. Примером может служить функция печати
.
Макрос F()
объединяет эти два макроса. Он делает строку строкой PROGMEM и бросает ее в FlashStringHelper.
Спасибо за ответ! Как FlashStringHelper помогает перегружать функции? Приводит ли он себя либо к динамической строке, либо к флэш-строке в зависимости от своего входного параметра?, @DV82XL
@DV82XL, строка PROGMEM - это тоже const char*. в C есть такие функции, как sprintf_P для строк PROGMEM, но Arduino решил использовать специальный тип 'dummy' для строки PROGMEM и использует print (const char*)
для обычной строки и print (flashstringhelper*)
для строки PROGMEM. в стандартном API Arduino нет Serial.print_P (некоторые ядра имеют его). чтобы использовать правильную функцию, компилятор C ++ вычисляет параметры функций с тем же именем https://github.com/arduino/ArduinoCore-avr/blob/9f8d27f09f3bbd1da1374b5549a82bda55d45d44/cores/arduino/Print.h#L65, @Juraj
- Утечка памяти, вызванная конкатенацией строк
- Не удается скомпилировать макрос F() с помощью R "string"
- Создание форматированной строки (включая числа с плавающей запятой) в Arduino-совместимом C++
- Передача функции-члена класса в качестве аргумента
- Arduino Преобразование std:string в String
- Преобразование строки в IP-адрес
- контент» не захватывается
- esp32 Stack canary watchpoint срабатывает
На ESP32 (как и на большинстве 32-битных плат) макросы - это не что иное, как слепок, на платах на основе AVR (например, Arduino Uno) для считывания данных со вспышки требуются специальные низкоуровневые функции. Макросы скрывают эту разницу от пользователя, так что код может быть написан таким образом, чтобы он компилировался и запускался на обеих платформах., @PMF
@PMF спасибо за разъяснение варианта использования. Означает ли это, что мне не нужно использовать
F()
илиFPSTR()
в моем собственном коде, если я планирую использовать только ESP8266?, @DV82XLЯ думаю, что нет, но это все еще может зависеть от компилятора (он все еще может делать копию с Flash на Ram при загрузке). Вы можете узнать это, добавив / удалив несколько F() и проверив результирующее использование оперативной памяти (последняя строка вывода компилятора). Если это не изменится, вам это не нужно., @PMF
Конечно, в ESP8266 макросы F() могут сэкономить место в куче., @dandavis
@dandavis Спасибо! Я добавил правку с некоторыми метриками. Автор кода иногда использует F(), иногда FPSTR(), а иногда и то, и другое. Каковы хорошие критерии для принятия решения о том, когда использовать каждый подход?, @DV82XL
это сэкономит больше оперативной памяти при использовании более длинных строк, например, сообщений об ошибках веб-страниц. Используйте
F()
в значительной степени только для строковых литералов., @dandavis