Непоследовательное поведение операторов включения?
Этот вопрос касается шрифтов. Ну, на самом деле, сингулярные включают файлы с константами, которые формируют растровые шрифты или изображения для ЖК- или OLED-дисплеев, таких как этот: https://github.com/ThingPulse/esp8266-oled-ssd1306/blob/master/src/OLEDDisplayFonts.h. Странно сложная для исследования (но распространенная) проблема возникает, когда кто-либо пытается включить один из этих файлов шрифтов в несколько файлов в приложении.
Даже при наличии включающей защиты повторное использование этого файла .h вызовет странные ошибки компоновщика при переопределении переменной. Оказывается, что любая переменная, фактически объявленная в файле .h, приведет к тому, что она будет создана в программе несколько раз, а затем, когда разные файлы, зависящие от файла font.h, будут связаны, переменные столкнутся. Примером этого может быть здесь: https://stackoverflow.com/a/4936294/11381501, или в моем собственном вопросе некоторое время назад здесь: как создавать большие массивы progmem и не раздражать компоновщика
Проблема в том, что я видел некоторые библиотеки, которые избегают этой ошибки (например, https://github.com/ThingPulse/esp8266-oled-ssd1306/blob/master/src/OLEDDisplay.h#L64) несмотря на наличие переменных, объявленных в файлах .h. Я даже включил этот файл OLEDDisplay.h (косвенно через файл основной библиотеки для моего экрана) в несколько моих собственных файлов .h в одном проекте, и это не вызвало ошибок компоновщика. Что заставляет эту переменную с глобальной областью действия в файле .h не создавать ошибки компоновщика, когда обычно это делает мой собственный код?
@, 👍0
Обсуждение1 ответ
Лучший ответ:
В esp8266-oled-ssd1306 это неправильно, но с примерами библиотеки это сохраняется за счет оптимизации. Другие файлы, в которые включается шрифт, не используют его, поэтому он оптимизирован. В противном случае он был бы продублирован.
OLEDDisplayFonts.h включен в OLEDDisplay.h, который включен в OLEDDisplay.cpp и в SSD1306Wire.h, который включен в скетч. Это создает два экземпляра переменных, определенных в OLEDDisplayFonts.h. Но пример в OLEDDisplay.cpp оптимизирован, потому что этот файл не использует шрифты. Таким образом, для компоновщика существует только один экземпляр переменной.
соответствующая часть из файла .map для примера SSD1306UiDemo.ino
.irom.text.OLEDDisplayFonts.h.857.11
0x000000004023b7fd 0x25ab ./SSD1306UiDemo.ino.cpp.o
.irom.text.OLEDDisplayFonts.h.434.10
0x000000004023dda8 0x13b9 ./SSD1306UiDemo.ino.cpp.o
.irom.text.OLEDDisplayFonts.h.10.9
0x000000004023f161 0xaab ./SSD1306UiDemo.ino.cpp.o
.irom.text.OLEDDisplayFonts.h.10.9
0x000000004023fc0c 0xaab ./libraries/esp8266-oled-ssd1306/src/OLEDDisplay.cpp.o
.irom.text.OLEDDisplayFonts.h.10.9
0x00000000402406c7 0xaab ./libraries/esp8266-oled-ssd1306/src/OLEDDisplayUi.cpp.o
Правильный способ - поместить переменные в файл cpp или c, и если они должны быть видны из другого файла, то используйте ключевое слово extern в этом файле (обычно поверх включенного файла .h), чтобы объявить, что переменная существует.
- Инициализация объекта как члена класса приводит к ошибке связывания?
- Как объявить массив переменного размера (глобально)
- Как получить тип данных переменной?
- Преобразование long в массив символов и обратно
- контент» не захватывается
- Получение имени перечисления из экземпляра перечисления
- Некоторые переменные не сохраняют свои значения при выходе из цикла while?
- Как отобразить переменные с плавающей запятой на OLED-дисплее (0,96 дюйма)
Защита от включения защищает только от многократного включения в пределах одной единицы перевода. Переменные, объявленные в таких заголовках, должны быть "статическими", но на самом деле должны быть просто объявлены в заголовке как "extern" и определены в их собственном отдельном TU., @Majenko
Вот как я делаю шрифты в своей библиотеке отображения: https://github.com/CariadDisplayLibrary/WhiteRabbit/tree/master/src, @Majenko
@Majenko, существует альтернатива объявлению
static
противextern
(с определением в другом месте) в C ++ 17 и далее, гдеinline
может применяться к ** переменным ** в соответствии с правилом с одним определением, которое едва ли отличается (если вообще отличается) от указанногоinline
функции., @timemage@timemage В настоящее время в C ++ существует слишком много "альтернатив". Это превратилось в ужасную запутанную мешанину "функций"..., @Majenko
@Majenko Ну, это решение, которое включает в себя вставку одного ключевого слова. Я подумал, что это было бы полезно узнать., @timemage