Любое предложение о том, как сделать ASSIGN
, если это безопасно?
В общем, вы не можете этого сделать. Вы должны не использовать макросы для lvalue. Это ужасная идея, и она, скорее всего, приведет к сумасшествию и невозможности найти ошибки.
Это действительно похоже на XY проблему . В вашем случае, как я понимаю, вы хотите создать макрос, чтобы упростить выражение, которое должно выполняться многократно в вашем коде.
Вместо использования простого макроса с одним аргументом вы можете определить макрос с двумя аргументами. Таким образом, он будет более совместим с остальной частью вашего кода, но при этом достигнет того же результата, используя более согласованный semanti c, который также "if-safe".
Вот оно ( Я называю это ASSIGN_FOO
просто чтобы отличить guish от вашего):
#define ASSIGN_FOO(x, v) do { (x)->has_value = true; (x)->value = v; } while (0)
struct foo var;
ASSIGN_FOO(var, 123);
Если вам интересно об этом do { ... } while (0)
, посмотрите здесь .
Если вы хотите, чтобы макрос возвращал назначенное значение, хотя (как вы ожидаете от обычного назначения), это не очень хороший вариант.
Вместо использования макроса Вы можете определить встроенную функцию, объявив ее с помощью __attribute__ ((always_inline))
. Таким образом, компилятор интегрирует код функции непосредственно в вызывающую программу, и функция будет действовать точно как макрос при компиляции программы, за исключением того, что теперь он более мощный, поскольку его можно использовать в большем количестве контекстов.
inline int __attribute__ ((always_inline)) assign_foo(struct foo *x, int value) {
x->has_value = true;
x->value = value;
return x->value;
}
struct foo var;
assign_foo(var, 123);
В дополнение к этому, не имеет особого смысла использовать макрос, который вы определили при обновлении значения в вашей структуре, поскольку он может легко привести к нежелательному неопределенному поведению, например:
struct foo var;
ASSIGN(var) += 5;
Что расширяется до:
var->has_value = true; var->value += 5; // Undefined behavior, var->value used uninitialized!
Решение здесь:
Если вы уже знаете, что значение присутствует, не имеет смысла переназначив has_value = true
, вы можете просто сделать приращение напрямую:
var->value += 10;
Если вы не знаете, присутствует ли значение, используйте функцию, чтобы сделать это безопасно вместо этого :
inline int __attribute__ ((always_inline)) increment_foo(struct foo *x, int value) {
if (!x->has_value) {
x->has_value = true;
x->value = value;
} else {
x->value += value;
}
return x->value;
}
increment_foo(var, 10);
Сравнение:
struct foo x;
printf("%d\n", ASSIGN(x) = 3); // Compilation error.
printf("%d\n", ASSIGN_FOO(x, 3); // Compilation error.
printf("%d\n", assign_foo(x, 3)); // No problem.
struct foo y;
printf("%d\n", ASSIGN(y) += 3); // Compilation error.
ASSIGN(y) += 3; printf("%d\n", y->value); // Undefined behavior.
printf("%d\n", increment_foo(y, 3)); // No problem.