У меня есть файл с сотнями макросов препроцессора вида...
#define foo (bar)
Есть ли простой однострочный способ определить каждый из них, только если он еще не существует?
Для одного макроса однажды можно было сделать следующее...
#ifndef foo
#define foo (bar)
#endif
Но у меня сотни таких макросов. Я надеялся на что-то вроде...
#define_if_not_defined foo (bar)
Я действительно пытался создать свой собственный макрос
#define DEFINE_IFNDEF(foo,bar)\
#ifndef foo\
#define foo (bar)\
#endif
Тогда я мог бы просто заменить...
#define foo(bar)
с DEFINE_IFNDEF(foo,bar)
Но, по-видимому, не существует хорошего способа поместить директивы препроцессора внутри макроса препроцессора.
ФОН
Я писал код, сертифицированный по безопасности, который должен был копировать некоторые функции из большой ранее существовавшей библиотеки из нескольких тысяч файлов, которые не были сертифицированы по безопасности.
В исходной библиотеке было несколько полезных определений макросов, разбросанных по очень большому количеству файлов. Включение целых файлов стало бы огромным дорогостоящим бременем с точки зрения проверки кода, но включение нескольких макросов из некоторых файлов, чтобы избежать повторного изобретения колеса, не было проблемой. Поэтому я собрал множество препроцессорных макросов тут и там из очень большого количества файлов той оригинальной библиотеки и поместил их в один файл.
Моя библиотека имеет некоторые важные преимущества с точки зрения меньшего размера кода, более высокой скорости, меньшей сложности кода и более простой проверки безопасности с точки зрения безопасности, но она не имеет всех функций, которые были в исходной библиотеке. Итак, теперь некоторые разработчики хотят попробовать использовать исходную библиотеку параллельно с моей библиотекой кода.
Проблема в том, что сейчас существуют сотни дубликатов макросов препроцессора, которые генерируют предупреждения. Мы хотели бы убрать предупреждения.
Если бы все макросы были из одного и того же заголовочного файла, я мог бы просто использовать #ifdef для всей их партии. Но в моем случае это не так просто и мне понадобится много #ifdef.
Если коротко, то ответ «Нет». Напишите сценарий, который считывает строки определения и выдает строки #ifndef
и #endif
.
Если желаемое определение идентично предыдущему определению, просто позвольте ему повториться. Это разрешено стандартом C (C 2018 6.10.3 2), и нет необходимости условно подавлять это. Если желаемое определение отличается от предыдущего определения, вы все равно не хотите подавлять это определение, поскольку, предположительно, некоторый более поздний код будет опираться на новое определение.
Clang, GCC и MSVC, похоже, не выдают предупреждений о повторяющихся определениях макросов, даже если -Wall -Wextra -Weverything
для Clang и GCC и/или /Wall
для MSVC. Какой компилятор вы используете и какие параметры командной строки вы с ним используете?
это похоже на работу для сценария, который найдет все #ifndef
и т. д. и внесет необходимые вам изменения.
Отредактируйте вопрос, чтобы предоставить минимально воспроизводимый пример. Оно должно включать как минимум две директивы #define
для одного имени макроса, которые приводят к диагностике компилятора (например, взять исходный файл одной компиляции и удалить из него все, кроме директив, необходимых для выявления проблемы), а также имя используемого компилятора, его номер версии и используемые с ним переключатели. Это должно решить вопросы о том, как вы получаете эту диагностику и являются ли определения макросов фактически полными дубликатами или просто повторным использованием одного и того же имени с разными определениями.
Я очень сомневаюсь, что в сертифицированной по безопасности кодовой базе допустимо делать такие вещи, как «Я собрал множество макросов препроцессора здесь и там из очень большого количества файлов». Это эквивалентно созданию множества дополнительных опасностей из-за несоответствия макросов или переопределений.
@Lundin Как я должен называть что-то вроде #define SS0_CORE0_BUS_CLK_PARENT_PLL_CTRL_WRAP_CHIP_DIV_CLK (2)
, кроме того, как первоначально назвал это продавец? Все макросы представляют собой числовые константы, используемые для взаимодействия с API-интерфейсами встроенного ПЗУ. Это даже не адреса памяти или значения битовых полей. Это просто случайные числа, присвоенные каким-то парнем, написавшим API ПЗУ. Нет никакой вероятности несоответствия. Я не менял значения или имена. Весь код на устройстве будет использовать одни и те же постоянные значения при взаимодействии с этим кодом ПЗУ.
@user4574 user4574 Это действительно похоже на то, что абсолютно не должно быть раскрыто за пределами конкретного драйвера.
@user4574: user4574: Обновите вопрос, чтобы ответить на вопросы, которые вам задали. Ни один из распространенных компиляторов не жалуется на повторяющиеся определения макросов, поэтому возникает вопрос, точно ли проблема описана в вашем сообщении. Приведите минимально воспроизводимый пример. Если вы не предоставите информацию и не проясните ситуацию, ваш пост должен быть отклонен и закрыт.
@EricPostpischil Я не могу дать вам имя компилятора, не указав имя поставщика. И я не чувствую, что должен это делать. Итак, если вам нужно закрыть вопрос, пусть будет так.
Легкий! Но не напрямую в C.
Создайте текстовый файл defines.txt
, например:
DEF_A=123
DEF_B=456
И пропустите его через внешний скрипт make_defines.pl
:
#!/usr/bin/perl
while(<>) {
my ($def, $val) = split /=/;
print "#ifndef $def\n#define $def $val#endif\n\n";
}
Теперь все, что вам нужно, это запустить его:
$ perl make_defines.pl defines.txt > defines.h
Вы также можете добавить его в makefile
defines.h: defines.txt
perl make_defines.pl $< > $@
Сделанный.
Я подумывал о написании сценариев или даже просто о вставке строк кода в Microsoft Excel, чтобы массово их изменить. Это хорошее и правильное решение. Но я надеялся на что-то, что существует внутри самого языка C. Если окажется, что этого не существует, я рассмотрю решение на основе сценариев.
В конце концов я просто вставил весь заголовочный файл в Microsoft Excel, а затем написал макрос, который расширял все строки #define
до #ifndef #define #endif
.
Это возможное решение, конечно. Но... он одноразовый. Не совсем удобно повторять для новой партии определений. Но все, что удовлетворит заказчика – подойдет.
Нет, но макросы вообще не следует использовать таким образом.
Вообще говоря, имена ваших макросов не должны конфликтовать с именами других макросов. Когда они это делают, это признак того, что вам нужно найти (и использовать) новые имена — даже если цель макроса аналогична или такая же, как у других существующих макросов!
Хороший способ сделать это — просто добавить ко всем макросам префикс имени вашей библиотеки. Например, если ваша библиотека называется «Quux», тогда ваши макросы должны называться примерно так: QUUX_ADD
и QUUX_IFY
.
Если ваша цель — заменить значение другого макроса своим определением, то вам придется использовать всю строку 4+ #ifdef
/#undef
/#endif
/#define
бит ··· вам понадобится только #undef
перед #define
.
У этого средства довольно неприятный запах. Избегать!
Если ваша цель состоит исключительно в том, чтобы гарантировать существование данного макроса, вы можете правильно включить заголовок туда, откуда он правильно получен (при условии, что такой заголовок существует), а затем проверить, что он действительно определен.
Обычным случаем является что-то вроде STRINGIFY
. Насколько я знаю, нет места, где этот макрос должен существовать, но он достаточно распространен, поэтому вам все равно следует остерегаться неожиданностей:
#ifndef STRINGIFY
#define STRINGIFY(s) STRINGIFY_(s)
#define STRINGIFY_(s) #s
#endif
Опять же, при этом все равно существует риск того, что чужой код попытается создать макрос STRINGIFY, но не сможет защититься от возможности того, что он уже существует.
Alas if you are stuck using untouchable library code written by some noob.
Это возвращает нас к первому пункту: избегайте конфликтов, используя уникальное имя.
#define QUUX_STRINGIFY(s) QUUX_STRINGIFY_(s)
#define QUUX_STRINGIFY_(s) #s
В заключение, для всех макросов, которые имеют временное назначение (в одном файле, будь то заголовок или исходный код!), не стесняйтесь называть их как-нибудь вроде «F
», но не забудьте отменить их определение, когда закончите с ними. .
Для примера использования трюка X-макроса:
#define FRUITS(F) \
F(Strawberry) \
F(Banana) \
F(Apple) \
F(Orange) \
F(Grape)
#define F(name) name,
enum Fruit { FRUITS(F) NUM_FRUITS };
#undef F
Обратите внимание, как мы сразу же избавились от временного имени макроса, как только с ним покончили.
Опять же, вполне возможно, что какой-то неосторожный программист где-то оставил беспризорный F
, чтобы испортить вашу компиляцию, но любой код, который делает это, должен быть немедленно выброшен (и сожжен) потому что вы не можете доверять тому, что человек(а), написавший его, этого не сделал. А еще я был крайне неосторожен с чем-то более важным. (Но это мое не столь скромное мнение по этому поводу.)
«Если ваша цель состоит исключительно в том, чтобы гарантировать существование данного макроса, вы можете правильно включить заголовок туда, откуда он правильно получен (при условии, что такой заголовок существует), а затем проверить, что он действительно определен». Хотя в целом это хороший совет, из-за него мне придется проверять десятки тысяч строк стороннего кода в рамках нашей сертификации безопасности. Необходимость потратить дополнительные несколько сотен тысяч долларов на зарплату инженерам и добавление месяцев к графику также является веской причиной не делать этого.
Я согласен с вашим предложением добавить уникальный префикс к моим собственным макросам. Я всегда так делаю, когда определяю что-то новое. Это отличный способ с самого начала избежать конфликтов имен. Но это не помогает мне выбраться из ситуации, когда я просто повторно использовал кучу материалов других людей и теперь имею уже существующие конфликты имен. Полагаю, я мог бы просмотреть сотни макросов один за другим и провести их рефакторинг. Но я надеялся на решение, которое позволило бы мне просто за 10 секунд найти и заменить все #defines в выбранном блоке текста.
@ user4574 Это вторая часть, «проверка того, что он действительно определен», это важная часть, и она не требует проверки десятков тысяч строк стороннего кода. Оберните свой макрос #ifdef
или используйте уникальный макрос. name (хотя, честно говоря, это гниль кода, если вы обнаружите используемый макрос, не зная, откуда он взялся!)
Ах... вы имеете дело с конфликтом кода между двумя пакетами, которые не являются вашим кодом?
Не глядя на ваш код, единственное, о чем я могу думать, это отменить определение конфликтующих макросов (обнаруженных при попытке компиляции) непосредственно перед заголовком, вызывающим конфликт.
Это код для конкретного встроенного процессора. Один набор кода я пишу сам в основном с нуля около двух лет, а другой набор представляет собой огромную библиотеку программного обеспечения, предоставленную производителем чипа. Когда я писал свой код, я даже не ожидал, что буду использовать его вместе с другим набором кода. В этом процессоре имеется встроенный код ПЗУ, который необходимо использовать для доступа к определенным функциям. Я скопировал некоторые определения макросов, связанные с взаимодействием с этим кодом ПЗУ. Если кто-то запустит обе библиотеки одновременно, возникнут повторяющиеся определения, которые генерируют предупреждения.
Собираетесь ли вы выпустить свой код для использования вместе с кодом OEM?
OEM-код не сертифицирован по безопасности, основан на автоматической генерации кода и содержит тысячи файлов, связанных между собой, как паутина. Если я скажу свое слово, мы никогда не будем использовать его в этом продукте. Но, по крайней мере, на данный момент его используют некоторые другие разработчики, и им тоже нужен мой код. Так что пока они должны существовать бок о бок. Но мое намерение состоит в том, чтобы мой код не зависел от исходной библиотеки и использовался вместо нее, а не параллельно с ней.
Ответ: «Если ваша цель — заменить значение другого макроса вашим определением, то вам придется использовать всю строку 4+ бит #ifdef
/#undef
/#endif
/#define
»: в этом нет необходимости. #undef
/#define
достаточно.
Я кое-чему научился!
Я не очень хорошо разбираюсь в макросах, но готов поспорить: «Нет, обходного пути нет».