Считается ли #ifdef __SD_H__ плохой практикой?
Предположим, я работаю над библиотекой^1, в которую хочу добавить поддержку SD.h
^2, но точно зная, что многие микроконтроллеры не поддерживают SD.h
(поэтому они приводят к ошибкам компиляции [например, Attiny85]) Я не хочу просто #include <SD.h>
(или даже #ifndef __SD_H__
, а затем #include <SD .h>
) в Mylibrary.h
и сделать его совершенно непригодным для использования некоторыми микроконтроллерами. Поэтому мне было интересно, можно ли просто добавить такую функциональность, как...
// Mylibrary.h
#ifdef __SD_H__
// Сохраняем функциональность для тех, кто включает `SD.h`, в их `sketch.ino`
void Mylibrary::save(String filename){
SD.open(filename, FILE_WRITE)
// ...
}
#endif
...без включения SD.h
в Mylibrary.h
и, следовательно, не заставляющего пользователя всякий раз, когда он хочет использовать некоторые функции SD, просто включать SD.h
в своем sketch.ino
? +(звучит утомительно делать что-то вроде это для каждого MCU, как альтернативный способ)
[1] (в моем случае эта библиотека)
[2] (в моем случае что-то связанное с это)
PS. Причина, по которой я спрашиваю, заключается в том, что я не припомню, чтобы где-нибудь это использовалось таким образом, и я немного запутался, правильно это или неправильно
@Giorgos Xou, 👍3
Обсуждение2 ответа
Лучший ответ:
Да, в целом использование определения защиты включения для определения включения заголовочного файла является плохой практикой. Он будет работать только с файлом заголовка в модуле компиляции, который включает обнаруженный файл заголовка.
В Arduino все ino-файлы скетча представляют собой одну единицу компиляции. Но каждый файл C или C++ представляет собой отдельную единицу компиляции. Они не «видят» защиту включения от включения в ino-файл(ы).
Если вы хотите использовать определение защиты включения, не пересылайте его исключительно. Используйте свое собственное определение и установите его, если обнаружите защиту включения. Таким образом, пользователь может установить его, если его библиотека SD имеет другое имя для защиты включения.
#ifdef __SD_H__
#define XY_USE_SD_LIB
#endif
#ifdef XY_USE_SD_LIB
// Сохраняем функциональность для тех, кто включает `SD.h`, в их `sketch.ino`
void Mylibrary::save(String filename){
SD.open(filename, FILE_WRITE)
// ...
}
#endif
Я использую это в своей популярной библиотеке ArduinoOTA здесь.
>Есть другой способ обнаружения включений, и он работает во всех модулях компиляции, но не на всех платформах Arduino. Это директива __has_include
. Я использую его здесь.
Это так классно! умница! Это именно то, что я искал! Спасибо!, @Giorgos Xou
вы быстрый :-). пожалуйста, прочитайте последний абзац для альтернативы, @Juraj
Это так интересно, что я не знал о __has_include
, @Giorgos Xou
Я не думаю, что это сработает, однако вы можете попробовать и посмотреть. Просто установите тип платы, который не поддерживает SD.h.
Моя проблема заключается в том, что ваша библиотека представляет собой отдельный модуль компиляции (т. е. файл), который будет скомпилирован с учетом того, что вы помещаете в качестве заголовочных файлов.
Если пользователь вашей библиотеки использует SD.h в основном файле .ino, то ваша библиотека (которая компилируется отдельно) не будет об этом знать. Другими словами, он не будет знать, следует ли реализовать функцию Mylibrary::save.
Я думаю, что довольно простым решением было бы поместить определение во включаемый файл вашей библиотеки, например.
#define USE_FILE_SAVING false
Затем проверьте это в своем коде. Вам не нужен целый список MCU, просто сообщите пользователю, что если он хочет реализовать сохранение файлов, измените эту одну строку.
Дополнительную информацию см. в разделе Как все организуется в среде IDE
Спасибо! это действительно интересно, я также только что понял, что это зависит от того, работает оно или нет, в зависимости от того, какую плату я выбираю (например, на arduino uno работает, с ESP32 от espressif он не учитывает, что SD.h
уже определен), @Giorgos Xou
Подождите, это так странно, он прекрасно копируется на обеих платах, но с ESP32 #ifdef __SD_H__ #pragma message("YES SD.h") #else #pragma message("NO SD.h") #endif
говорит "Нет" ...", @Giorgos Xou
Добавление SD.h в ваш основной проект не повлияет на компиляцию вашей библиотеки. Это отдельная единица компиляции, и она не учитывает, что имеется в вашем .ino-файле. См.: https://arduinoprosto.ru/q/13182/10794., @Nick Gammon
[В некоторых случаях это действительно действует *(например, с Arduino UNO)*](https://s5.gifyu.com/images/S8aAm.gif), @Giorgos Xou
@GiorgosXou Однако это не библиотека, верно? Конечно, это влияет на *модуль компиляции*. В этом GIF-файле вы показываете, что размещение включения внутри файла в единице компиляции (то есть файла, который мы просматриваем), конечно, повлияет на компиляцию этого файла. Но если вы поместите это включение в файл A, это не повлияет на компиляцию файла B (вашей библиотеки)., @Nick Gammon
Ваша библиотека компилируется отдельно (в другой компиляции) из вашего файла .INO. Библиотека не может знать, что вы можете добавить или не добавить в свой файл .INO., @Nick Gammon
[Вся моя библиотека находится в файле .h
](https://github.com/GiorgosXou/NeuralNetworks/blob/master/src/NeuralNetwork.h). Отдельных файлов cpp
нет, я просто организую реализацию каждого класса с помощью [#pragma Region file.cpp
](https://github.com/GiorgosXou/NeuralNetworks/blob/master/src/NeuralNetwork.h#L570C1 -L570C33) внутри .h
*(именно по причине экстремальной оптимизации макросов)*, @Giorgos Xou
* «Библиотека не может знать, какие включения вы могли или не могли поместить в свой .INO-файл». * во многих случаях, например, #ifdef __SD_H__
, она знает, в других случаях, как [@juraj сказал](https://arduino .stackexchange.com/a/94745/77085) вам нужно использовать __has_include
, например #if определено __has_include #if __has_include(<SD.h>) #include <SD.h> ...
. Но насколько я понимаю, не стоит на 100% зависеть только от тех, @Giorgos Xou
Хорошо, значит, вся библиотека находится в файле .h? Тогда вместо того, чтобы спрашивать, сработает это или нет (что вы можете выяснить сами эмпирически), как насчет того, чтобы сказать нам, работает это или нет, а если нет, спросите нас, почему бы и нет?, @Nick Gammon
Я думал, что ясно дал понять, у меня даже был комментарий // Mylibrary.h
к моему фрагменту кода. Я не спрашивал "сработает это или нет", я спросил ["как бы вы подошли к такой проблеме"](https://arduinoprosto.ru/q/94743/is-ifdef-sd-h- считается плохой практикой?noredirect=1#comment219474_94743), потому что эмпирически я знаю, что делаю ошибки..., @Giorgos Xou
- Ошибка: invalid application of 'sizeof' to incomplete type 'int []' при попытке вычислить размер массива в библиотеке
- Глобальные переменные занимают много места в динамической памяти.
- Включить Guards vs #pragma один раз
- Ошибка: "'lcd' does not name a type" при использовании библиотеки LiquidCrystal.
- Альтернативы дисплею Nextion
- Двоичный форматировщик сообщений
- Как масштабировать растровое изображение (массив uint8_t) в Arduino?
- Программирование Arduino Uno R3 для срабатывания реле каждые 24 часа
[Chat-GPT говорит, что это разумно](https://chat.openai.com/share/67507088-d6bf-44ce-b3b8-ec6c78b68e50), но мне очень хочется узнать ваше мнение о том, как бы вы подошли к такому вопросу, @Giorgos Xou
Не доверяйте ChatGPT, всегда думайте, что это галлюцинации., @the busybee
Это плохо, и я это видел :), @Carsten S
Обратите внимание, что идентификаторы, начинающиеся с двух символов подчеркивания (или одного подчеркивания и заглавной буквы) [зарезервированы](https://port70.net/~nsz/c/c11/n1570.html#7.1.3): «Все идентификаторы, которые начинаются с подчеркивания, а прописная буква или другое подчеркивание всегда зарезервированы для любого использования»., @Andrew Henle
Поскольку вы спросили ChatGPT, я тоже спросил: «Хорошо ли в C++ помещать весь свой код в файл .h?» И ответ был: «Нет, не считается хорошей практикой помещать весь ваш код в файл .h (заголовок) в C++. В C++ код обычно организуется в файлы заголовков и исходные файлы, и существуют определенные соглашения. о том, что должно быть в каждом типе файла». Так что нет, размещение всего вашего кода (объявлений и определений, то есть прототипов и реализаций функций) не считается хорошей практикой. Вы можете поискать в других частях Интернета другие мнения по этому поводу., @Nick Gammon
@NickGammon, когда я начал создавать эту библиотеку, ChatGPT не было. Основываясь на моих исследованиях *(и, конечно же, эмпирически)*, я обнаружил, что это работает. Кроме того, когда-то они были у меня отдельные, но позже я решил, что мне нужно объединить их в
.h
ради лучшей оптимизации МАКРО., @Giorgos Xou