class Base {
public:
int err;
Base() {
err = func1();
assert(err);
err = func2(arg1);
assert(err);
err = func3(arg2, arg3);
assert(err);
// some code
}
}
Можно как нибудь красивее написать?
у меня есть такое решение:
class Base {
public:
int err;
Base() {
if (err = func1()
|| err = func2(arg1)
|| err = func3(arg2, arg3) {
assert(err);
}
// some code
}
}
Это правильное решение? может что то можно сделать красивее?
Теоретически, Вы хотели бы писать:
assert(func1());
assert(func2(arg1));
assert(func3(arg2, arg3));
Но тогда при компиляции в Release режиме макрос раскроется в "ничего" и не будет даже вызова функций. Это решается написанием своего собственного макроса, который безусловно вычисляет аргумент и затем вызывает оригинальный assert:
#define my_assert(...) do { decltype(auto) res = __VA_ARGS__; assert(res); } while(false)
и использовать как
my_assert(func1());
my_assert(func2(arg1));
my_assert(func3(arg2, arg3));
Зачем нужен do { ... } while(false)? Это стандартная техника в макросах, которая вынуждает пользователя писать ; после вызова. Если бы реализация была #define my_assert(...) { decltype(auto) res = __VA_ARGS__; assert(res); }, то строка my_assert(f()) была бы валидной (без ; на конце!). Так что это лишь вопрос консистентности кода.
Без макроса обойтись нельзя. Оригинальный assert является макросом, так как он вынужден работать с __LINE__, __FILE__, __PRETTY_FUNCTION__ и прочими макро-определениями, чтобы выводить подробное сообщение об ошибке. Поэтому и обертка над ним обязана быть макросом (по крайней мере до появления std::source_location)
Если важно сохранять результат, можно модифицировать макрос соответствующим образом, чтобы не создавалась временная переменная.
Ваше решение:
if (err = func1()
|| err = func2(arg1)
|| err = func3(arg2, arg3) {
assert(err);
}
неверно, потому что по идее err должно быть комбинацией возвращаемых значений функций, а вы его каждый раз переприсваиваете.
Вопрос можно ли сделать лучше, чем оригинальное:
err = func1();
assert(err);
err = func2(arg1);
assert(err);
err = func3(arg2, arg3);
assert(err);
На первый взгляд, можно написать изящнее, например, так:
int err = func1() && func2(arg1) && func3(arg2, arg3);
assert(err);
но к сожалению, это будет не всегда правильным. Дело в том, что логические выражения вычисляются частично, конкретно данное выражение будет вычисляться только до первого возвращенного нуля. Если хоть один член логического AND является ложью, всё выражение также будет ложным, и вычислять остальные члены нет необходимости. В некоторых языках есть возможность задать полное вычисление, но C++ такой языковой конструкции не предоставляет.
Подобную сокращенную конструкцию всё же можно использовать, если после возврата нуля дальнейшая работа теряет смысл и, соответственно, вызывать оставшиеся функции нет необходимости. Но в общем случае ваш оригинальный код (с assert после каждого вызова) сократить нельзя, потому что он работает всегда, и всегда вызывает функции в заданном порядке (что временами бывает важным). Разве что с использованием хитрых макросов, как в другом ответе, но в таком случае "упрощение" получается сильно сложнее оригинального кода.
Продвижение своими сайтами как стратегия роста и независимости