Re[17]: api design: return code or exception - formal criter
От: Николай Ивченков  
Дата: 07.09.09 15:13
Оценка:
ЮЖ>Смысл принципа: не хочу — не плачу. Если у меня из контекста использования автоматически вытекает выполнения предусловия — зачем мне нужна проверка "внутри"? На всякий случай? Нарушение в том, что в этом случае у меня нет рычага для ее отключения. А насколько это бьет по производительности — совершенно неважно.

Ну, если брать это за аксиому...

НИ>>Использование механизма исключений всё равно необходимо, т.к. у аллокатора нет иного способа сообщения об ошибке выделения памяти.


ЮЖ>Ты хочешь сказать что аллокатор кидает out_of_range?


Нет, я хочу сказать, что, устранив throw, соответствующий проверке предусловия pos <= str.size(), мы всё равно не получим конструктор, не генерирующий исключений (для которого компилятор потенциально мог бы сгенерировать более оптимальный код).
Re[18]: api design: return code or exception - formal criter
От: Юрий Жмеренецкий ICQ 380412032
Дата: 07.09.09 16:41
Оценка: 4 (1)
Здравствуйте, Николай Ивченков, Вы писали:

ЮЖ>>Смысл принципа: не хочу — не плачу. Если у меня из контекста использования автоматически вытекает выполнения предусловия — зачем мне нужна проверка "внутри"? На всякий случай? Нарушение в том, что в этом случае у меня нет рычага для ее отключения. А насколько это бьет по производительности — совершенно неважно.


НИ>Ну, если брать это за аксиому...


Просто ты предполагаешь что, в обсуждаемом случае, производительность — главный аспект. Это не всегда так, см. ниже.


ЮЖ>>Ты хочешь сказать что аллокатор кидает out_of_range?


НИ>Нет, я хочу сказать, что, устранив throw, соответствующий проверке предусловия pos <= str.size(), мы всё равно не получим конструктор, не генерирующий исключений (для которого компилятор потенциально мог бы сгенерировать более оптимальный код).


Ок, теперь понял о чем ты. Меня больше интересует поведение клиента в том случае, когда он не смог обеспечить выполнение предусловий. Могу ли я в этом случае (как реализующий функцию) переводить программу в состояние 'сгенерированно исключение'? Клиент не выполнил своего контракта — как я могу ожидать от него готовности к такой ситуации? Если он готов обработать такую ситуацию — почему он сразу не выполнил предусловия?, он ведь заинтересован в результате. Возбудив исключение — я просто замаскирую дефект, логическую ошибку программиста (клиента). Логическую — не потому что logic_error, а потому что это именно ошибка в логике, неспособность понять формулу 'если P то Q'. Такие ошибки надо исправлять (не допускать), а не обрабатывать. Есть еще некоторые проблемы, но они уже являются производными от этой.
Re[4]: api design: return code or exception - formal criteri
От: crable США  
Дата: 07.09.09 16:57
Оценка:
Здравствуйте, Vain, Вы писали:

V>Здравствуйте, crable, Вы писали:


V>>>К чему это всё скажите вы? А к тому что иногда поддержка исключений библиотекой приводит к образованию некоторого пользовательского болота, организованного недобросовестными программистами использующими библиотеку, из которого вам впоследствии будет трудно выбраться.

V>>>Иногда лучше вообще не пользоваться исключениями и "заставлять" пользователя обрабатывать код возврата.
C>>А мне показалось, что лучше не ставить ассерты перед throw и недобросовестеыми тут выглядят как раз программисты библиотеки, сделавшие это.
V>Тогда придётся перезапускать приложение с включёнными галками ловить исключения, что тоже не есть хорошо.

Во первых, каким образом использование кодов возврата вместо исключений поможет решить проблему с ассертами?

Во-вторых, в чём же всё-таки были виноваты пользователи билиотеки?

И в третьих, ты сам описал ситуацию, при которой эти ассерты только мешают, более того, я, например, склонен считать, что так будет в большинстве случаев. Добалять ассерты только потому, что кому-то трудно раз в сто лет заставить студию перехватывать исключения выглядит, по меньшей мере нелепо. Кстати, для того, чтобы поставить эту галку вовсе необязятельно перезапускать приложение.
The last good thing written in C was Franz Schubert's Symphony No. 9.
Re[19]: api design: return code or exception - formal criter
От: Николай Ивченков  
Дата: 07.09.09 18:22
Оценка:
Юрий Жмеренецкий:

ЮЖ>>>Смысл принципа: не хочу — не плачу. Если у меня из контекста использования автоматически вытекает выполнения предусловия — зачем мне нужна проверка "внутри"? На всякий случай? Нарушение в том, что в этом случае у меня нет рычага для ее отключения.


Можно привести пример, где предоставляется выбор: operator[] и at.

ЮЖ>Меня больше интересует поведение клиента в том случае, когда он не смог обеспечить выполнение предусловий. Могу ли я в этом случае (как реализующий функцию) переводить программу в состояние 'сгенерированно исключение'? Клиент не выполнил своего контракта — как я могу ожидать от него готовности к такой ситуации? Если он готов обработать такую ситуацию — почему он сразу не выполнил предусловия?, он ведь заинтересован в результате. Возбудив исключение — я просто замаскирую дефект, логическую ошибку программиста (клиента). Логическую — не потому что logic_error, а потому что это именно ошибка в логике, неспособность понять формулу 'если P то Q'. Такие ошибки надо исправлять (не допускать), а не обрабатывать. Есть еще некоторые проблемы, но они уже являются производными от этой.


Видимо, это такая своеобразная замена assert-у. Предполагается, что программист должен отлавливать исключения std::logic_error (и производные от него) и трактовать их как внутренние ошибки в программе. Для release-сборки это приемлемо, а вот для debug-сборки assert-ы, вероятно, предпочтительнее (хотя я где-то читал, что есть люди, практикующие компоновку debug- и release-модулей, и для них использование assert-ов в шаблонах и inline-функциях оборачивается нарушением ODR).
Re[6]: api design: return code or exception - formal criteri
От: Erop Россия  
Дата: 07.09.09 20:18
Оценка:
Здравствуйте, Vain, Вы писали:

V>Одна библиотека не сможет этого обеспечить, нужны ещё нормальные программисты, которые её будут правильно использовать. А на практике, обычно, это не выполняется. Поэтому, иногда, лучше не использовать исключения, которые приводят к болоту, а использовать только коды возврата. При этом пользователь не сможет написать рабочий код без проверки кода возврата, что есть защита от дурака.


Во-первых, болото можно легко развести и при исключениях и при кодах возврата. IMHO, при кодах развести проще, так как их проще игнорить. Твой способ "форсировать" проверки элементарно обходится примерно так:
//  пусть где-то в хедере библиотеки объявлено:
//  void LibFunc( T1 arg1, T2 arg2, T3 arg3, LIB_RESULT& funcRes );
//  Дальше пишем в своём хедере так:
    extern LIB_RESULT libResult;
    void LibFunc( T1 arg1, T2 arg2, T3 arg3, LIB_RESULT& funcRes = libResult );
void


Чем делаем контроль факультативным.
А можно и так:
    inline LIB_RESULT LibFunc( T1 arg1, T2 arg2, T3 arg3 )
    {
        LIB_RESULT funcRes = 0;
        LibFunc( arg1, arg2, arg3, funcRes );
        return funcRes;
    }
чем делаем контроль не только факультативным, но и удобным...

Это конечно всё ССЗБ, но и asserts писать где попало, это тоже ССЗБ...
Так что, IMHO, если уж ставится задача форсировать обработку ошибок, то надо таки исключения бросать...


С другой стороны, если тебе уж кажется, что так важно поддержать твой сценарий, то можно сделать как-то так:
typedef bool (*ShouldThrowIt_t)( const std::exception* e );
extern ShouldThrowIt_t ShouldThrowItCallback; // = 0;

template<typename T>
void throwIfShould( const T& e ) 
{
    if( ShouldThrowItCallback == 0 || ShouldThrowItCallback( &e ) ) {
        throw e;
    }
}
ну и предоставляешь снаружи библиотеки доступ к установке/съёму ShouldThrowItCallback, а в библиотеке вместо throw xxx(x, y, z); пишешь throwIfShould( xxx(x, y, z) );...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: api design: return code or exception - formal criteri
От: Erop Россия  
Дата: 07.09.09 20:55
Оценка: 2 (1) :)
Здравствуйте, byleas, Вы писали:

B>Проще объединить, что и предложили в N2838:

B>
int func(int param, std::error_code& ec = throws())


Не, ну тогда жечь, так жечь...
Можно завести такой классец, который, например, умеет хранить внутри себя исключение.
При этом можно его сделать таким странным, что он может его бросать в случае если передали специально сконструированный экземпляр.
Скажем так:
//  Интерфейс для скармливания исключений принимающей стороне.
class IExceptionsTransmitter {
public:
    struct IExceptionHolder {
        virtual ~IExceptionHolder() {}
        virtual std::exception& Get() = 0; 
        virtual void ThrowIt() = 0;
        virtual IExceptionHolder* Clone() const = 0;
    };
    
    template<typename TException>
    void operator = ( const TException& e ) const
    {
        TransmitException( exceptionHolder<TException>( e ) );
    }

protected:
    virtual void TransmitException( const IExceptionHolder& holder ) const = 0;

private:
    template<typename TException> struct exceptionHolder : IExceptionHolder {
        TException Data;
        virtual TException& Get() { return Data; }
        virtual void ThrowIt() { throw Data; }
        virtual IExceptionHolder* Clone() const { return new IExceptionHolder( *this ); }
    };
};

class ExceptionsTransmitter : public IExceptionsTransmitter {
public:
    mutable std::vector<shared_ptr<IExceptionHolder> > TransmittedExceptions;
protected:
    virtual void TransmitException( const IExceptionHolder& holder ) const 
        { TransmittedExceptions.push_back( holder.Clone() ); }
};

class ExceptionsThrower : public IExceptionsTransmitter {
protected:
    virtual void TransmitException( const IExceptionHolder& holder ) const { holder.ThrowIt(); }
};

inline ExceptionsThrower throws() { return ExceptionsThrower(); }


Ну и теперь можно писать просто и незамысловато:
int func(int param, const IExceptionsTransmitter& et = throws())
{
  if(param == invalid_param_value){
    et = std::invalid_argument("param");
    return EINVAL;
  }
    // ... do the work
}
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[20]: api design: return code or exception - formal criter
От: Юрий Жмеренецкий ICQ 380412032
Дата: 08.09.09 06:09
Оценка:
Здравствуйте, Николай Ивченков, Вы писали:

НИ>Можно привести пример, где предоставляется выбор: operator[] и at.


Это лучше чем один, потенциально кидающий вариант. Все равно, в местах где индекс, передаваемый в at, получен не от клиента (пусть будет api::read_index) и клиент не может убедиться в том, что индекс валиден — такая ситуация будет скорее всего расценена как невозможность выполнения постусловий при выполнененных предусловиях. А это не логическая ошибка. Т.е. при использовании at в этом случае нужно поймать исключение и преобразовать его в std::runtime_error (или наследника). Только намного логичнее сразу проверить этот индекс и возбудить std::runtime_error при необходимости. В этом случае можно сразу использовать operator[].

ЮЖ>>Меня больше интересует поведение клиента в том случае, когда он не смог обеспечить выполнение предусловий. Могу ли я в этом случае (как реализующий функцию) переводить программу в состояние 'сгенерированно исключение'? Клиент не выполнил своего контракта — как я могу ожидать от него готовности к такой ситуации? Если он готов обработать такую ситуацию — почему он сразу не выполнил предусловия?, он ведь заинтересован в результате. Возбудив исключение — я просто замаскирую дефект, логическую ошибку программиста (клиента). Логическую — не потому что logic_error, а потому что это именно ошибка в логике, неспособность понять формулу 'если P то Q'. Такие ошибки надо исправлять (не допускать), а не обрабатывать. Есть еще некоторые проблемы, но они уже являются производными от этой.


НИ>Видимо, это такая своеобразная замена assert-у.


Нельзя рассматривать исключения как замену assert-у. Кроме того, если быть последовательным, тогда нужно таким же образом проверять все предусловия и все постусловия.

НИ>Предполагается, что программист должен отлавливать исключения std::logic_error (и производные от него) и трактовать их как внутренние ошибки в программе.


Для чего? для отлова багов? Единственный возможный вариант при диагностике такой ситуации — вызов abort в месте обнаружения. Только для полного счастья, повторюсь, необходимо проверять все предусловия и все постусловия. А это приведет к катастрофическому снижению производительности. Если же возбуждать logic_error (вместо вызова abort), то nothrow функции с пред и постусловиями волшебным образом получают возможность возбуждать исключения, соответственно их использование намного усложняется.

НИ>Для release-сборки это приемлемо


Не могу с этим согласиться.
Re[21]: api design: return code or exception - formal criter
От: Николай Ивченков  
Дата: 08.09.09 10:13
Оценка:
Юрий Жмеренецкий:

ЮЖ>Нельзя рассматривать исключения как замену assert-у. Кроме того, если быть последовательным, тогда нужно таким же образом проверять все предусловия


Какое этому обоснование?

НИ>>Предполагается, что программист должен отлавливать исключения std::logic_error (и производные от него) и трактовать их как внутренние ошибки в программе.


ЮЖ>Для чего? для отлова багов? Единственный возможный вариант при диагностике такой ситуации — вызов abort в месте обнаружения.


Хорошо, у пользователя программа упала по abort. Разработчику нужно пофиксить багу. Как ты предлагаешь выяснять примерную причину ошибки на основе тех данных, которые среднестатистический пользователь в состоянии предоставить техподдержке?

ЮЖ>Только для полного счастья, повторюсь, необходимо проверять все предусловия и все постусловия. А это приведет к катастрофическому снижению производительности.


Нужно найти разумный компромисс.

ЮЖ>Если же возбуждать logic_error (вместо вызова abort), то nothrow функции с пред и постусловиями волшебным образом получают возможность возбуждать исключения, соответственно их использование намного усложняется.


Если функция может выпускать наружу исключения, то она уже не является nothrow. Конечно, это может усложнить её использование, но зато взамен мы получаем какую-то информацию об ошибке.
Re[4]: api design: return code or exception - formal criteri
От: Erop Россия  
Дата: 08.09.09 11:20
Оценка:
Здравствуйте, Erop, Вы писали:

E>При этом можно его сделать таким странным, что он может его бросать в случае если передали специально сконструированный экземпляр.


Да, кстати, про константную ссылку я погорячился, конечно.
Вполне можно делать и неконстантную. Просто из throws() возвращать ссылку на статический объект и всё...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[22]: api design: return code or exception - formal criter
От: Юрий Жмеренецкий ICQ 380412032
Дата: 08.09.09 12:33
Оценка: :)
Здравствуйте, Николай Ивченков, Вы писали:

НИ>Юрий Жмеренецкий:


ЮЖ>>Нельзя рассматривать исключения как замену assert-у. Кроме того, если быть последовательным, тогда нужно таким же образом проверять все предусловия


НИ>Какое этому обоснование?


Иначе, при таком подходе (очень плохом, имхо), нельзя обнаружить все ошибки (логические).

НИ>>>Предполагается, что программист должен отлавливать исключения std::logic_error (и производные от него) и трактовать их как внутренние ошибки в программе.


ЮЖ>>Для чего? для отлова багов? Единственный возможный вариант при диагностике такой ситуации — вызов abort в месте обнаружения.


НИ>Хорошо, у пользователя программа упала по abort. Разработчику нужно пофиксить багу. Как ты предлагаешь выяснять примерную причину ошибки


Это другой вопрос (решаемый в принципе). Разница между возбуждением исключения и abort в этом случае — нет гарантий, что размотка стека не приведет к инициации других ошибок, нет гарантий что все данные останутся в порядке, нет гарантий что обработчик исключений не содержит багов, и т.д. abort выводит программу из состояния 'баг' с минимальными последствиями. Если же требуется все-таки не падать даже при наличии багов — то все эти проблемы придется все-таки решить.
Но здесь возникает забавное логичское противоречие — если программист может решить эти проблемы не наплодив новых багов — почему он не может их не плодить изначально?

ЮЖ>>Только для полного счастья, повторюсь, необходимо проверять все предусловия и все постусловия. А это приведет к катастрофическому снижению производительности.


НИ>Нужно найти разумный компромисс.


Разумый выход — снижение количества потенциально опасных мест, которые могут привести к остановке (defect -> fault). При большом количестве дефектов (сам по себе дефект не всегда приводит к остановке) появляется проблема с их идентификацией, которую ты озвучил. Вместо строительства подсистемы ловли багов логичнее попытаться снизить общее количество мест их возникновения. Вот здесь как раз можно эксплуатировать ценное свойство пред/пост условий — выводимость из контекста. Не нужно выполнять повторную проверку, там, где ее выполнение выводимо из контекста. И никакой ошибки в этом случае не возникнет. Но для этого требуется две вещи — документация предусловий и следование простому логическому правилу — 'если P то Q'.


ЮЖ>>Если же возбуждать logic_error (вместо вызова abort), то nothrow функции с пред и постусловиями волшебным образом получают возможность возбуждать исключения, соответственно их использование намного усложняется.


НИ>Если функция может выпускать наружу исключения, то она уже не является nothrow.

Я это и имел ввиду.

НИ>Конечно, это может усложнить её использование, но зато взамен мы получаем какую-то информацию об ошибке.


Опять тот же вопрос — т.е. программист обладает достаточной квалификацией, для того чтобы писать код, устойчивый к исключениям в любых местах (+ допускаются неизбежные дополнительные затраты по ресурсам), а проверить предусловия для вызова функции он не в состоянии?

Чуть-чуть усложнили здесь, чуть-чуть усложнили там — увеличивается объем кода, увеличивает число состояний программы — еще больше увеличиваются шансы на ошибку. А ведь это код еще и поддерживать надо... В большинстве случаев — это исскуственное усложнение кода на ровном месте и последующее решение сопутствующих проблем.

PS: Кроме того, всю (а не 'какую-то') необходимую информацию можно получить куда проще — она (вместве с контекстом) доступна перед вызовом.
Re[23]: api design: return code or exception - formal criter
От: Николай Ивченков  
Дата: 08.09.09 15:03
Оценка:
Юрий Жмеренецкий:

ЮЖ>>>Нельзя рассматривать исключения как замену assert-у. Кроме того, если быть последовательным, тогда нужно таким же образом проверять все предусловия


НИ>>Какое этому обоснование?


ЮЖ>Иначе, при таком подходе (очень плохом, имхо), нельзя обнаружить все ошибки (логические).


Очень туманное объяснение.

НИ>>Хорошо, у пользователя программа упала по abort. Разработчику нужно пофиксить багу. Как ты предлагаешь выяснять примерную причину ошибки


ЮЖ>Это другой вопрос (решаемый в принципе).


Этот вопрос напрямую связан с выбором между throw и немедленным abort. Где-то, конечно, можно вести очень подробные логи — тыкать логирование в каждую функцию, — но это изврат. Или под "решаемый в принципе" подразумевается тщательный review всего кода?

ЮЖ>Разница между возбуждением исключения и abort в этом случае — нет гарантий, что размотка стека не приведет к инициации других ошибок


И что?

ЮЖ>нет гарантий что все данные останутся в порядке


А в случае внезапного abort несохранённые данные вообще будут безвозвратно утеряны. Хорошенькое дельце, ничего не скажешь.

ЮЖ>нет гарантий что обработчик исключений не содержит багов


Если так рассуждать, то пользователю вообще не следует запускать программу, т.к. изначально нет никакой гарантии, что она не содержит багов.

ЮЖ>abort выводит программу из состояния 'баг' с минимальными последствиями.


Потеря несохранённых данных — это, по-твоему, минимальные последствия?

ЮЖ>Если же требуется все-таки не падать даже при наличии багов — то все эти проблемы придется все-таки решить.

ЮЖ>Но здесь возникает забавное логичское противоречие — если программист может решить эти проблемы не наплодив новых багов — почему он не может их не плодить изначально?

Код обработчика логической ошибки программы может быть гораздо короче, чем весь код, для которого эта ошибка отлавливается. Меньший объём кода -> меньшая вероятность ошибок.

ЮЖ>>>Только для полного счастья, повторюсь, необходимо проверять все предусловия и все постусловия. А это приведет к катастрофическому снижению производительности.


НИ>>Нужно найти разумный компромисс.


ЮЖ>Разумый выход — снижение количества потенциально опасных мест, которые могут привести к остановке (defect -> fault). При большом количестве дефектов (сам по себе дефект не всегда приводит к остановке) появляется проблема с их идентификацией, которую ты озвучил. Вместо строительства подсистемы ловли багов логичнее попытаться снизить общее количество мест их возникновения. Вот здесь как раз можно эксплуатировать ценное свойство пред/пост условий — выводимость из контекста. Не нужно выполнять повторную проверку, там, где ее выполнение выводимо из контекста. И никакой ошибки в этом случае не возникнет.


Выводимо оно из контекста или нет, решает программист. Решение программиста может быть неправильным.

ЮЖ>Опять тот же вопрос — т.е. программист обладает достаточной квалификацией, для того чтобы писать код, устойчивый к исключениям в любых местах (+ допускаются неизбежные дополнительные затраты по ресурсам), а проверить предусловия для вызова функции он не в состоянии?


Программист может быть уверен (ошибочно), что эти предусловия всегда соблюдаются. Зачем тогда он станет их проверять? А если и проверит, то что будет делать в случае обнаружения несоответствия (в release-сборке)?

ЮЖ>Чуть-чуть усложнили здесь, чуть-чуть усложнили там


Как часто происходит такое усложнение? Отмечу, что генерация исключения не лишает нас возможности произвести тот же abort в любое другое время после обработки ошибки. И даже если в процессе раскрутки стека мы получим какие-нибудь неразрушительные ошибки вроде утечки памяти, это не будет иметь никакого значения, коль скоро программа всё равно будет аварийно завершена.

ЮЖ>PS: Кроме того, всю (а не 'какую-то') необходимую информацию можно получить куда проще — она (вместве с контекстом) доступна перед вызовом.


И куда её девать? Логировать? А как это скажется на производительности? Какой величины логи мы будем получать? И в любом ли коде у нас вообще есть возможность записывать что-то в лог?
Re[24]: api design: return code or exception - formal criter
От: Юрий Жмеренецкий ICQ 380412032
Дата: 08.09.09 17:35
Оценка: 4 (1)
Здравствуйте, Николай Ивченков, Вы писали:

НИ>Юрий Жмеренецкий:


ЮЖ>>>>Нельзя рассматривать исключения как замену assert-у. Кроме того, если быть последовательным, тогда нужно таким же образом проверять все предусловия


НИ>>>Какое этому обоснование?


ЮЖ>>Иначе, при таком подходе (очень плохом, имхо), нельзя обнаружить все ошибки (логические).


НИ>Очень туманное объяснение.


Тогда объясни — _зачем_ нужно принудительно проверять предусловия и каким-то образом (отличным от assert) реагировать на их нарушение? Для решения каких проблем это используется?

НИ>>>Хорошо, у пользователя программа упала по abort. Разработчику нужно пофиксить багу. Как ты предлагаешь выяснять примерную причину ошибки


ЮЖ>>Это другой вопрос (решаемый в принципе).


НИ>Этот вопрос напрямую связан с выбором между throw и немедленным abort. Где-то, конечно, можно вести очень подробные логи — тыкать логирование в каждую функцию, — но это изврат.


Повторю — нужно снижать количество мест, содержащих дефекты — а не плодить их.

НИ>Или под "решаемый в принципе" подразумевается тщательный review всего кода?


Подразумевается только то что написано.

Я тебе говорю про другое — предусловия нужно проверять с помощью assert'ов, а не throw/abort. Проблем, возникающих при использовании abort, меньше чем при использовании throw. Только в большинстве случаев и это не является оправданием для использования этого метода.

ЮЖ>>Разница между возбуждением исключения и abort в этом случае — нет гарантий, что размотка стека не приведет к инициации других ошибок


НИ>И что?


См. ниже.

ЮЖ>>нет гарантий что все данные останутся в порядке


НИ>А в случае внезапного abort несохранённые данные вообще будут безвозвратно утеряны. Хорошенькое дельце, ничего не скажешь.


И сохранить их получится со 100% гарантией, ага. В валидном состоянии. Так? Только проблема в том что, как минимум нет гарантии, что на момент обнаружения логической ошибки в системе не произошло фатальных изменений (мало ли почему не выполнены предусловия). Банально может произойти сохранение мусора (хорошо если не перезапись оригинала).

ЮЖ>>нет гарантий что обработчик исключений не содержит багов


НИ>Если так рассуждать, то пользователю вообще не следует запускать программу, т.к. изначально нет никакой гарантии, что она не содержит багов.


Верно. Для той категории багов о которой мы говорим (логических ошибок). Ведь изначально вместе с 'throw logic_error' явно постулируется факт наличия дефектов (нарушение предусловий).

ЮЖ>>abort выводит программу из состояния 'баг' с минимальными последствиями.


НИ>Потеря несохранённых данных — это, по-твоему, минимальные последствия?

Ответил выше.


НИ> Меньший объём кода -> меньшая вероятность ошибок.

С этим полностью согласен.

ЮЖ>>>>Только для полного счастья, повторюсь, необходимо проверять все предусловия и все постусловия. А это приведет к катастрофическому снижению производительности.


НИ>>>Нужно найти разумный компромисс.


ЮЖ>>Разумый выход — снижение количества потенциально опасных мест, которые могут привести к остановке (defect -> fault). При большом количестве дефектов (сам по себе дефект не всегда приводит к остановке) появляется проблема с их идентификацией, которую ты озвучил. Вместо строительства подсистемы ловли багов логичнее попытаться снизить общее количество мест их возникновения. Вот здесь как раз можно эксплуатировать ценное свойство пред/пост условий — выводимость из контекста. Не нужно выполнять повторную проверку, там, где ее выполнение выводимо из контекста. И никакой ошибки в этом случае не возникнет.


НИ>Выводимо оно из контекста или нет, решает программист. Решение программиста может быть неправильным.


Опять 25. Неправильным оно может быть только в одном случае — если он ранее нарушил предусловие. Если программист не хочет ошибаться — пусть зовет на помощь компилятор — во многих случаях он может помочь.

ЮЖ>>Опять тот же вопрос — т.е. программист обладает достаточной квалификацией, для того чтобы писать код, устойчивый к исключениям в любых местах (+ допускаются неизбежные дополнительные затраты по ресурсам), а проверить предусловия для вызова функции он не в состоянии?


НИ>Программист может быть уверен (ошибочно), что эти предусловия всегда соблюдаются.

Он должен быть уверен что они соблюдаются. И сам в свою очередь должен их соблюдать.

НИ>Зачем тогда он станет их проверять? А если и проверит, то что будет делать в случае обнаружения несоответствия (в release-сборке)?

+1. Их не надо проверять, они уже проверены и выполняются по определению. Поэтому второй вопрос отпадает, logic_error как и abort становится ненужным.


ЮЖ>>Чуть-чуть усложнили здесь, чуть-чуть усложнили там


НИ>Как часто происходит такое усложнение?


Везде при подобном коде:

/// \pre x > 10, p != NULL
void f(int x, int* p)
{
  assert(x > 10);
  assert(p);

  if(x > 10)
   throw std::invalid_argument("f::x"); // В клинических случаях добавляется запись в лог, etc.
                                        // Тип исключения в принципе не так важен, но это по  
                                        // сути логическая ошибка (std::logic_error).
                                        // Иногда можно увидеть std::runtime_error - но это уже похоже на саботаж

  if(!p)
   throw std::invalid_argument("f::p");

  //...
}



НИ>Отмечу, что генерация исключения не лишает нас возможности произвести тот же abort в любое другое время после обработки ошибки. И даже если в процессе раскрутки стека мы получим какие-нибудь неразрушительные ошибки вроде утечки памяти, это не будет иметь никакого значения, коль скоро программа всё равно будет аварийно завершена.


И throw и abort не защитят от порчи данных до обнаружения. abort хотя бы защитит от возможных деструктивных изменений в данных во время раскрутки, + защитит от возможной 'обработки' — catch(const std::logic_error&), правда в этом случае бить по пальцам нужно стальной и заточенной линейкой.

ЮЖ>>PS: Кроме того, всю (а не 'какую-то') необходимую информацию можно получить куда проще — она (вместве с контекстом) доступна перед вызовом.


НИ>И куда её девать? Логировать?


Незнаю, она тебе была нужна =) Единственное что необходимо — на ее основе надо сделать выводы о возможности дальнейшего выполнения. Т.е. это самый 'верх' — то место где проверяются предусловия.

/// \pre arg > 0
void f(int arg);

/// \pre arg none
void my_main(int arg)
{
  // Я хочу вызвать функцию 'f'...

  if(arg <= 0) // <- Несоответстиве(*) предусловий для my_main#arg и f#arg - сигнал о необходимости проверки.
  {
    // Вот тут доступен весь контекст. Нужно в лог - пишем в лог...
    // + Не нужно протаскивать никакие данные через исключения.
    log_error(arg);
    return;
  }

  // Я обеспечил выполнение предусловий - выполнил свою сторону контракта.
  f(arg); 
}

// * Буквальное несоответстиве не всегда приводит к проверке
Re[7]: api design: return code or exception - formal criteri
От: Vain Россия google.ru
Дата: 08.09.09 21:08
Оценка: -1
Здравствуйте, Erop, Вы писали:

V>>Одна библиотека не сможет этого обеспечить, нужны ещё нормальные программисты, которые её будут правильно использовать. А на практике, обычно, это не выполняется. Поэтому, иногда, лучше не использовать исключения, которые приводят к болоту, а использовать только коды возврата. При этом пользователь не сможет написать рабочий код без проверки кода возврата, что есть защита от дурака.

E>Во-первых, болото можно легко развести и при исключениях и при кодах возврата. IMHO, при кодах развести проще, так как их проще игнорить. Твой способ "форсировать" проверки элементарно обходится примерно так:
Это не аргумент, так можно сломать любую функцию, коды воврата тут вообще ни причём.
E>Это конечно всё ССЗБ, но и asserts писать где попало, это тоже ССЗБ...
Я уже объяснил для чего ассерт..
E>Так что, IMHO, если уж ставится задача форсировать обработку ошибок, то надо таки исключения бросать...
Фраза "форсировать обработку ошибок" уже попахивает говнокодом, вам не кажется?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[5]: api design: return code or exception - formal criteri
От: Vain Россия google.ru
Дата: 08.09.09 21:24
Оценка: :)
Здравствуйте, crable, Вы писали:

V>>>>К чему это всё скажите вы? А к тому что иногда поддержка исключений библиотекой приводит к образованию некоторого пользовательского болота, организованного недобросовестными программистами использующими библиотеку, из которого вам впоследствии будет трудно выбраться.

V>>>>Иногда лучше вообще не пользоваться исключениями и "заставлять" пользователя обрабатывать код возврата.
C>>>А мне показалось, что лучше не ставить ассерты перед throw и недобросовестеыми тут выглядят как раз программисты библиотеки, сделавшие это.
V>>Тогда придётся перезапускать приложение с включёнными галками ловить исключения, что тоже не есть хорошо.
C>Во первых, каким образом использование кодов возврата вместо исключений поможет решить проблему с ассертами?
Оно и не должно. Оно решает проблему с конкретными функциями, где кидание исключений не приемлема.
C>Во-вторых, в чём же всё-таки были виноваты пользователи билиотеки?
В написании говнокода и непотребщины, которое пришлось переписывать естественно.
C>И в третьих, ты сам описал ситуацию, при которой эти ассерты только мешают,
Они не мешали, они стали мешать из-за бросания исключений оттуда, откуда можно было вернуть код возврата.
C>более того, я, например, склонен считать, что так будет в большинстве случаев. Добалять ассерты только потому, что кому-то трудно раз в сто лет заставить студию перехватывать исключения выглядит, по меньшей мере нелепо.
Во первых, в достаточно сложном приложении, включать по-умолчанию ловлю ассертов вообще не преемлемо, т.к. ассерты начинают летать в приложении с самого его запуска и вы постоянно будете на них натыкаться в отладчике.

Во-вторых, когда приложение "грохается" на исключении, чтобы воспроизвести падение, надо как минимум привести приложение в то состояние, в котором оно было перед самым падением, а это иногда оччень не просто, из-за количества действий которое нужно сделать и раннее включение ловли исключений этому будет только мешать.

Поэтому, ассерт вставляют перед исключением, чтобы "поймать падение" с первого раза и не мучится с перезапуском приложения и приведением его в состояние перед падением.

C>Кстати, для того, чтобы поставить эту галку вовсе необязятельно перезапускать приложение.

А исключение уже проскочило и не было поймано, поэтому придётся.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[6]: api design: return code or exception - formal criteri
От: Юрий Жмеренецкий ICQ 380412032
Дата: 08.09.09 22:10
Оценка:
Здравствуйте, Vain, Вы писали:

[...]

V>Во первых, в достаточно сложном приложении, включать по-умолчанию ловлю ассертов вообще не преемлемо, т.к. ассерты начинают летать в приложении с самого его запуска и вы постоянно будете на них натыкаться в отладчике.

-1


V>Поэтому, ассерт вставляют перед исключением, чтобы "поймать падение" с первого раза и не мучится с перезапуском приложения и приведением его в состояние перед падением.


Ммм... breakpoint в конструкторе исключения?
Re[25]: api design: return code or exception - formal criter
От: Николай Ивченков  
Дата: 08.09.09 23:43
Оценка: 5 (1) +1
Юрий Жмеренецкий:

ЮЖ>Тогда объясни — _зачем_ нужно принудительно проверять предусловия и каким-то образом (отличным от assert) реагировать на их нарушение?


Вставлять проверку предусловия перед каждым вызовом функции неудобно — так весь код ими будет пестрить. assert не годится для проверки в release-версии. Там, где принудительная проверка даёт заметный overhead, нужно предоставлять версию функции без проверки — разрешать компромисс между производительностью и обладанием контроля над ошибками — это дело пользователя функции.

ЮЖ>Я тебе говорю про другое — предусловия нужно проверять с помощью assert'ов, а не throw/abort.


Ещё раз: assert-ы не работают в релизе. А некоторые программы имеют неприятное свойство глючить после стадии тестирования — у конечного пользователя.

НИ>>А в случае внезапного abort несохранённые данные вообще будут безвозвратно утеряны. Хорошенькое дельце, ничего не скажешь.


ЮЖ>И сохранить их получится со 100% гарантией, ага. В валидном состоянии. Так?


Нет, но вероятность сохранить хоть что-то есть.

ЮЖ>Только проблема в том что, как минимум нет гарантии, что на момент обнаружения логической ошибки в системе не произошло фатальных изменений (мало ли почему не выполнены предусловия).


Такой гарантии вообще никогда нет.

ЮЖ>Банально может произойти сохранение мусора


Этот вариант не исключён, но иначе у пользователя вообще нет шансов восстановить данные. Я категорически не согласен с тем, что пользователю надо отказывать в такой возможности — даже в виду незначительной вероятности порчи уже сохранённой информации (сохранение желательно вести во временный файл). К тому же в случае многопоточной программы внезапная смерть всех потоков также может быть весьма разрушительной.

ЮЖ>>>Разумый выход — снижение количества потенциально опасных мест, которые могут привести к остановке (defect -> fault). При большом количестве дефектов (сам по себе дефект не всегда приводит к остановке) появляется проблема с их идентификацией, которую ты озвучил. Вместо строительства подсистемы ловли багов логичнее попытаться снизить общее количество мест их возникновения. Вот здесь как раз можно эксплуатировать ценное свойство пред/пост условий — выводимость из контекста. Не нужно выполнять повторную проверку, там, где ее выполнение выводимо из контекста. И никакой ошибки в этом случае не возникнет.


НИ>>Выводимо оно из контекста или нет, решает программист. Решение программиста может быть неправильным.


ЮЖ>Опять 25. Неправильным оно может быть только в одном случае — если он ранее нарушил предусловие.


Он мог неверно оценить постусловия предыдущих вычислений.

ЮЖ>Если программист не хочет ошибаться — пусть зовет на помощь компилятор — во многих случаях он может помочь.


В некоторых — не может. О них-то тут и идёт речь.

НИ>>Программист может быть уверен (ошибочно), что эти предусловия всегда соблюдаются.

ЮЖ>Он должен быть уверен что они соблюдаются. И сам в свою очередь должен их соблюдать.

Я не понял, мы о простых смертных программистах говорим или о роботах, которые никогда не ошибаются?

НИ>>Зачем тогда он станет их проверять? А если и проверит, то что будет делать в случае обнаружения несоответствия (в release-сборке)?

ЮЖ>+1. Их не надо проверять, они уже проверены и выполняются по определению.

Кем проверены? Тебя послушать, так и assert-ы не нужны: идеальный программист всюду идеально подгонит постусловия предыдущих вычислений под предусловия последующих, и все будут жить долго и счастливо. Прямо утопия какая-то получается.

ЮЖ>>>Чуть-чуть усложнили здесь, чуть-чуть усложнили там


НИ>>Как часто происходит такое усложнение?


ЮЖ>Везде при подобном коде:


ЮЖ>
/// \pre x > 10, p != NULL
ЮЖ>void f(int x, int* p)
ЮЖ>{
ЮЖ>  assert(x > 10);
ЮЖ>  assert(p);

ЮЖ>  if(x > 10)
ЮЖ>   throw std::invalid_argument("f::x"); // В клинических случаях добавляется запись в лог, etc.
ЮЖ>                                        // Тип исключения в принципе не так важен, но это по  
ЮЖ>                                        // сути логическая ошибка (std::logic_error).
ЮЖ>                                        // Иногда можно увидеть std::runtime_error - но это уже похоже на саботаж

ЮЖ>  if(!p)
ЮЖ>   throw std::invalid_argument("f::p");

ЮЖ>  //...
ЮЖ>}

Тут-то как раз всё очень просто разрешается.

namespace Impl
{
    void throw_precondition_error(
        char const *string_literal_condition,
        char const *string_literal_file,
        long line);
}

#define CHECK_PRECONDITION(expr) \
    (assert(expr), (expr) ? void() : Impl::throw_precondition_error(#expr, __FILE__, __LINE__))
    
class PreconditionError : public std::exception
{
public:
    virtual char const *what() const throw()
        { return "Precondition error detected"; }
    char const *condition() const
        { return m_condition; }
    char const *file() const
        { return m_file; }
    long line() const
        { return m_line; }
    ~PreconditionError() throw() {}
private:
    PreconditionError(char const *condition, char const *file, long line) :
        m_condition(condition),
        m_file(file),
        m_line(line)
    {}

    char const *m_condition;
    char const *m_file;
    long m_line;
    
    friend void Impl::throw_precondition_error(
        char const *string_literal_condition,
        char const *string_literal_file,
        long line);
};

namespace Impl
{
    void throw_precondition_error(
        char const *string_literal_condition,
        char const *string_literal_file,
        long line)
    {
        throw PreconditionError(string_literal_condition, string_literal_file, line);
    }
}

/// \pre x > 10, p != NULL
void f(int x, int* p)
{
  CHECK_PRECONDITION(x > 10);
  CHECK_PRECONDITION(p);
  //...
}

Я пока вижу сложность только в использовании подобных функций внутри nothrow функций.

НИ>>Отмечу, что генерация исключения не лишает нас возможности произвести тот же abort в любое другое время после обработки ошибки. И даже если в процессе раскрутки стека мы получим какие-нибудь неразрушительные ошибки вроде утечки памяти, это не будет иметь никакого значения, коль скоро программа всё равно будет аварийно завершена.


ЮЖ>abort хотя бы защитит от возможных деструктивных изменений в данных во время раскрутки


В многопоточной программе действие, лишь наполовину выполненное другим потоком, также может быть деструктивным.

ЮЖ>>>PS: Кроме того, всю (а не 'какую-то') необходимую информацию можно получить куда проще — она (вместве с контекстом) доступна перед вызовом.


НИ>>И куда её девать? Логировать?


ЮЖ>Незнаю, она тебе была нужна =)


Мне-то она нужна именно в логе, но запись в него должна вестись по мере возникновения ошибки, а не на всякий случай при каждом вхождении в какую-то функцию. Чтобы узнать, кто позвал функцию, где обнаружился сбой, нужно контролировать либо вхождения в функции, либо выходы из них по исключению. В случае с немедленным вызовом abort второй способ отбрасывается. Теперь возникает вопрос: как контролировать вхождения? Вместо жирных логов можно использовать TLS-хранилище для запоминания стека вызовов, но это всё равно может быть накладно с точки зрения производительности, и данное хранилище придётся использовать на всех уровнях.

ЮЖ> // Вот тут доступен весь контекст. Нужно в лог — пишем в лог...

ЮЖ> // + Не нужно протаскивать никакие данные через исключения.

Если функция может быть вызвана из нескольких мест в программе, то о доступности всего контекста можно говорить лишь при наличии сведений о стеке вызовов. Сама по себе функция понятия не имеет о том, кто её вызывает, а при поиске бага эта информация важна.
Re[6]: api design: return code or exception - formal criteri
От: crable США  
Дата: 09.09.09 04:11
Оценка:
Здравствуйте, Vain, Вы писали:

V>Здравствуйте, crable, Вы писали:


[snip]

C>>Во первых, каким образом использование кодов возврата вместо исключений поможет решить проблему с ассертами?

V>Оно и не должно. Оно решает проблему с конкретными функциями, где кидание исключений не приемлема.

Может наконец напишешь, что же это за функции и ситуации, в которых кидание исключений неприемлимо? В твоём первоначальном сообщении ничего подобного не видно.

C>>Во-вторых, в чём же всё-таки были виноваты пользователи билиотеки?

V>В написании говнокода и непотребщины, которое пришлось переписывать естественно.

отсюда
Автор: Vain
Дата: 04.09.09
это никак не следует.

C>>И в третьих, ты сам описал ситуацию, при которой эти ассерты только мешают,

V>Они не мешали, они стали мешать из-за бросания исключений оттуда, откуда можно было вернуть код возврата.

Можешь пояснить? Почему они стали мешать из-за бросания исключений и не мешали бы при использовании кодов возврата? Небольшой пример тоже не помешал бы.

C>>более того, я, например, склонен считать, что так будет в большинстве случаев. Добалять ассерты только потому, что кому-то трудно раз в сто лет заставить студию перехватывать исключения выглядит, по меньшей мере нелепо.

V>Во первых, в достаточно сложном приложении, включать по-умолчанию ловлю ассертов вообще не преемлемо, т.к. ассерты начинают летать в приложении с самого его запуска и вы постоянно будете на них натыкаться в отладчике.

Может быть не "ассертов", а "исключений"?

V>Во-вторых, когда приложение "грохается" на исключении, чтобы воспроизвести падение, надо как минимум привести приложение в то состояние, в котором оно было перед самым падением, а это иногда оччень не просто, из-за количества действий которое нужно сделать и раннее включение ловли исключений этому будет только мешать.


Что, в данном контесте, значит "грохается"? Выбрасывает исключение, которое нигде не обрабатывается?

V>Поэтому, ассерт вставляют перед исключением, чтобы "поймать падение" с первого раза и не мучится с перезапуском приложения и приведением его в состояние перед падением.


C>>Кстати, для того, чтобы поставить эту галку вовсе необязятельно перезапускать приложение.

V>А исключение уже проскочило и не было поймано, поэтому придётся.
The last good thing written in C was Franz Schubert's Symphony No. 9.
Re[23]: api design: return code or exception - formal criter
От: Erop Россия  
Дата: 09.09.09 04:53
Оценка: :)
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Иначе, при таком подходе (очень плохом, имхо), нельзя обнаружить все ошибки (логические).


Вообще-то нормальные программы ещё что-то и делают, так что ошибки в них можно найти и по результатам их деятельности

ЮЖ>Это другой вопрос (решаемый в принципе). Разница между возбуждением исключения и abort в этом случае — нет гарантий, что размотка стека не приведет к инициации других ошибок, нет гарантий что все данные останутся в порядке, нет гарантий что обработчик исключений не содержит багов, и т.д. abort выводит программу из состояния 'баг' с минимальными последствиями. Если же требуется все-таки не падать даже при наличии багов — то все эти проблемы придется все-таки решить.


Не правда ваша.
1) обычный assert приводит к тому, что программа делает красиво ручкой, а хотелось бы ещё и документик сохранить, например
2) assert, который провалился у пользователя, ситуация неприятная, но, увы, возможная. Дальше всё упирается в простой вопрос кто за что готов платить Если речь идёт о простом таком пользователе-человеке, то ему хорошо бы получить как-то просто после этого инструкции как решить его проблему. А разработчику хорошо бы как-то получить описание возникшей у пользователя ситуации, приведшей к проявлению ошибки...
3) Собственно между "ошибок много" и "ошибок мало" разница более или менее количественная. Грубо говоря понижение вероятности встретить ошибку в программе стоит денег на разработку и тестирование.
4) Я не знаю ситуаций, когда обработка ошибки совсем не важна. Если уж прога упала, то кто-то что-то должен написать заранее такое, чтобы жизнь продолжилась. Если прога для пользователя, то должно появиться окошко, из которого удобно обратиться в техподдержку, сразу же и инфу об ошибке собрав, если надо, и получив инструкции, если ошибка уже известная...
А если это СУ космического аппарата, то надо что-то по assert тоже что-то делать. Таки чтобы аппарат в таком случае не потерялся, реактор не взорвался и т. д...

ЮЖ>Но здесь возникает забавное логичское противоречие — если программист может решить эти проблемы не наплодив новых багов — почему он не может их не плодить изначально?


Потому, что при разработке системы обработки ошибок, её обычно проектируют и тестируют. ЧТо делает задачу более структурированной, что упрощает жизнь разработчика

ЮЖ>Но для этого требуется две вещи — документация предусловий и следование простому логическому правилу — 'если P то Q'.


Это плохо верифицируемо. Так что это повышает цену разработки и некисло...
Кроме того, во многих случаях пред/пост-условие трудно не то, что бы проверить, но даже хотя бы и выписать формально...

IMHO, разумный компромисс состоит в том, чтобы иметь какую-нибудь вменяемую систему обработки багов программы и ошибок пользователя, и при этом всюду тотально проверять то из пост/пред-условий, что можно проверить легко и быстро.
Самые тормозные проверки можно оставлять только в _DEBUG версии, например...

ЮЖ>Опять тот же вопрос — т.е. программист обладает достаточной квалификацией, для того чтобы писать код, устойчивый к исключениям в любых местах (+ допускаются неизбежные дополнительные затраты по ресурсам), а проверить предусловия для вызова функции он не в состоянии?


Если assert кидается исключениями, и других исключений мы в каком-то коде не ждём, то можно снизить сильно требования к коду. Можно требовать чтобы он корректно разрушал свои данные и всё. НИ транзакционности можно не требовать, ни даже отсутствие утечек ресурсов, если только такие утечки не приводят к каким-то фатальным последствиям.
Всё-таки провал assert'а -- это катастрофа
Просто можно иметь цель сделать так, чтобы катастрофа проходила как в современном авто -- без большого риска потери данных, работы, чего-то ещё, а не как в "копейке", когда всё гарантированно накрывается и все гибнут. И можно "загружать" всё заново...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[25]: api design: return code or exception - formal criter
От: Erop Россия  
Дата: 09.09.09 04:59
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Тогда объясни — _зачем_ нужно принудительно проверять предусловия и каким-то образом (отличным от assert) реагировать на их нарушение? Для решения каких проблем это используется?


Затем, что есть рефакторинг...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[25]: api design: return code or exception - formal criter
От: minorlogic Украина  
Дата: 09.09.09 05:32
Оценка: +1
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Везде при подобном коде:


ЮЖ>
/// \pre x > 10, p != NULL
ЮЖ>void f(int x, int* p)
ЮЖ>{
ЮЖ>  assert(x > 10);
ЮЖ>  assert(p);

ЮЖ>  if(x > 10)
ЮЖ>   throw std::invalid_argument("f::x"); // В клинических случаях добавляется запись в лог, etc.
ЮЖ>                                        // Тип исключения в принципе не так важен, но это по  
ЮЖ>                                        // сути логическая ошибка (std::logic_error).
ЮЖ>                                        // Иногда можно увидеть std::runtime_error - но это уже похоже на саботаж

ЮЖ>  if(!p)
ЮЖ>   throw std::invalid_argument("f::p");

ЮЖ>  //...
ЮЖ>}


Кажется я начинаю понимать, у вас другая терминология. Вы пытаетесь проверить DBC входных и т.п. аргументов. Для этих целей лучше написать нормальный обработчик типа CHECK_DC(...); CHECK_PRE_DC(...); CHECK_POST_DC(...); и так далее. Такие обработчики проверяют правильность выполнения программы и вполне резонно могут кидать исключения или настраивать свое поведение в релизной и отладочной сборках. С помощью подобных макросов (или функций) вы сможете легко, без оверхеда обрабатывать входные параметры, определенные состояния объектов и т.п.

Культура же програмирования на С++ подразумевает использования макроса ASSERT (assert) для проверки корректности програмым. Т.е. это проверка которая должна срабатывать ВСЕГДА.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Ищу работу, 3D, SLAM, computer graphics/vision.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.