Есть ли хитрость, чтобы запретить макрос C вызываться как левое значение? - PullRequest
7 голосов
/ 15 апреля 2011

Например,

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

Я бы хотел запретить вызов макросов подобным образом без изменения существующего интерфейса макросов.

LEFT(n) = ...

Есть идеи?

Ответы [ 9 ]

8 голосов
/ 15 апреля 2011

Попробуйте это:

#define LEFT(X) ((X)->left+0)
#define RIGHT(X) ((X)->right+0)
6 голосов
/ 15 апреля 2011
#undef LEFT
#undef RIGHT

//template<class T>
inline const node * const LEFT(const node * X) {
    return X->left;
}
5 голосов
/ 15 апреля 2011

Я бы пошел со встроенной функцией, но если вы хотите макрос:

#define LEFT(X) (1 ? (X)->left : 0)
3 голосов
/ 15 апреля 2011

Вы можете использовать троичный оператор, чтобы результат макроса был равен r-значению, но сообщение об ошибке может сбивать пользователей с толку:

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
}

Обман в семантике этого конкретногооператор.Тип выражения, содержащего только оператор, является общим типом (или типом, конвертируемым из) двух ветвей выражения, даже если когда-либо вычисляется только одна из них.Теперь, когда компилятор вычисляет true ? x.left : (node*)0, он преобразуется в выражение x.left, но с общим типом x.left и (node*)0.Оба они в основном одного и того же типа, с единственной деталью, которая (node*)0 является значением, а не значением l.

3 голосов
/ 15 апреля 2011

Я не думаю, что есть способ предотвратить это.Вероятно, лучшим способом было бы не использовать макросы.

1 голос
/ 15 апреля 2011

Для C ++ я бы использовал унарный +:

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

. В общем случае макросов это имеет преимущество перед добавлением +0, что он также работает для void* и указателей на функции.Для C вы можете использовать оператор запятой

#define LEFT(X) (0, ((X)->left))
#define RIGHT(X) (0, ((X)->right))
1 голос
/ 15 апреля 2011

Может быть const, хотя для этого требуется явный тип:

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

... хотя, если у вас есть расширение компилятора typeof:

#define LEFT(X) ((const typeof(X->left)) (X->left))
0 голосов
/ 15 апреля 2011

Вы не пишете макросы «accessor» подобным образом, вы делаете

typedef struct
{
  ...
} X;

X x;

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

Затем вы получаете доступ к макросу LEFT, как и любая другая переменная.Это делает код более читабельным и в то же время эффективно отключает весь небезопасный, уродливый, склонный к ошибкам балет с неопределенным поведением, связанный с функциональными макросами.

0 голосов
/ 15 апреля 2011

Я бы просто пошел на явное приведение

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

результатом такого преобразования всегда является rvalue.Если, кроме того, вы не хотите разрешать изменять указатели, чтобы добавить const, как уже было указано в другом ответе:

#define LEFT(X) ((struct node const*)(X->left))
#define RIGHT(X) ((struct node const*)(X->right))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...