Есть ли простой однострочный способ определить макрос препроцессора C, только если он еще не существует?

У меня есть файл с сотнями макросов препроцессора вида...

#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.

Я не очень хорошо разбираюсь в макросах, но готов поспорить: «Нет, обходного пути нет».

Ted Lyngmo 20.08.2024 00:48

Если коротко, то ответ «Нет». Напишите сценарий, который считывает строки определения и выдает строки #ifndef и #endif.

Jonathan Leffler 20.08.2024 00:56

Если желаемое определение идентично предыдущему определению, просто позвольте ему повториться. Это разрешено стандартом C (C 2018 6.10.3 2), и нет необходимости условно подавлять это. Если желаемое определение отличается от предыдущего определения, вы все равно не хотите подавлять это определение, поскольку, предположительно, некоторый более поздний код будет опираться на новое определение.

Eric Postpischil 20.08.2024 00:56

Clang, GCC и MSVC, похоже, не выдают предупреждений о повторяющихся определениях макросов, даже если -Wall -Wextra -Weverything для Clang и GCC и/или /Wall для MSVC. Какой компилятор вы используете и какие параметры командной строки вы с ним используете?

Eric Postpischil 20.08.2024 01:00

это похоже на работу для сценария, который найдет все #ifndef и т. д. и внесет необходимые вам изменения.

Amr 20.08.2024 01:06

Отредактируйте вопрос, чтобы предоставить минимально воспроизводимый пример. Оно должно включать как минимум две директивы #define для одного имени макроса, которые приводят к диагностике компилятора (например, взять исходный файл одной компиляции и удалить из него все, кроме директив, необходимых для выявления проблемы), а также имя используемого компилятора, его номер версии и используемые с ним переключатели. Это должно решить вопросы о том, как вы получаете эту диагностику и являются ли определения макросов фактически полными дубликатами или просто повторным использованием одного и того же имени с разными определениями.

Eric Postpischil 20.08.2024 01:20

Я очень сомневаюсь, что в сертифицированной по безопасности кодовой базе допустимо делать такие вещи, как «Я собрал множество макросов препроцессора здесь и там из очень большого количества файлов». Это эквивалентно созданию множества дополнительных опасностей из-за несоответствия макросов или переопределений.

Lundin 20.08.2024 08:35

@Lundin Как я должен называть что-то вроде #define SS0_CORE0_BUS_CLK_PARENT_PLL_CTRL_WRAP_CHIP_DIV_CLK (2), кроме того, как первоначально назвал это продавец? Все макросы представляют собой числовые константы, используемые для взаимодействия с API-интерфейсами встроенного ПЗУ. Это даже не адреса памяти или значения битовых полей. Это просто случайные числа, присвоенные каким-то парнем, написавшим API ПЗУ. Нет никакой вероятности несоответствия. Я не менял значения или имена. Весь код на устройстве будет использовать одни и те же постоянные значения при взаимодействии с этим кодом ПЗУ.

user4574 20.08.2024 16:34

@user4574 user4574 Это действительно похоже на то, что абсолютно не должно быть раскрыто за пределами конкретного драйвера.

Lundin 20.08.2024 16:41

@user4574: user4574: Обновите вопрос, чтобы ответить на вопросы, которые вам задали. Ни один из распространенных компиляторов не жалуется на повторяющиеся определения макросов, поэтому возникает вопрос, точно ли проблема описана в вашем сообщении. Приведите минимально воспроизводимый пример. Если вы не предоставите информацию и не проясните ситуацию, ваш пост должен быть отклонен и закрыт.

Eric Postpischil 20.08.2024 16:51

@EricPostpischil Я не могу дать вам имя компилятора, не указав имя поставщика. И я не чувствую, что должен это делать. Итак, если вам нужно закрыть вопрос, пусть будет так.

user4574 20.08.2024 17:21
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
11
99
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Легкий! Но не напрямую в 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. Если окажется, что этого не существует, я рассмотрю решение на основе сценариев.

user4574 20.08.2024 03:09

В конце концов я просто вставил весь заголовочный файл в Microsoft Excel, а затем написал макрос, который расширял все строки #define до #ifndef #define #endif.

user4574 20.08.2024 19:00

Это возможное решение, конечно. Но... он одноразовый. Не совсем удобно повторять для новой партии определений. Но все, что удовлетворит заказчика – подойдет.

White Owl 20.08.2024 21:14

Нет, но макросы вообще не следует использовать таким образом.

Избегайте конфликтов

Вообще говоря, имена ваших макросов не должны конфликтовать с именами других макросов. Когда они это делают, это признак того, что вам нужно найти (и использовать) новые имена — даже если цель макроса аналогична или такая же, как у других существующих макросов!

Хороший способ сделать это — просто добавить ко всем макросам префикс имени вашей библиотеки. Например, если ваша библиотека называется «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

P.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, чтобы испортить вашу компиляцию, но любой код, который делает это, должен быть немедленно выброшен (и сожжен) потому что вы не можете доверять тому, что человек(а), написавший его, этого не сделал. А еще я был крайне неосторожен с чем-то более важным. (Но это мое не столь скромное мнение по этому поводу.)

«Если ваша цель состоит исключительно в том, чтобы гарантировать существование данного макроса, вы можете правильно включить заголовок туда, откуда он правильно получен (при условии, что такой заголовок существует), а затем проверить, что он действительно определен». Хотя в целом это хороший совет, из-за него мне придется проверять десятки тысяч строк стороннего кода в рамках нашей сертификации безопасности. Необходимость потратить дополнительные несколько сотен тысяч долларов на зарплату инженерам и добавление месяцев к графику также является веской причиной не делать этого.

user4574 20.08.2024 03:01

Я согласен с вашим предложением добавить уникальный префикс к моим собственным макросам. Я всегда так делаю, когда определяю что-то новое. Это отличный способ с самого начала избежать конфликтов имен. Но это не помогает мне выбраться из ситуации, когда я просто повторно использовал кучу материалов других людей и теперь имею уже существующие конфликты имен. Полагаю, я мог бы просмотреть сотни макросов один за другим и провести их рефакторинг. Но я надеялся на решение, которое позволило бы мне просто за 10 секунд найти и заменить все #defines в выбранном блоке текста.

user4574 20.08.2024 03:06

@ user4574 Это вторая часть, «проверка того, что он действительно определен», это важная часть, и она не требует проверки десятков тысяч строк стороннего кода. Оберните свой макрос #ifdef или используйте уникальный макрос. name (хотя, честно говоря, это гниль кода, если вы обнаружите используемый макрос, не зная, откуда он взялся!)

Dúthomhas 20.08.2024 03:07

Ах... вы имеете дело с конфликтом кода между двумя пакетами, которые не являются вашим кодом?

Dúthomhas 20.08.2024 03:08

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

Dúthomhas 20.08.2024 03:10

Это код для конкретного встроенного процессора. Один набор кода я пишу сам в основном с нуля около двух лет, а другой набор представляет собой огромную библиотеку программного обеспечения, предоставленную производителем чипа. Когда я писал свой код, я даже не ожидал, что буду использовать его вместе с другим набором кода. В этом процессоре имеется встроенный код ПЗУ, который необходимо использовать для доступа к определенным функциям. Я скопировал некоторые определения макросов, связанные с взаимодействием с этим кодом ПЗУ. Если кто-то запустит обе библиотеки одновременно, возникнут повторяющиеся определения, которые генерируют предупреждения.

user4574 20.08.2024 03:20

Собираетесь ли вы выпустить свой код для использования вместе с кодом OEM?

Dúthomhas 20.08.2024 03:59

OEM-код не сертифицирован по безопасности, основан на автоматической генерации кода и содержит тысячи файлов, связанных между собой, как паутина. Если я скажу свое слово, мы никогда не будем использовать его в этом продукте. Но, по крайней мере, на данный момент его используют некоторые другие разработчики, и им тоже нужен мой код. Так что пока они должны существовать бок о бок. Но мое намерение состоит в том, чтобы мой код не зависел от исходной библиотеки и использовался вместо нее, а не параллельно с ней.

user4574 20.08.2024 16:28

Ответ: «Если ваша цель — заменить значение другого макроса вашим определением, то вам придется использовать всю строку 4+ бит #ifdef/#undef/#endif/#define»: в этом нет необходимости. #undef/#define достаточно.

Eric Postpischil 20.08.2024 16:54

Я кое-чему научился!

Dúthomhas 20.08.2024 19:29

Другие вопросы по теме