Считается ли #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. Причина, по которой я спрашиваю, заключается в том, что я не припомню, чтобы где-нибудь это использовалось таким образом, и я немного запутался, правильно это или неправильно

, 👍3

Обсуждение

[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


2 ответа


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

7

Да, в целом использование определения защиты включения для определения включения заголовочного файла является плохой практикой. Он будет работать только с файлом заголовка в модуле компиляции, который включает обнаруженный файл заголовка.

В 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


2

Я не думаю, что это сработает, однако вы можете попробовать и посмотреть. Просто установите тип платы, который не поддерживает 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