Какова цель 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%. Все равно не ничего.

, 👍2

Обсуждение

На ESP32 (как и на большинстве 32-битных плат) макросы - это не что иное, как слепок, на платах на основе AVR (например, Arduino Uno) для считывания данных со вспышки требуются специальные низкоуровневые функции. Макросы скрывают эту разницу от пользователя, так что код может быть написан таким образом, чтобы он компилировался и запускался на обеих платформах., @PMF

@PMF спасибо за разъяснение варианта использования. Означает ли это, что мне не нужно использовать F() или FPSTR() в моем собственном коде, если я планирую использовать только ESP8266?, @DV82XL

Я думаю, что нет, но это все еще может зависеть от компилятора (он все еще может делать копию с Flash на Ram при загрузке). Вы можете узнать это, добавив / удалив несколько F() и проверив результирующее использование оперативной памяти (последняя строка вывода компилятора). Если это не изменится, вам это не нужно., @PMF

Конечно, в ESP8266 макросы F() могут сэкономить место в куче., @dandavis

@dandavis Спасибо! Я добавил правку с некоторыми метриками. Автор кода иногда использует F(), иногда FPSTR(), а иногда и то, и другое. Каковы хорошие критерии для принятия решения о том, когда использовать каждый подход?, @DV82XL

это сэкономит больше оперативной памяти при использовании более длинных строк, например, сообщений об ошибках веб-страниц. Используйте F() в значительной степени только для строковых литералов., @dandavis


1 ответ


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

3

Цель макроса 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