Предварительная обработка C

#gcc #c-preprocessor

#gcc #c-препроцессор

Вопрос:

Почему некоторые строки заменяются, а другие не заменяются на #define?

 $ cat test.c
#define X_ REPLACED
int main() {
  X_x();
  X_();
  return 0;
} 

$ gcc -E test.c
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"

int main() {
  X_x();
  REPLACED();
  return 0;
}
  

Приведенное выше X_(); заменено, но X_x(); это не так. Каковы правила? Я нигде не могу найти никакой информации об этом, несмотря на долгое расследование.

Причина, по которой я смотрю на это: я хочу создать ссылку на библиотеку FFT (FFTW), имена процедур которой начинаются с fftwf_, fftw_, fftwl_ или fftwq_, в зависимости от того, используется ли одинарная, двойная, длинная двойная или квадратичная точность.

(версия gcc: 4.4.3)

Комментарии:

1. Я могу начать понимать причину вашего желания этого, но вам следует уточнить подробнее. Вы пытаетесь написать свой код так, чтобы он работал независимо от того, какой из них используется, чтобы несколько #define s или -D флагов могли изменять набор, используемый вашим кодом?

Ответ №1:

Поскольку препроцессор работает по каждому токену и X_x считается как один токен, следовательно, он игнорируется #define . Если вам нужно X_x стать REPLACEDx() , используйте sed или любое другое регулярное выражение для предварительной обработки кода.

Поскольку макросы являются немыми, анализ аргументов невозможен, поэтому вы не можете выполнить `#define fft_func(mytype Если вы хотите выполнить этот эффект без регулярных выражений, используйте

 #define CONCAT2(x, y) x##y
#define CONCAT(x, y) CONCAT2(x, y)
#define FFTW_FUNC(fft_type, fft_func) CONCAT(CONCAT(CONCAT(fftw, fft_type), _), fft_func)

int main() {
// ...
FFTW_FUNC(type, func)(args);
// ...
}
  

Для типов БПФ:

 #define FFTWT_FLOAT f
#define FFTWT_DOUBLE
#define FFTWT_LONG_DOUBLE l
#define FFTWT_QUAD_PRECISION q
  

Комментарии:

1. Вот пример того, насколько плохо это было бы: #define max(a,b) … (теперь поищите, где у вас есть «max» где угодно ).

2. Вы, конечно, имеете в виду #define CONCAT(x, y) CONCAT2(x, y)

3. @ChrisLutz исправлено; я знал, что забыл кое-что добавить

4. @crashmstr У кувалды есть свое применение. Полировка тонкого фарфора не входит в их число. Программист должен быть рассудительным.

5. Я бы использовал #ifndef FFTW_TYPE / #define FFTWT FFTWT_DOUBLE / #endif / #define FFTW(funcname) FFTW_FUNC(FFTWT, funcname) , чтобы пользователь мог передать -DFFTWT=FFTWT_LONG_DOUBLE в командной строке и изменить то, что использует скомпилированный файл. Небольшое дополнительное оборудование могло бы настроить соответствующий typedef , и если бы в какой-то момент для части кода абсолютно требовался определенный тип, он все равно был бы доступен.

Ответ №2:

Спасибо, это именно то, чего я хотел. Я немного отредактировал ваше решение следующим образом:

 #ifdef single
  #define myType fftwf_
#else
  #ifdef double
    #define myType fftw_
  #endif
#endif

#define CONCAT(x, y) x##y
#define FFTW_FUNC(fft_type, fft_func) CONCAT(fft_type, fft_func)

int main() {
  //
  FFTW_FUNC(myType, func)(args);
  //                                                   
}
  

Вышесказанное дает:

 $ gcc -E -Ddouble test.c; gcc -E -Dsingle test.c
# 1 "test.c"
# 1 "<built-in>"    
# 1 "<command-line>"
# 1 "test.c"
# 12 "test.c"
int main() {

  fftw_func(args);

}
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.c"
# 12 "test.c"
int main() {

  fftwf_func(args);

}