Определение наличия прототипа с правильным типом возврата - PullRequest
4 голосов
/ 19 декабря 2010

Вот случайная проблема в C черной магии, с которой я только что придумал:

Напишите функцию, которая возвращает 1, если malloc был создан прототип для возврата типа указателя, и 0, если malloc имеет тип возврата int (либо неявно, либо из-за неправильного прототипа), без вызова любое поведение, определяемое реализацией или неопределенное .

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

Мне не нужно решение для немедленного использования, но оно может быть удобно в configure сценариях и подобных.

Обновление: Лучший пример полезности (с strdup вместо malloc):

#undef strdup
#define strdup(x) (strdup_has_proto ? strdup((x)) : my_strdup((x)))

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

Ответы [ 4 ]

0 голосов
/ 22 декабря 2010

Поскольку можно добавить void * и int, я подумал о других операторах, таких как сравнение. Результаты, особенно с трюком R ?:, странно противоречивы:

int f() { return 0; }

void *g() { return 0; }

#define T(x)  (1?0:x())   /* R's trick from a comment */

void h()
{
    void *n = 0;
    int i = 0;

    f() > n;  /* m.c:12: warning: comparison between pointer and integer */
    g() > n;
    T(f) > n;
    T(g) > n;

    f() > i;
    g() > i;  /* m.c:18: warning: comparison between pointer and integer */
    T(f) > i;
    T(g) > i; /* m.c:20: warning: comparison between pointer and integer */
}
0 голосов
/ 22 декабря 2010

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

0 голосов
/ 22 декабря 2010

Некоторые частичные ответы и ответы на связанные вопросы:

Если предполагаемая функция должна возвращать тип с плавающей запятой, тест прост:

#define HAS_PROTO_FP_RET_1_ARG(f) !!((1?1:(f)(0))/2)

1 получает повышениек типу с плавающей запятой, если и только если объявлено, что f возвращает тип с плавающей запятой, а деление на 2 приводит к ненулевому значению, если и только если 1 имеет тип с плавающей запятой.

Этот тест может бытьполезно для проверки наличия математических функций C99.

С указателями потенциально полезно выражение (1?0:(f)(0)) - оно оценивается как 0 (int) или (void *)0) (нулевой указатель)постоянная) в зависимости от типа возврата f.Но мне еще предстоит найти какой-либо хитрый способ проверить, имеет ли выражение целочисленное значение или тип указателя.

Большая проблема, с которой я сталкиваюсь, состоит в том, что void * не может участвовать в арифметике указателей и неявно не конвертируется вдругие типы указателей в арифметических контекстах.Например, если это произойдет, это сработает (слегка нарушая мои правила, касающиеся UB / IDB):

#define HAS_PROTO_PTR_RET_1_ARG(f) ((int)((char (*)[2])2 - (1?0:(f)(0))) == 1)

Есть идеи для решения этой проблемы?

Обновление: У меня есть решение, которое зависит только от того, что intmax_t больше int:

#define HAS_PROTO_PTR_RET_1_ARG(f) ( \
    sizeof(int)!=sizeof(void *) ? sizeof ((f)(0)) == sizeof(void *) : \
    sizeof (1?(intmax_t)0:(f)(0)) == sizeof(void *) )

Есть два случая.Если int и void * имеют разные размеры, мы просто проверяем размер возвращаемого значения.В противном случае int и void * имеют одинаковый размер, поэтому (по большому предположению) intmax_t и void * не имеют одинаковый размер.Мы создаем выражение, тип которого равен intmax_t, если тип возвращаемого значения int, и void *, если тип возвращаемого значения void *, и проверяем его размер.

Этот макрос завершается ошибкой только на машинах, гдеint, intmax_t и void * имеют одинаковый размер.Практически говоря, это означает только на DSP с 64-битной или более int.Поскольку почти все настоящие POSIX и POSIX-подобные системы имеют 32-битный int фиксированного размера и intmax_t должен быть как минимум 64-битным, это достаточно портативно, чтобы сделать меня счастливым.

0 голосов
/ 19 декабря 2010

Как насчет великолепно плохих хаков?

char has_malloc = 0;
#define malloc(x) \
     has_malloc = 1; \
     malloc((x))
//.. run includes here
#undef malloc

Но тогда вы можете просто #include <malloc.h> для гарантии.

Редактировать: Почему бы просто не определить int malloc ()?Если кто-то пытается вызвать int malloc, он должен вместо этого вызвать вашу версию.

...