Заменить постояный вызов assert

126
04 февраля 2021, 17:50
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
    }
}

Это правильное решение? может что то можно сделать красивее?

Answer 1

Теоретически, Вы хотели бы писать:

    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)

Если важно сохранять результат, можно модифицировать макрос соответствующим образом, чтобы не создавалась временная переменная.

Answer 2

Ваше решение:

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 после каждого вызова) сократить нельзя, потому что он работает всегда, и всегда вызывает функции в заданном порядке (что временами бывает важным). Разве что с использованием хитрых макросов, как в другом ответе, но в таком случае "упрощение" получается сильно сложнее оригинального кода.

READ ALSO
Не правильно работает функция std::snprintf

Не правильно работает функция std::snprintf

Мне нужно чтобы выводилось Score: 0:0, а выводится так Score: 0Делаю так:

93
Как избежать множественного включения?

Как избежать множественного включения?

Можно ли создать объект класса внутри другого класса не подключая заголовочный файл в header с описанием этого классасумбурно

103
error C3867 C++

error C3867 C++

Пишет error C3867:"std::basic_string,std::allocator>::c_str" Динамические переменные не использую, нужно отсортировать массив строк c[100] в алфавитном порядке

97
чистка проекта в линукс. configure automake

чистка проекта в линукс. configure automake

Не знаю как назвать тему вопросаВ anjuta создаю проект на gtk

121