Возможно ли, чтобы макросы препроцессора C содержали директивы препроцессора? - PullRequest
31 голосов
/ 19 ноября 2008

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

#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);

Ответы [ 7 ]

13 голосов
/ 19 ноября 2008

Препроцессор Boost (который работает как для 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
6 голосов
/ 19 ноября 2008

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

4 голосов
/ 19 ноября 2008

Единственное решение, которое у меня есть, это мошенничество - создайте список типов, у которых в качестве набора определений есть _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
0 голосов
/ 18 августа 2018

Пока вас интересуют только интегральные значения и предполагается, что аппаратное обеспечение использует комплимент 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))
0 голосов
/ 02 сентября 2009

Нет простого способа сделать это. Самое близкое, что вы можете получить, это #define большое количество макросов 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);
0 голосов
/ 19 ноября 2008

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

0 голосов
/ 19 ноября 2008

Я не думаю, что это тот случай, когда оператор ## не разрешен в #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 будет принимать только #defined символы, а не #define аргументы. Это две разные вещи.

...