Как получить тип переменной в коде C? - PullRequest
10 голосов
/ 06 февраля 2012

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

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

#pragma omp parallel for reduction(+:x)

в это:

my_reduction(PLUS, &x, sizeof(x));
#pragma omp parallel for

где раньше у меня (скажем)

enum reduction_op {PLUS, MINUS, TIMES, AND,
  OR, BIT_AND, BIT_OR, BIT_XOR, /* ... */};

А my_reduction имеет подпись

void my_reduction(enum reduction_op op, void * var, size_t size);

Помимо прочего, my_reduction должен будет применить операцию сложения к редукционной переменной, как первоначально предполагал программист. Но моя функция не может знать, как сделать это правильно. В частности, хотя он знает тип операции (PLUS), местоположение исходной переменной (var) и размер типа переменной, он не знает сам тип переменной. В частности, он не знает, имеет ли var целочисленный тип или тип с плавающей точкой. От POV низкого уровня операция сложения для этих двух классов типов полностью отличается.

Если бы только нестандартный оператор typeof, который поддерживает GCC, работал бы так, как работает sizeof - возвращая некоторую переменную типа - я мог бы легко решить эту проблему. Но typeof на самом деле не похож на sizeof: его можно использовать только, по-видимому, в объявлениях l-значения.

Теперь компилятор, очевидно, знает тип x до того, как завершит генерацию исполняемого кода. Это заставляет меня задуматься, могу ли я каким-то образом использовать синтаксический анализатор GCC, просто чтобы получить тип x и передать его моему сценарию, а затем снова запустить GCC, чтобы скомпилировать мой измененный исходный код. Тогда было бы достаточно просто объявить

enum var_type { INT8, UINT8, INT16, UINT16, /* ,..., */ FLOAT, DOUBLE};
void my_reduction(enum reduction_op op, void * var, enum var_type vtype);

И my_reduction могут быть преобразованы соответствующим образом перед разыменованием и применением оператора.

Как видите, я пытаюсь создать некий "диспетчерский" механизм на C. Почему бы просто не использовать перегрузку C ++? Поскольку мой проект ограничивает меня в работе с унаследованным исходным кодом, написанным на C. Я могу автоматически изменять код с помощью скрипта, но не могу переписать его на другой язык.

Спасибо!

Ответы [ 5 ]

9 голосов

C11 _Generic

Не является прямым решением, но оно позволяет вам достичь желаемого результата, если вы терпеливы кодировать все типы, как в:

#include <assert.h>
#include <string.h>

#define typename(x) _Generic((x), \
    int:     "int", \
    float:   "float", \
    default: "other")

int main(void) {
    int i;
    float f;
    void* v;
    assert(strcmp(typename(i), "int")   == 0);
    assert(strcmp(typename(f), "float") == 0);
    assert(strcmp(typename(v), "other") == 0);
}

Скомпилируйте и запустите с:

gcc -std=c11 a.c
./a.out

Хорошая отправная точка с тоннами типов может быть найдена в этом ответе .

Протестировано в Ubuntu 17.10,GCC 7.2.0.GCC добавил поддержку только в 4.9.

4 голосов
/ 09 декабря 2016

Вы можете использовать функцию sizeof для определения типа, пусть переменная неизвестного типа будет var.тогда

if(sizeof(var)==sizeof(char))
        printf("char");
    else if(sizeof(var)==sizeof(int))
        printf("int");
    else if(sizeof(var)==sizeof(double))
        printf("double");

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

4 голосов
/ 06 февраля 2012

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

void int_reduction (enum reduction_op op, void * var, size_t size);

#define reduction(type,op,var,size) type##_reduction(op, var, size)

...
reduction(int, PLUS, &x, sizeof(x)); // function call

Обратите внимание, что это очень плохая практика, и ее следует использовать только в качестве крайней меры при плохом обслуживаниинаписанный старый код, если даже тогда.При таком подходе нет безопасности типов или других подобных гарантий.

Более безопасный подход - это явный вызов int_reduction() из вызывающей стороны или вызов универсальной функции, которая решает тип во время выполнения:

void reduction (enum type, enum reduction_op op, void * var, size_t size)
{
  switch(type)
  {
    case INT_TYPE:
      int_reduction(op, var, size);
      break;
    ...
  }
} 

Если int_reduction встроен и выполнены различные другие оптимизации, эта оценка времени выполнения не обязательно намного медленнее, чем запутанные макросы, но гораздо безопаснее.

2 голосов
/ 06 февраля 2012

Вы также можете настроить GCC с помощью плагина или расширения MELT для своих нужд.Тем не менее, это требует понимания некоторых внутренних представлений GCC (Gimple, Tree), которые являются сложными (так что, по крайней мере, это займет у вас несколько дней работы).

Но типы - это только для компиляции в C.реифицированные.

2 голосов
/ 06 февраля 2012

GCC обеспечивает расширение typeof .Это не стандарт, но достаточно распространенный (некоторые другие компиляторы, например, clang / llvm, имеют его).

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

...