В модульных (компонентных) расширяемых системах отключать ASSERT-ы в «релизе» нельзя. Это связано с тем, что для Вас-то, быть может эта система «релизная», а вот для клиента, который пишет для неё (и отлаживает) свои компоненты, она никакая не «релизная».
Хорошо известно, что определение ошибки тем более затруднено и дороже обходится, чем позже она обнаруживается, т.е. чем дальше разнесены источник и его эффекты. Это позволяет сформулировать правило:
Позволяйте ошибкам заявлять о себе как можно раньше.
В компонентно-ориентированных системах дефекты всегда должны содержаться в их компонентах
и не распространяться на другие компоненты. Другие компоненты даже могут быть черными ящиками и не иметь исходных текстов, что делает отладку на уровне исходных текстов невозможной. Более того, поток передачи управления в больших объектно-ориентированных программных системах настолько запутанный, что нереально, и это пустая трата времени, проследить его за пределами границ компонентов для целей отладки.
Единственный жизнеспособный отладочный подход есть проектирование всего, от языка программирования до библиотек, до компонентов и приложений, используя оборонительный стиль программирования. В частности, входные точки в компоненты (вызовы процедур/методов) должны останавливать исполнение, если их предусловия не обеспечены:
Никогда не позволяйте ошибкам проникать сквозь границы компонентов.
К счастью, большинство проверок предусловий недорого и поэтому их отключение при исполнении не имеет смысла. Это важно, поскольку в компонентно-ориентированной системе проверки периода исполнения не могут быть выключены в конечной (готовой) системе, поэтому разрабатываемая и готовая системы не различаются. На практике большинство компонентов уже отлажено в режиме черного ящика ("продукция"), а другие отлаживаются в режиме белого ящика. Готовые компоненты должны взаимодействовать, чтобы доказать приверженность сформулированному выше правилу, которое означает «никогда не отключать проверки периода исполнения».
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Почему нельзя отключать ASSERT-ы в релизе
СГ>В модульных (компонентных) расширяемых системах отключать ASSERT-ы в «релизе» нельзя. Это связано с тем, что для Вас-то, быть может эта система «релизная», а вот для клиента, который пишет для неё (и отлаживает) свои компоненты, она никакая не «релизная».
Я склонен думать, что здесь присутствует терминологическая путаница. ASSERT по определению должен быть отключен в релизе. Иначе он такровым не является — так уж исторически сложилось и менять этот распорядок нехорошо. Если нечто, подобное ассерту должно присутствовать в релизе, то и называться это должно по-другому. Например, VERIFY. И это будет являться штатным механизмом сообщения об ошибках. Весь смысл и заключается в том, что это уже другой механизм, отличный от ASSERT. Тем не менее, встречаются люди, которые даже не могут себе представить, что на свете существуют задачи, в которых наличие ASSERT в релизе категорически недопустмо.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Честно говоря, релизный код не должен содержать ошибок. Тем более ошибки которые может интерпретировать только программист. Хотя такие ненормальные сообщения постоянно появляются, в действительности, для нормального продукта должна выводиться ошибка которая может быть корректно интерпретирована пользователем. Для остального есть лог, либо добавочное сообщение, что-то типа подробности. Система ошибок должна быть юзабельна для пользователя, будь то программист на этапе разработки, или бухгалтер на этапе работы.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Почему нельзя отключать ASSERT-ы в релизе
Догма сомнительная, один термин "компонента" о чем говорит .
Как пример , использую я гдето двусвязанный список объектов ( интрузивный ), и при разработке использую такую функцию как ValidateList, ValidateObject и т.д. которые проверяют список на инварианты.
Например первая проходит по всему списку , проверяет двусторониие ссылки , голову и хвост. Вторая ищет элемент , убедиться что он там есть и конечно вызывает ValidateList и т.д.
McSeem2 wrote: > > Я склонен думать, что здесь присутствует терминологическая путаница. > ASSERT по определению должен быть отключен в релизе. Иначе он такровым > не является — так уж исторически сложилось и менять этот распорядок > нехорошо. Если нечто, подобное ассерту должно присутствовать в релизе, > то и называться это должно по-другому. Например, VERIFY. И это будет > являться штатным механизмом сообщения об ошибках. Весь смысл и
А чем этот VERIFY отличается от ASSERT'а, кроме названия?
> заключается в том, что это уже другой механизм, отличный от ASSERT. Тем > не менее, встречаются люди, которые даже не могут себе представить, что > на свете существуют задачи, в которых наличие ASSERT в релизе > категорически недопустмо.
И что это за задачи такие?
И кстати, какое ожидается поведение от программы в таких задачах в том
месте, где должен бы сработать ASSERT, но его нету?
Здравствуйте, McSeem2, Вы писали:
MS>Здравствуйте, Сергей Губанов, Вы писали:
СГ>>Почему нельзя отключать ASSERT-ы в релизе
СГ>>В модульных (компонентных) расширяемых системах отключать ASSERT-ы в «релизе» нельзя. Это связано с тем, что для Вас-то, быть может эта система «релизная», а вот для клиента, который пишет для неё (и отлаживает) свои компоненты, она никакая не «релизная».
MS>Я склонен думать, что здесь присутствует терминологическая путаница. ASSERT по определению должен быть отключен в релизе. Иначе он такровым не является — так уж исторически сложилось и менять этот распорядок нехорошо. Если нечто, подобное ассерту должно присутствовать в релизе, то и называться это должно по-другому. Например, VERIFY. И это будет являться штатным механизмом сообщения об ошибках. Весь смысл и заключается в том, что это уже другой механизм, отличный от ASSERT. Тем не менее, встречаются люди, которые даже не могут себе представить, что на свете существуют задачи, в которых наличие ASSERT в релизе категорически недопустмо.
Сергей говорит о расширяемых (extensible) системах и о применении в них принципа контрактного программирования (Мейер).
В таких системах заранее неизвестно, кто и как будет вызывать наш код. Поэтому отключать проверку предусловий и правда не следует.
Тот ASSERT, о котором говорит Сергей, не приводит к завершению работу программной системы, а только прерывает выполнение текущей команды. Т.е. это разновидность исключения, обрабатываемая системой, "ловушка" (trap).
Вы же говорите, как мне кажется, о другом типе систем.
Поэтому, скорее всего, противоречия нет.
Но у меня возник вопрос: а почему "наличие ASSERT категорически недопустимо"?
Если все предусловия верны, то ASSERT никак себя не проявит. А если где-то предусловие не выполняется, то, не исключено, что генерация исключения — меньшее из зол. (Я не утверждаю, что всегда.)
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
AVC,
> Но у меня возник вопрос: а почему "наличие ASSERT категорически недопустимо"?
При достаточно глубоких проверках, ASSERT's могут быть очень дороги. Например, это так в отладочной версии STLport, проверяющей валидность и принадлежность итераторов одному контейнеру и т.п.
Posted via RSDN NNTP Server 2.0
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Почему нельзя отключать ASSERT-ы в релизе
СГ>В модульных (компонентных) расширяемых системах отключать ASSERT-ы в «релизе» нельзя. Это связано с тем, что для Вас-то, быть может эта система «релизная», а вот для клиента, который пишет для неё (и отлаживает) свои компоненты, она никакая не «релизная».
Я, например, не линюсь писать как-то так:
internal void Fill(TDataSet dataSet) {
// Объявлена как internal, то есть я могу и должен проконтролировать все вызовы
// Во время отладки не "замылится", заявит о себе так, что внимания не обратить невозможно…
Debug.Assert(dataSet != null, "dataSet != null");
// Но если не проконтролировал - чтож, и такое случается :shuffle:
// … а в релизе сообщит об ошибке и программа продолжит работать.if(dataSet == null) {
throw new ArgumentNullException("dataSet"); // Если недосмотрел, то вместо NullReferenceException получу нечно более осмысленное.
}//if
// …
}
Подсмотрел такую практику в каком-то С++ коде, который мне очень понравился и перенял. Да, нелегко приучить себя писать двойные проверки, но зато оправдывает себя.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Павел Кузнецов, Вы писали:
>> Но у меня возник вопрос: а почему "наличие ASSERT категорически недопустимо"?
ПК>При достаточно глубоких проверках, ASSERT's могут быть очень дороги. Например, это так в отладочной версии STLport, проверяющей валидность и принадлежность итераторов одному контейнеру и т.п.
Т.е. первый (и самый очевидный) ответ — по причине больших накладных расходов.
Понятно, что в релизной версии предпочитают не сипользовать отладочную версию STLport.
Но, ИМХО, это не означает категорической недопустимости ASSERT в релизе, а является некоторым (разумным) компромиссом между надежностью и эффективностью.
(Предложение реализовать итераторы (STL) таким образом, чтобы необходимые проверки осуществлялись эффективно, я опускаю. Наверное, это выше человеческих сил. )
Но отдельное (stand-alone) приложение с использованием STL мы можем "вылизать", выловив (почти) все ошибки.
А что делать с программным компонентом, который будет использоваться неизвестно кем и неизвестно как?
Например, зловредными индусами, которыми нас тут недавно стращали.
Здесь мы не можем надеяться, что исключили ошибки в использовании компонента. Ведь большинство этих ошибок еще даже не были сделаны, когда мы завершили отладку.
Разумно ли в такой ситуации изымать из кода проверку предусловий?
ИМХО, неразумно.
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
про это еще Страуструп говорил
примененять защитные проверки в debug конфигурации и отключать их в release — это всё равно что плавать на корабле с полным набором спасательных кругов и шлюпок возле берега, и сгружать их на берег перех выходом в открытое море.
Здравствуйте, AVC, Вы писали:
AVC>Например, зловредными индусами, которыми нас тут недавно стращали. AVC>Здесь мы не можем надеяться, что исключили ошибки в использовании компонента. Ведь большинство этих ошибок еще даже не были сделаны, когда мы завершили отладку. AVC>Разумно ли в такой ситуации изымать из кода проверку предусловий?
Почему-то постоянно кто-то пытается использовать ASSERTs для того, для чего они не предназначены.
ASSERT проверяет условие, которое истинно всегда. Если вы уже знаете, что вы не контролируете входные данные и они могут быть недействительными, то ASSERT, очевидно, для этого не подходит. Вместо этого там должна быть полноценная проверка аргументов, с возвратом кодов ошибок, выбрасыванием исключений, и т.д, и все это является частью интерфейса вашего компонента.
С другой стороны, если я в release версии своего компонента оставлю все ASSERTs, и в один прекрасный момент вы обнаружите, что срабатывает ASSERT где-то на двенадцатом уровне вызовов, чем вам это поможет?
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Почему нельзя отключать ASSERT-ы в релизе
Лучше использовать настраиваемые логи, тогда вопрос о том, что отключать в релизе, а что нет, просто не будет существовать, т.к. это можно будет сделать в любой момент.
Здравствуйте, Дарней, Вы писали:
Д>про это еще Страуструп говорил Д>примененять защитные проверки в debug конфигурации и отключать их в release — это всё равно что плавать на корабле с полным набором спасательных кругов и шлюпок возле берега, и сгружать их на берег перех выходом в открытое море.
Да-да, еще Страуструп.
А до Страуструпа — еще Хоар.
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
Здравствуйте, Alex Fedotov, Вы писали:
AVC>>Например, зловредными индусами, которыми нас тут недавно стращали. AVC>>Здесь мы не можем надеяться, что исключили ошибки в использовании компонента. Ведь большинство этих ошибок еще даже не были сделаны, когда мы завершили отладку. AVC>>Разумно ли в такой ситуации изымать из кода проверку предусловий?
AF>Почему-то постоянно кто-то пытается использовать ASSERTs для того, для чего они не предназначены.
Покажите мне этих негодяев, я им задам!
AF>ASSERT проверяет условие, которое истинно всегда. Если вы уже знаете, что вы не контролируете входные данные и они могут быть недействительными, то ASSERT, очевидно, для этого не подходит. Вместо этого там должна быть полноценная проверка аргументов, с возвратом кодов ошибок, выбрасыванием исключений, и т.д, и все это является частью интерфейса вашего компонента.
В BlackBox, хелп к которому цитировал Губанов, ASSERT именно этим и занимается.
И как Вы правильно сказали, определенные требования к аргументам являются частью интерфейса компонента.
Поэтому отключать ASSERT в таких условиях нельзя. О чем опять же правильно написано в этом хелпе.
По моему, мы пришли к консенсусу, если только не принимать близко к сердцу лингвистические споры об ASSERT и VERIFY.
По поводу последнего только замечу, что в разных языках существуют разные традиции именования.
AF>С другой стороны, если я в release версии своего компонента оставлю все ASSERTs, и в один прекрасный момент вы обнаружите, что срабатывает ASSERT где-то на двенадцатом уровне вызовов, чем вам это поможет?
В BlackBox (как и в большинстве Оберон-систем) практически нет разницы между release и debug.
Отладочная информация не включается в код, а в случае срабатывания "ловушки" восстанавливается RTS.
Недавно подобный факт открыл для себя VladD2, правда, теоретически — при чтении статьи об одном из способов обработки исключений в Обероне. (Кажется, он думал, что Сергей об этом не знал. )
на Ваш вопрос отвечаем: в результате срабатывания ASSERT на двенадцатом уровне вызовов мы можем восстановить полную информацию о всех 12 подпрограммах, и даже больше. По крайней мере, в BlackBox.
Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.
Здравствуйте, GlebZ, Вы писали:
GZ>Честно говоря, релизный код не должен содержать ошибок.
Естественно, но дело не в ошибках внутри Вашего модуля (а я говорю именно о модулях — бинарниках). Ошибок внутри релизного модуля быть, конечно, не должно, но и ASSERT-ы в нём не должны быть отключены.
Причина запрета отключения ассертов в модуле находится не внутри модуля, а вне его.
Другой разработчик, разрабатывая свои модули взаимодействующие с Вашим (безошибочным) модулем, сам может ошибаться. Так вот, чтобы ошибка из его модуля не пролезла внутрь Вашего — Ваш модуль должен тщательно "обороняться". Оставленные ассерты — это "оборона" Вашего (безошибочного) модуля от ошибок других программистов в других модулях (чтоб ошибки не проникали из одного модуля внутрь другого).
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Другой разработчик, разрабатывая свои модули взаимодействующие с Вашим (безошибочным) модулем, сам может ошибаться. Так вот, чтобы ошибка из его модуля не пролезла внутрь Вашего — Ваш модуль должен тщательно "обороняться". Оставленные ассерты — это "оборона" Вашего (безошибочного) модуля от ошибок других программистов в других модулях (чтоб ошибки не проникали из одного модуля внутрь другого).
Давай разведем процесс по ролям. Допустим у нас есть два программиста, при этом один поставил свой модуль и его забрали в армию. И клиенту поставляют вместе с модулем программу написанную вторым программистом.
1. Роль — программист который делает модуль. Для него ассерт как таковой информативен, поскольку несет в себе информацию о месте возникновения ошибки. Имея место, существует надежда что он может локализовать ошибку.
2. Роль — программист-клиент модуля. Для него ассерт по боку. Если он получит ассерт, то единственное что он поймет что где-то что-то у нас порой. Для него нужна более информативная ошибка о неверных параметрах, и каким образом он дошел до такой жизни.
3. Роль — клиент. А вот ему все по фигу. Его не волнует кто ошибся и где ошибся. Он сам может ошибиться. Тогда получается так, ассерт принесенный из модуля, его не волнует. Ошибка принесенная из модуля, тоже ему немного о чем говорит. Подобную ошибку должен выводить программист который написал программу, и в терминах понятных для клиента. То есть, основная информация должна быть выдана программистом основной программы на основе ошибки в модуле. Другое дело, что дополнительно если эта ошибка выходит за пределы бизнес-логики, то программисту нужно получить более подробную информацию. Это делается дополнительными средствами(типа логов, или отправке дампа по почте). Но то что в этой подробной информации написано, клиент вряд-ли разберется.
Вот в результате мы и получаем, что ассерт написанный данным программистом, в релизе никому не нужен кроме самого программиста.