2011-06-16 60 views
4

如何使uint8_t,uint16_t,uint32_t,int8_t,int16_t,int32_t和float_t的通用函数如下所示?C类型双引号问题

我不喜欢在每种情况下重复相同的逻辑,因为你可以看到。每种情况唯一的区别是铸造。

我理想上喜欢一种遵循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; 
} 
+0

怎么可以这样'*(* UINT16)的pData>(UINT16)limits.Max'是有史以来真的吗? – 2011-06-16 07:41:05

+0

@Let_Me_Be:我想'limits.max'不一定总是65535! – 2011-06-16 07:43:14

+0

这是正确的,每个键都有自己的一组定义的最小/最大限制。 – thegreendroid 2011-06-16 07:47:38

回答

1

嗯,你可以提取石膏:

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,那么你不能对所有东西都使用相同的类型,因为没有整型可以表示所有整数类型的所有值。对于float,您还需要单独使用testLimitsf函数,或许使用long double作为未来灵活性的常见类型。
  • [编辑:我刚刚意识到,假设IEEE-754,double实际上可以准确地表示您使用的所有类型的所有值。因此,通过轻微的可移植性限制,您可以使用testLimitsf处理所有事务并处理双打]
  • 您确定在比较之前值得转换为(例如)uint8_t吗?要么这个值在uint8_t的范围内,在这种情况下你不需要转换,你可以做比较。否则,该值不在范围内,在这种情况下,模减少使得比较有点没有意义,除了在0和-1的特殊情况下。所以这可能是值得的,如果你还没有说明的话会这样做,但它看起来很腥。
  • 您的评论说,“我试图使该功能更有效”。这可能与此相反。在逻辑上可以内联testLimits,也可以调用switch中的投射函数,但我不会指望它。
+0

这绝对是最干净的解决方案看,到目前为止,我喜欢它:) 我想使用的int64_t应该做的伎俩。程序代码空间在我的目标环境中非常重要,所以我尽可能保持高效,同时遵守标准。 – thegreendroid 2011-06-16 10:50:04

2

为什么不能使用宏?

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

我曾考虑过使用宏,但它仍然生成相同的代码。我试图让这个功能更有效率。但是,看起来似乎没有简单的方法来实现我想要做的事情。无论如何,谢谢:) – thegreendroid 2011-06-16 07:53:27

3

有没有简单的方法做泛型编程这样的。如果你担心维修,那么这可能是一个难得的机会,其中宏是合适的。

+0

够公平的。我确实对这个问题思考过很长时间,我无法想出一个我喜欢的解决方案。哦,我想我必须忍受它。出于维护的目的,我想在这种情况下最好使用宏。谢谢:) – thegreendroid 2011-06-16 07:55:08

1

标准(C1X)将(从维基百科为例)添加类型的通用表达式的支持的下一个版本:

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

GCC有一些初步C1X支持。我认为_Generic尚未得到支持,但请记住未来。

+0

谢谢,我会记住这一点:) – thegreendroid 2011-06-16 10:29:25