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
после каждого вызова) сократить нельзя, потому что он работает всегда, и всегда вызывает функции в заданном порядке (что временами бывает важным). Разве что с использованием хитрых макросов, как в другом ответе, но в таком случае "упрощение" получается сильно сложнее оригинального кода.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Мне нужно чтобы выводилось Score: 0:0, а выводится так Score: 0Делаю так:
Можно ли создать объект класса внутри другого класса не подключая заголовочный файл в header с описанием этого классасумбурно
Пишет error C3867:"std::basic_string,std::allocator>::c_str" Динамические переменные не использую, нужно отсортировать массив строк c[100] в алфавитном порядке
Не знаю как назвать тему вопросаВ anjuta создаю проект на gtk