开发者

Is there any trick to forbid C macro to be called as a lvalue?

For example,

struct node {
  struct node *left, *right;
};
#define LEFT(X) (X->left)
#define RIGHT(X) (X->right)

I wou开发者_StackOverflow社区ld like to forbid macro call like this without changing the existing macro interface.

LEFT(n) = ...

Any idea?


Try this:

#define LEFT(X) ((X)->left+0)
#define RIGHT(X) ((X)->right+0)


#undef LEFT
#undef RIGHT

//template<class T>
inline const node * const LEFT(const node * X) {
    return X->left;
}


I'd go with the inline function, but if you want a macro:

#define LEFT(X) (1 ? (X)->left : 0)


I don't think there is any way of preventing that. Probably the best way would be not to use macros.


You can use the ternary operator to force the result of the macro to be an rvalue, but the error message might be confusing to users:

struct node {
   node *left, *right;
};
#define LEFT( x ) ( true ? (x).left : (node*)0 )
int main() {
   node n;
   // LEFT( n ); // error
   node *l = LEFT( n ); // OK
}

The trickery there is in the semantics of that specific operator. The type of the expression containing just the operator is a common type (or a type convertible from) of the two branches of the expression, even if only one of them is ever evaluated. Now, when the compiler evaluates true ? x.left : (node*)0 it resolves to the expression x.left but with the common type of x.left and (node*)0. Both of them are basically the same type, with the only detail that (node*)0 is an rvalue, rather than a lvalue.


Maybe const, though it requires an explicit type:

#define LEFT(X) ((const struct node *) (X->left))

...though if you have the typeof compiler extension:

#define LEFT(X) ((const typeof(X->left)) (X->left))


For C++, I would use unary +:

#define LEFT(X) (+((X)->left))
#define RIGHT(X) (+((X)->right))

For a general macro case, this has the advantage over adding +0 that it also works for void* and function pointers. For C, you can use the comma operator

#define LEFT(X) (0, ((X)->left))
#define RIGHT(X) (0, ((X)->right))


I'd just go for an explicit cast

#define LEFT(X) ((struct node*)(X->left))
#define RIGHT(X) ((struct node*)(X->right))

the result of such a cast is always an rvalue. If in addition you don't want to allow to change what the pointers are pointing to add a const as was already given in another answer:

#define LEFT(X) ((struct node const*)(X->left))
#define RIGHT(X) ((struct node const*)(X->right))


You don't write "accessor" macros like that, you do

typedef struct
{
  ...
} X;

X x;

#define LEFT (x.y.z.left)

Then you access the LEFT macro like any other variable. This makes the code more readable while at the same time effectively disabling the whole unsafe, ugly-looking, error prone, undefined-behavior ballet associated with function-like macros.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜