在单个 C 宏中定义变量并“返回”表达式

问题描述 投票:0回答:1

注意:我对使用 GCC 语句表达式不感兴趣!

我正在使用红黑树实现一个集合数据结构,并使其适用于多种类型,我正在使用宏。

这是节点的定义:

typedef struct {
    bool colour;
    void* val;
    void* parent;
    void* left;
    void* right;
}* set;

我想创建一个宏函数

set_contains(s, t)
,如果
t
在集合
s
中,它将“返回”一个真实的表达式。我所说的“返回”是指我可以这样使用它:

if(set_contains(my_set, 6)) {
    ...
}

例如,如果 x 是奇数,则以下宏“返回”真表达式:

#define isOdd(x)                        \
    /* notice the lack of semicolon! */ \
    (bool)((x)&1)

我的

set_contains
宏如下所示:

#define set_contains(s, t)                                  \
    do {                                                    \
        set it = s;                                         \
        while(it && *(typeof((t))*)it->val != (t)) {        \
            if((t) < *(typeof((t))*)it->val) it = it->left; \
            else it = it->right;                            \
        }                                                   \
        /* This is what I want to "return" */               \
        (bool)it;                                           \
    } while(0)

如何在宏末尾添加

(bool)it
,以便我可以将整个内容用作表达式(如
isOdd
宏)?
do while
是必要的,因为我需要定义一个临时变量 (
it
) 来迭代集合。当然,我不能在
it
之后使用
while
,因为它在该范围内未定义。再一次,我想在不使用 GCC 语句表达式的情况下实现这一目标。

c data-structures macros set c-preprocessor
1个回答
0
投票

仅使用宏不足以实现此目的;也许最不痛苦的解决方案是创建多个类型感知函数并使用

_Generic
选择正确的函数:

#define set_contains(set, val) _Generic((val),                      \
                                        int: set_contains_int,      \
                                     double: set_contains_double,   \
                                     char *: set_contains_string,   \
                                     /* any additional types */     \
                                       )(set, val)
...
bool set_contains_int(set s, int val) { ... }
bool set_contains_double(set s, double val) { ... }
bool set_contains_string(set s, char *val) { ... }
...
if (set_contains(set, 6))
  // do something

_Generic
指令根据val
type
选择正确的函数。 缺点是你最终会得到很多重复的逻辑。

或者,您可以创建一个作为回调传递的比较器函数:

int cmp_int( const void *l, const void *r )
{
  const int *il = l;
  const int *ir = r;

  if ( *l < *r )
    return -1;
  else if ( *l > *r )
    return 1;
  
  return 0;
}

bool set_contains(set s, const void *val, int (*cmp)(const void *, const void *))
{
  ...
  while( it && cmp(it->val, val) != 0 )
  {
    if ( cmp(it->val, val) < 0 )
      it = it->left;
    else
      it = it->right;
  }
  return it != NULL;
}

这将被称为

int val = 6;
if (set_contains(set, &val, cmp_int))
  ...

但这意味着你不能直接用

set_contains
作为文字来调用
val
(像
set_contains(set, 6, cmp_int)
这样的东西是行不通的)。 为此,您仍然必须创建类型感知的前端,例如:

bool set_contains_int(set s, int val)
{
  return set_contains(s, &val, cmp_int);
}

对于这样的事情,我更喜欢第二种方法(在我看来,减少重复工作),但与真正的类型多态性相比,这两种方法都有点恶心。

© www.soinside.com 2019 - 2024. All rights reserved.