Вопрос типа C - PullRequest
       27

Вопрос типа C

4 голосов
/ 16 июня 2011

Как сделать приведенную ниже функцию универсальной для uint8_t, uint16_t, uint32_t, int8_t, int16_t, int32_t и float_t?

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

В идеале мне бы хотелось решение, которое соответствует стандарту C и поэтому является переносимым.Любые идеи приветствуются.

Спасибо.

static bool_t IsWithinLimits(const dbKey_t *key, const void *data)
{
    bool_t isWithinLimits = TRUE;
    limits_t limits = getDefinedLimits(key);

    switch(key->type)
    {
      case TYPE_UINT8:
        if((*(const UINT8*)data > (UINT8)limits.max) || (*(const UINT8*)data < (UINT8)limits.min))
        {
          isWithinLimits = FALSE;
        }
        break;

      case TYPE_UINT16:
        if((*(UINT16*)pData > (UINT16)limits.max) || (*(UINT16*)data < (UINT16)limits.min))
        {
          isWithinLimits = FALSE;
        }
        break;

      case TYPE_UINT32:
       ...
       break;

      case TYPE_INT8:
       ...
       break;

      case TYPE_INT16:
       ...
       break;

      case TYPE_INT32:
       ...
       break;

      case TYPE_FLOAT:
       ...
       break;
    }

  return isWithinLimits;
}

Ответы [ 4 ]

3 голосов
/ 16 июня 2011

Нет простого способа сделать общее программирование, как это на C. Если вы беспокоитесь о техническом обслуживании, то это может быть редким случаем, когда уместен макрос.

2 голосов
/ 16 июня 2011

Почему бы не использовать макрос?

#define DO_MY_WORK(TYPE)\
  if((*(TYPE*)pData > (TYPE)tLimits.zMax) || (*(TYPE*)pData < (TYPE)tLimits.zMin))\
  {\
      isWithinLimits = FALSE;\
  }
1 голос
/ 16 июня 2011

Ну, вы могли бы извлечь приведения:

int64_t loadptr_uint8(const void *p)  {
    return *(uint8_t*)p;
}
int64_t convert_uint8(int64_t val) {
    return (uint8_t)val;
}

int testLimits(const limits_t *plimits, const void *pData, int64_t(*loadptr)(void*), int64_t (*convert)(int64_t)) {
    return loadptr(pData) <= convert(limits->max) && loadptr(pData) >= convert(limits->min);
}

switch(key->type) {
    case TYPE_UINT8:
        isWithinLimits = testLimits(&limits, pData, loadptr_uint8, convert_uint8);
        break;
    // etc
}

Или, если различные типы образуют непрерывный диапазон значений от 0, вы можете даже создать два массива указателей на функциии выполните:

bool isWithinLimits = testLimits(&limits, pData, loadptrs[key->type], converts[key->type]);

Примечания:

  • Вам все еще нужно написать две функции для каждого типа, хотя они легко генерируются макросом, если вы предпочитаете.
  • Кажется, для этого небольшого кода оно того не стоит.
  • Я выбрал int64_t, поскольку он способен представлять все значения всех целочисленных типов, которые вы используете, поэтому преобразования в int64_t никогда не сбрасывает информацию и никогда не изменяет результат сравнения по сравнению с выполнением того же сравнения в типе источника.Но если вы также хотите охватить uint64_t, то вы не можете использовать один и тот же тип для всего, поскольку нет целочисленного типа, который мог бы представлять все значения всех целочисленных типов.Вам также понадобится отдельная функция testLimitsf для float, возможно, использующая long double в качестве общего типа для будущей гибкости.
  • [Редактировать: я только что понял, предполагая IEEE-754, doubleна самом деле может точно представлять все значения всех типов, которые вы используете.Таким образом, с небольшим ограничением переносимости, вы можете использовать testLimitsf для всего и иметь дело с двойными числами]
  • Вы уверены, что стоит сравнить (например) с uint8_t перед сравнением?Либо значение находится в диапазоне для uint8_t, и в этом случае вам не нужно конвертировать, вы можете просто сделать сравнение.Или же значение не находится в диапазоне, и в этом случае уменьшение по модулю делает сравнение немного бессмысленным, за исключением особых случаев 0 и -1.Так что это может стоить того, если что-то, что вы не заявили, делает это так, но мне это кажется подозрительным.
  • Вы сказали в комментарии: «Я пытаюсь сделать эту функцию более эффективной».Это может пойти против этого.Логически возможно встроить testLimits, а также вызовы функций приведения в switch, но я бы на это не рассчитывал.
1 голос
/ 16 июня 2011

В следующей редакции стандарта (C1x) добавится поддержка обобщенных выражений типов (пример из Википедии):

#define cbrt(X) _Generic((X), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(X)

gcc имеет некоторую предварительную поддержку C1x. Я думаю, что _Generic еще не поддерживается, но помните об этом на будущее.

...