2015-03-10 3 views
3

Я пишу код C для функции типа регистрации данных. Получите значение переменной, выполните простую операцию и запишите данные в другую переменную.Регистрация для разных типов данных в C

Ключевым моментом здесь является использование большого количества различных типов данных и операций с данными. Я попытался использовать указатели void для всех переменных. Затем я вызываю одну и ту же функцию для каждого.

Взгляните на функцию. Вы увидите, что я переписываю код снова и снова для каждого типа данных.

/***********************************************************/ 
    static void GenData(USHORT data_type, 
         USHORT operation, 
         void *var_p, 
         void *data_p) 
    /***********************************************************/ 
    { 
     switch (data_type) 
     { 
     case (DATA_INT): 
     switch (operation) { 
     case (OP_ONE_SHOT): 
      *(int*)data_p = *(int*)var_p; 
      break; 
     case (OP_COUNTER): 
      *(int*)data_p += 1; 
      break; 
     case (OP_CURR_TIME): 
      *(int*)data_p = (int)sytime; 
     case (OP_ELAPSED_TIME): 
      *(int*)data_p += delta_tick_time; 
      break; 
     case (OP_MIN): 
      if (*(int*)data_p > *(int*)var_p) { 
      *(int*)data_p = *(int*)var_p; 
      } 
      break; 
     case (OP_MAX): 
      if (*(int*)data_p < *(int*)var_p) { 
      *(int*)data_p = *(int*)var_p; 
      } 
      break; 
     case (OP_ADD_ITSELF): 
      *(int*)data_p += *(int*)data_p; 
      break; 
     default: 
      break; 
     } 
     break; 

     case (DATA_BYTE): 
     switch (operation) { 
     case (OP_ONE_SHOT): 
      *(BYTE*)data_p = *(BYTE*)var_p; 
      break; 
     case (OP_COUNTER): 
      *(BYTE*)data_p += 1; 
      break; 
     ... 
     ... 

     case (DATA_SHORT): 
     ...and so on... 
    } 

Арифметика на пустой указатель в C не является действительным. Я не уверен, что это означает, что я не могу улучшить эту функцию. Есть ли способ, которым я могу написать это, поэтому мне не нужно снова и снова переписывать детали «mathy»?

Как всегда, благодарю вас за ваше время и усилия, чтобы помочь.

+0

Пожалуйста, сообщите точное сообщение об ошибке, скопируйте и вставьте ошибку компилятора. –

+1

Возможно, связано: [Существует ли спецификатор преобразования для printf] (http://stackoverflow.com/questions/26728333/is-there-a-generic-conversion-specifier-for-printf/26728390#26728390) –

+0

Вы можете используйте шаблоны для типов, которые в конечном итоге имеют один и тот же код, но, в конце концов, причина, по которой разные типы, заключается в том, что они разные, а ваш код будет другим. Планируете ли вы позволить удвоить ++? У вас будет тип для коэффициентов, не будет ли его код другим? В зависимости от того, какой компилятор вы используете, вы можете использовать «typeof» для передачи типа, в который вы делаете. Если случаи могут быть объединены, вы также можете комбинировать их в своих операторах switch. Однако вам нужны такие типы, чтобы операции переполнения/переполнения выполнялись в правильном месте. – LawfulEvil

ответ

2

Это сравнительно легко создать шаблонный код с помощью макросов ...

#define CASE(DATA_TYPE, TYPE) \ 
    case (DATA_TYPE): \ 
    switch (operation) { \ 
    case (OP_ONE_SHOT): \ 
     *(TYPE*)data_p = *(TYPE*)var_p; \ 
     break; \ 
    case (OP_COUNTER): \ 
     *(TYPE*)data_p += 1; \ 
     break; \ 
    case (OP_CURR_TIME): \ 
     *(TYPE*)data_p = (int)sytime; \ 
    ...etc... 

static void GenData(USHORT data_type, 
        USHORT operation, 
        void *var_p, 
        void *data_p) 
{ 
    switch (data_type) 
    { 
    CASE(DATA_INT, int) 
    CASE(DATA_BYTE, BYTE) 
    CASE(DATA_SHORT, short) 
    ...etc... 
    } 
} 
+0

Ooops, у нас есть тот же ответ, удаляя ... –

+0

Я не могу заставить это скомпилироваться из-за использования 'data_p' и' var_p' внутри макроса. 'ошибка C2100: незаконная косвенность' – user432209

+0

@ user432209 Я не могу догадаться, что случилось, не видя ваш точный код. [Вот самодостаточная примерная программа] (http://ideone.com/75VVpd), которая компилируется чисто ... –

2

Другим решением было бы бросить оба аргумента к одному типу, который поддерживает все необходимые операции и приспосабливает длинный тип вы поддерживаете (скажем, , long int). Тогда вы могли бы выполнить операцию и привести результат обратно к вам типа:

static long DoOperation(USHORT operation, long var, long data) 
{ 
    switch(operation) { 
     case(OP_ONE_SHOT): 
      return data; 
     case (OP_COUNTER): 
      return data+1; 
     [...etc...] 
    } 
} 
static void GenData(USHORT data_type, 
        USHORT operation, 
        void *var_p, 
        void *data_p) 
{ 
    switch(data_type) { 
     case (DATA_INT): 
      *(int*)data_p = (int)DoOperation(operation, *(int*)var_p, *(int*)data_p); 
      break; 
     case (DATA_BYTE): 
      *(BYTE*)data_p = (BYTE)DoOperation(operation, *(BYTE*)var_p, *(BYTE*)data_p); 
      break; 
     [...etc...] 
    } 

} 

Конечно, нет никакой гарантии, что такой тип существует.

+0

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

+0

Ну, идея у меня была, чтобы избежать создания экземпляра одного и того же кода несколько раз. Поскольку арифметика для float/double полностью отличается от целочисленной арифметики, с этим подходом не справиться. 'int64_t' может быть обработан правильно, если вы измените' DoOperation' для работы с типом 'int64_t'. –

0

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

/***********************************************************/ 
static void GenData(USHORT data_type, 
        USHORT operation, 
        void *var_p, 
        void *data_p) 
/***********************************************************/ 
{ 
    int int_data, int_var;  

    switch (data_type) 
    { 
    case (DATA_INT): 
    switch (operation) { 
    case (OP_ONE_SHOT): 
     *(int*)data_p = *(int*)var_p; 
     break; 
    case (OP_COUNTER): 
     //*(int*)data_p += 1; 
     int_data = *(int*)data_p; 
     int_data +=1; 
     *(int*)data_p= int_data; 
     break; 
    case (OP_CURR_TIME): 
     *(int*)data_p = (int)sytime; 
    case (OP_ELAPSED_TIME): 
     //*(int*)data_p += delta_tick_time; 
     int_data = *(int*)data_p; 
     int_data += delta_tick_time; 
     *(int*)data_p= int_data; 
     break; 
     //...and so on... 
+0

В вашем коде '* (int *) data_p = int_data;' и подобные строки перезаписывают ценность данных 'int' по адресу памяти указанного вызывающего абонента, даже если данные на этом адресе могут иметь разный размер (например, только «BYTE»). Это вызывает неопределенное поведение. –

+0

Я показываю только случай типа 'int', если тип является' char' или 'long', вы должны использовать локальную переменную правильного типа. При вызове функции вы должны установить 'data_type' правильное значение для пройденного типа. Единственный способ убедиться, что данные имеют правильный тип, - написать функцию для типа данных и не использовать указатель void. – EffegiWeb

Смежные вопросы