Странное поведение defines - специфично ли это для Arduino и как оно работает?

Вопрос, который у меня давно возник, касается взаимодействия между операторами #define и заголовком библиотеки include . Я видел, как несколько библиотек Arduino использовали эту довольно удобную систему настройки, где вы можете написать оператор #define, затем включить инструкцию заголовка #include, и библиотека изменит свое поведение.

Кто-нибудь знает, почему это работает или не работает? Мое понимание того, как работает #include, заключается в том, что он копирует файл на свое место в новом файле и что код C / C ++ с заголовками должен, таким образом, формировать что-то вроде древовидной формы. Таким образом, имело бы смысл, что любые строки кода, присутствующие до того, как я #включу библиотеку, должны быть видны библиотеке, когда препроцессор сделает свое дело и скопирует файл #include-d.

Но ранее мне говорили, что на самом деле это так не работает (несмотря на реальные примеры, но к тому моменту я уже неуместно разместил библиотеки, которые это делали). Тем не менее, я наконец-то нашел пример того, как кто-то это делает, и я хотел бы знать, как работает такое поведение. Это специфическая особенность Arduino или что-то в этом роде?

Вот библиотека, которая это делает: https://github.com/madpilot/mDNSResolver

, 👍0

Обсуждение

Из моего прочтения исходного кода для этой библиотеки я не вижу, что она * может * работать так, как описано. Код библиотеки (файлы cpp) отличается * единицами перевода* от скетча, где будет находиться #define., @Majenko

Можете ли вы объяснить единицы перевода? Я слышал о них, но они кажутся мне очень туманными., @RDragonrydr

Когда вы компилируете файл .c или .cpp, он собирает все содержимое файлов заголовков вместе и буквально заменяет строку на #includes, а затем #defines . Затем компилирует этот один большой результирующий файл. Это единица перевода. Как правило, для каждого файла cpp или c у вас есть одна единица перевода. Это самодостаточная вещь, которая не может быть изменена ничем извне. Единственное исключение - в Arduino, если у вас есть несколько файлов .ino в скетче, они объединяются в одну гигантскую единицу перевода., @Majenko

Если бы я делал то, что вы описываете, я бы использовал класс с конструктором, код которого находится в заголовочном файле и, таким образом, перекомпилируется для каждой единицы перевода (например, внутри скетча). Этот конструктор будет вызываться с помощью макросов, которые вы можете переопределить из скетча, поскольку конструктор является частью того же TU, что и скетч., @Majenko

отсутствующее определение отсутствует не в вашем скетче, оно отсутствует в файле cpp библиотеки, который является его собственным. один изящный трюк, который я использовал, заключается в том, чтобы поместить весь lib-код в H-файл вместо cpp-файла. В результате получается, что код H-файла просто вставляется на место в скетче, что означает, что для него доступны предыдущие определения. Я не знаю, есть ли в этом какие-то недостатки, но все работает так, как я и ожидал..., @dandavis


2 ответа


1

Библиотека mDNSResolver README имеет

Вы можете изменить несколько настроек, определив некоторые константы перед включением заголовочного файла mDNSResolver [в скетч]

Я добавил "в скетче", потому что именно туда вы включаете заголовочный файл mDNSResolver.

Это не сработает, потому что mDNSResolver.cpp , который использует значения defines, компилируется без каких-либо знаний о содержимом файла ino.

Более глубокое объяснение было бы слишком длинным и выходило бы за рамки Arduino SE.

,

-1

В первых нескольких строках связанного вами файла library .h он проверяет символ, который он не определил, ESP32 (#if defined(ESP32)) и изменяет компиляцию в соответствии с определенностью этого символа.

Поскольку входные данные компилятора будут представлять собой один длинный файл со всеми файлами #include, уже "вставленными" в строку, у вас (или ранее включенного файла .h) есть возможность предварительно определить эту переменную для изменения компиляции, как это предусмотрено в этой библиотеке writer.

Обновление:

@RDragonflydr: Я полагаю, вы правы, что символ ESP32 определяется компилятором (или, что более вероятно, makefile) для целевого процессора. И чтобы пояснить мое утверждение о том, что "входные данные компилятора будут представлять собой один длинный файл", я имею в виду компиляцию каждого модуля вашего проекта, а не всего проекта сразу. Каждая библиотека будет скомпилирована отдельно, и каждый из ваших модулей будет скомпилирован отдельно, и в каждом из этих случаеввходные данные компилятора будут состоять из кода модуля со всеми включенными файлами #, "вставленными" на место, замененными символами и макросами #defined, а комментарии удалены.

Код библиотеки (.cpp) не #включен, поэтому он не становится частью единицы перевода. Включен только заголовочный файл библиотеки. Это общее - и необходимое - для всех сред и одна из функций препроцессора C или C ++. Это происходит для каждого модуля - отдельно - в многомодульной компиляции. Файлы библиотеки будут

,

Имея хороший опыт работы в C и C ++ на протяжении многих лет, я совершенно ошеломлен. Вы хотите сказать, что процесс сборки Arduino объединяет все источники скетча в одну большую единицу перевода? Боже, я не знал об этом! Это также верно для библиотек Arduino по умолчанию или просто добавленных библиотек?, @the busybee

Я не совсем понимаю, что вы пытаетесь здесь сказать. AFAIK определение ESP32 устанавливается ядром и / или компилятором Arduino, чтобы сообщить коду, на чем он работает. Это не имеет никакого отношения к определению "настройки", о котором я спрашивал., @RDragonrydr

Я знаю, что процесс сборки Arduino объединяет все файлы - это то, что позволяет определять "настройки" для работы? Я не уверен, есть ли способ заставить это работать в других средах, кроме Arduino IDE. Если это вообще работает, то только в Arduino, и это хрупко, если вы используете другую IDE для кодирования для Arduino?, @RDragonrydr

@thebusybee, только файлы ino в скетче объединяются в один файл cpp, а не файлы C, C ++, S (конечно), @Juraj