wsxiaoys
wsxiaoys

Reputation: 155

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 would like to forbid macro call like this without changing the existing macro interface.

LEFT(n) = ...

Any idea?

Upvotes: 7

Views: 469

Answers (9)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507105

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))

Upvotes: 1

Lundin
Lundin

Reputation: 214275

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.

Upvotes: 0

Jens Gustedt
Jens Gustedt

Reputation: 78943

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))

Upvotes: 0

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215387

Try this:

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

Upvotes: 8

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.

Upvotes: 3

AProgrammer
AProgrammer

Reputation: 52314

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

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

Upvotes: 5

aib
aib

Reputation: 46981

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))

Upvotes: 1

ColWhi
ColWhi

Reputation: 1077

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

Upvotes: 3

Alexey Malistov
Alexey Malistov

Reputation: 26995

#undef LEFT
#undef RIGHT

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

Upvotes: 6

Related Questions