首页 文章

C型双关语问题

提问于
浏览
4

如何为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

    好吧,你可以提取演员表:

    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 ,那么't use the same type for everything, since there is no integer type that can represent all the values of all integer types. You'还需要一个单独的 testLimitsf 函数用于 float ,可能使用 long double 作为未来灵活性的通用类型 .

    • [编辑:我刚刚意识到,假设IEEE-754, double 实际上可以准确地代表您使用的所有类型的所有值 . 因此,轻微的可移植性限制,您可以使用 testLimitsf 来处理所有事情并处理双打]

    • 您确定在比较之前转换为(例如) uint8_t 是否值得?这个值在 uint8_t 的范围内,在这种情况下你不在范围内,在这种情况下模数减少使得比较有点无意义,除了在0和-1的特殊情况下 . 所以它可能是值得的,如果你没有陈述的东西使它如此,但它看起来很可疑 .

    • 你在评论中说,"I am trying to make that function more efficient" . 这可能违背了这一点 . 在逻辑上可以内联 testLimits 以及 switch 中对转换函数的调用,但我不会指望它 .

  • 1

    标准的下一个版本(C1x)将添加对类型泛型表达式的支持(来自维基百科的示例):

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

    gcc有一些初步的C1x支持 . 我认为_Generic尚未得到支持,但请记住未来 .

  • 2

    为什么不使用宏?

    #define DO_MY_WORK(TYPE)\
      if((*(TYPE*)pData > (TYPE)tLimits.zMax) || (*(TYPE*)pData < (TYPE)tLimits.zMin))\
      {\
          isWithinLimits = FALSE;\
      }
    
  • 1

    在C中没有简单的方法来执行这样的泛型编程 . 如果您担心维护,那么这可能是一个非常适合宏的场合 .

相关问题