Могут ли макросы препроцессора C содержать директивы препроцессора?

Я хотел бы сделать эквивалент следующего:

#define print_max(TYPE) \
#  ifdef TYPE##_MAX \
     printf("%lld\n", TYPE##_MAX); \
#  endif

print_max(INT);

Теперь #ifdef или любая вложенная директива препроцессора не допускается, насколько я могу судить в макросе функции. Есть идеи?

Обновление: похоже, это невозможно. Даже взлом для проверки во время выполнения кажется недостижимым. Так что я думаю, что выберу что-то вроде:

#ifndef BLAH_MAX
#  define BLAH_MAX 0
#endif
# etc... for each type I'm interested in

#define print_max(TYPE) \
    if (TYPE##_MAX) \
       printf("%lld\n", TYPE##_MAX);

print_max(INT);
print_max(BLAH);
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
32
0
36 952
7

Ответы 7

Я не думаю, что это случай, когда оператор ## запрещен в #ifdef. Я пробовал это:

#define _print_max(TYPE) \
#ifdef TYPE \
printf("%lld\n", _TYPE); \
#endif

#define print_max(TYPE) _print_max(MAX##_TYPE)


void main() 
{
    print_max(INT)
}

и он все еще не работал (ему не нравился #ifdef TYPE). Проблема в том, что #ifdef принимает только # определенные символы, а не #define аргументы. Это разные вещи.

Я пробовал это раньше. Проблема в том, что # уже зарезервирован для преобразования параметра макроса в строку. Он не анализируется как токен препроцессора, как в #define.

Очень простой ответ, объясняющий, почему это просто невозможно.

polynomial_donut 22.08.2018 21:39

В отличие от шаблонов препроцессор не полный по Тьюрингу. #ifdef внутри макроса невозможен. Ваше единственное решение - убедиться, что вы вызываете print_max только для типов, для которых определен соответствующий _MAX, например INT_MAX. Компилятор обязательно скажет вам, когда это не так.

Единственное решение, которое у меня есть, - это обман: создать список типов, которые имеют _XXX_MAX в качестве набора определений, а затем использовать его. Я не знаю, как создать список в автоматическом режиме в препроцессоре, поэтому не пытаюсь. Предполагается, что список не будет слишком длинным и не будет поддерживаться слишком интенсивно.

#define PRINT_MAX(type) printf("%lld\n", _TYPE##_MAX);
#define HAVE_MAX(type) _TYPE##_MAX // not sure if this works 


/* a repetitious block of code that I cannot factor out - this is the cheat */
#ifdef HAVE_MAX(INT)
#define PRINT_INT_MAX PRINT_MAX(INT)
#endif

#ifdef HAVE_MAX(LONG)
#define PRINT_LONG_MAX PRINT_MAX(LONG)
#endif
/* end of cheat */


#define print_max(type) PRINT_##TYPE##_MAX

Библиотека Препроцессор ускорения (которая работает как для C, так и для C++, хотя Boost в целом является библиотекой C++) может помочь в решении такого рода задач. Вместо использования #ifdef в макросе (что недопустимо) он помогает вам включать файл несколько раз, каждый раз определяя разные макросы, чтобы в файле можно было использовать #ifdef.

Следующий код, если он сохранен в max.c, должен делать то, что вы хотите для каждого из слов, перечисленных в MAXES #define в верхней части файла. Однако это не сработает, если любое из значений _MAX является плавающей точкой, поскольку препроцессор не может обрабатывать плавающую точку.

(Boost Processor - удобный инструмент, но он не совсем простой; вы можете решить, является ли этот подход улучшением по сравнению с копированием и вставкой.)

#define MAXES (SHRT)(INT)(LONG)(PATH)(DOESNT_EXIST)

#if !BOOST_PP_IS_ITERATING

/* This portion of the file (from here to #else) is the "main" file */

#include <values.h>
#include <stdio.h>
#include <boost/preprocessor.hpp>

/* Define a function print_maxes that iterates over the bottom portion of this
 * file for each word in MAXES */
#define BOOST_PP_FILENAME_1 "max.c"
#define BOOST_PP_ITERATION_LIMITS (0,BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(MAXES)))
void print_maxes(void) {
#include BOOST_PP_ITERATE()
}

int main(int argc, char *argv[])
{
    print_maxes();
}

#else

/* This portion of the file is evaluated multiple times, with
 * BOOST_PP_ITERATION() resolving to a different number every time */

/* Use BOOST_PP_ITERATION() to look up the current word in MAXES */
#define CURRENT BOOST_PP_SEQ_ELEM(BOOST_PP_ITERATION(), MAXES)
#define CURRENT_MAX BOOST_PP_CAT(CURRENT, _MAX)

#if CURRENT_MAX
printf("The max of " BOOST_PP_STRINGIZE(CURRENT) " is %lld\n", (long long) CURRENT_MAX);
#else
printf("The max of " BOOST_PP_STRINGIZE(CURRENT) " is undefined\n");
#endif

#undef CURRENT
#undef CURRENT_MAX

#endif

Нет простого способа сделать это. Самое близкое, что вы можете сделать, - это # ​​определить большое количество макросов IFDEF, таких как:

#undef IFDEF_INT_MAX
#ifdef INT_MAX
#define IFDEF_INT_MAX(X)  X
#else
#define IFDEF_INT_MAX(X)
#endif

#undef IFDEF_BLAH_MAX
#ifdef BLAH_MAX
#define IFDEF_BLAH_MAX(X)  X
#else
#define IFDEF_BLAH_MAX(X)
#endif

     :

поскольку вам нужно их много (и они могут быть полезны в нескольких местах), это делает много Имеет смысл поместить все это в отдельный заголовочный файл ifdefs.h, который вы можете включить в любой момент, когда они вам понадобятся. Вы даже можете написать сценарий, который регенерирует ifdef.h из список "интересующих макросов"

Тогда ваш код станет

#include "ifdefs.h"
#define print_max(TYPE) \
IFDEF_##TYPE##_MAX( printf("%lld\n", TYPE##_MAX); )

print_max(INT);
print_max(BLAH);

Пока вас интересуют только целые значения и предполагается, что оборудование использует 2 дополнения и 8-битные байты:

// Of course all this MAX/MIN stuff assumes 2's compilment, with 8-bit bytes...

#define LARGEST_INTEGRAL_TYPE long long

/* This will evaluate to TRUE for an unsigned type, and FALSE for a signed
 * type.  We use 'signed char' since it should be the smallest signed type
 * (which will sign-extend up to <type>'s size) vs. possibly overflowing if
 * going in the other direction (from a larger type to a smaller one).
 */
#define ISUNSIGNED(type) (((type) ((signed char) -1)) > (type) 0)

/* We must test for the "signed-ness" of <type> to determine how to calculate
 * the minimum/maximum value.
 *
 * e.g., If a typedef'ed type name is passed in that is actually an unsigned
 * type:
 *
 *  typedef unsigned int Oid;
 *  MAXIMUM_(Oid);
 */
#define MINIMUM_(type)  ((type) (ISUNSIGNED(type) ? MINIMUM_UNSIGNED_(type)   \
                              : MINIMUM_SIGNED_(  type)))

#define MAXIMUM_(type)  ((type) (ISUNSIGNED(type) ? MAXIMUM_UNSIGNED_(type)   \
                          : MAXIMUM_SIGNED_(  type)))

/* Minumum unsigned value; zero, by definition -- we really only have this
 * macro for symmetry.
 */
#define MINIMUM_UNSIGNED_(type)     ((type) 0)

// Maximum unsigned value; all 1's.
#define MAXIMUM_UNSIGNED_(type)         \
     ((~((unsigned LARGEST_INTEGRAL_TYPE) 0))   \
      >> ((sizeof(LARGEST_INTEGRAL_TYPE) - sizeof(type)) * 8))

/* Minimum signed value; a 1 in the most-significant bit.
 *
 * We use LARGEST_INTEGRAL_TYPE as our base type for the initial bit-shift
 * because we should never overflow (i.e., <type> should always be the same
 * size or smaller than LARGEST_INTEGRAL_TYPE).
 */
#define MINIMUM_SIGNED_(type)       \
  ((type)               \
   ((signed LARGEST_INTEGRAL_TYPE)  \
    (~((unsigned LARGEST_INTEGRAL_TYPE) 0x0) << ((sizeof(type) * 8) - 1))))

// Maximum signed value; 0 in most-significant bit; remaining bits all 1's.
#define MAXIMUM_SIGNED_(type)       (~MINIMUM_SIGNED_(type))

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