Re[26]: error is not an exception
От: IT Россия linq2db.com
Дата: 30.11.05 12:33
Оценка: 5 (2)
Здравствуйте, Сергей Губанов, Вы писали:

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


Доказательств правильности программы пока не существует вообще. Асёрты помогают выявить проблему и только. Грамотно расставленные асёрты помогают выявить проблему на ранней стадии. Но они даже не помогают её локализовать. Хотя в принципе, это возможно, но в таком коде за асёртами самой логики не будет видно

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

Лучший способ борьбы с инвариантами — это не асёрты, а изничтожение самих инвариантов, чем их меньше, тем меньше проблем. Но это мы сейчас опять скатимся во флейм stateless vs stateful.

Юнит тесты vs asserts обладают одним существеным преимуществом. Они доказывают, что сделанные изменения не нарушили работоспособность системы. В случае с асёртами мне никто не запрещает изменить условия проверок и сделать чужой код не работоспособным. В случае с юнит-тестами я сразу же получу красную лампочку. В случае с asserts это будет головной болью того, чей код перестанет работать. И, кстати, он может поменять спорный assert назад и сделать это моей проблемой и так до бесконечности.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Если нам не помогут, то мы тоже никого не пощадим.
Re[25]: error is not an exception
От: Cyberax Марс  
Дата: 30.11.05 12:40
Оценка: 1 (1)
IT wrote:

> C>Assertы нужны для проверки "невозможных" случаев, типа:

> Это всё легко покрывается юнит-тестами. Или ты их заменяешь асёртами?

Уберите слово "легко"

> C>Ну да, просто заменить на замусоривание исключениями.

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

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

> C>НЕТ! Тогда стоит к устаревшим правилам отнести и хороший стиль

> программирования. В конце концов, он в .NET не нужен....
> Зачем передёргивать? Или ты хочешь сказать, что хороший стиль и асёрты
> — это синонимы?

Это реакция на слово "устаревший"

> ЗЫ. Не хочу с тобой продолжать бодаться, всё равно ни мне тебя, ни

> тебе меня похоже не переубедить. Скажу только, что лично я, никакой
> практической пользы от асёртов сегодня не вижу. Речь идёт о .NET/C#.
> Такое моё сугуболичное ИМХО.

В .NET сильно помогает то, что бибилиотека делает очень много проверок.
То есть то, что в С++ могло проскочить незамеченным здесь вызовет
исключение. Но это не отменяет ценности ассертов в своем коде

> Пойду лучше Серёгу за забытый ролбэк отстегаю


Бей его, бей

--
С уважением,
Alex Besogonov (alexy@izh.com)
Posted via RSDN NNTP Server 2.0
Sapienti sat!
Re[30]: Что вы предлагаете на замену эксепшенов?
От: vdimas Россия  
Дата: 30.11.05 13:15
Оценка:
Здравствуйте, IT, Вы писали:

IT>Это просто более громоздкая конструкция. В том же примере можно было создать err в вызывающем методе и передавать его обычным способом. Т.е. out/ref параметров практически всегда можно избежать и их наличие очень точный индикатор проблем в дизайне.


Оффтоп, надеюсь на громоздкие value-типы этот принцип не распространяется?
Re[12]: Вот подумалось
От: vdimas Россия  
Дата: 30.11.05 13:27
Оценка: :)
Здравствуйте, Belsen, Вы писали:

[...]

я провел лениво взглядом не вдаваясь в подробности, дошел до конца... где-то в мозжечке что-то засвербило... заставил себя навести резкость и... чуть не свалился со стула!
Я несколько раз слышал о таком коде, но увидел впервые.

В общем, послеобеденный сон на рабочем месте был безвозратно испорчен.
Re[12]: Вот подумалось
От: vdimas Россия  
Дата: 30.11.05 13:38
Оценка:
Здравствуйте, reductor, Вы писали:

R>Но конечно основной смысл в том, что если у нас есть

R>
R>// некий абстрактный код
R>int y = 5;
R>if (x == 1) return y;
R>y++;
R>if (x == 2) return y;
R>return y + 5
R>

R>То понять что здесь происходит невозможно ни компилятору (та самая проблема с автоматами) ни человеку.

Я в очередной раз ловлю себя на ступоре после прочтения подобной фразы. Может я чего-то не понимаю? Откуда у компилятора или у человека сложности с прочтением? Насчет обязательности соответствия y<=f(x) мне понятно, насчет каких-то затруднений компиляции — нет. Хочешь, прямо здесь и сейчас граф детерминриованного автомата нарисую по этому куску кода? Не знаю, как другие программисты трассируют исходники в голове, но я, когда трассирую куски алгоритмов именно думаю как автомат — т.е. перехожу из состояние в состояние (состояние — множество значений всех локальных/глобальных переменных и проч. ячеек данных быстрой и медленной памяти, прямо или коссвено принимающих участие в алгоритме) в ответ на некие действия над этими переменными.

Где фишка? Если можно построить детерминированный автомат, то можно проверифицировать что угодно.
Re[13]: Вот подумалось
От: _Winnie Россия C++.freerun
Дата: 30.11.05 13:45
Оценка:
В банковских операциях разбираюсь, как кот в желудях, так что где-то мог и ошибиться.
О rollback-commit относительно них узнал, читая эту тему.

Вот решение на pure C.
Если можно использовать С++ RAII c автоматическим rollback в деструкторе, то всё еще красивше.
И причём тут исключения?

#include <stddef.h>

typedef struct банковская_операция_tag
{
    void *handle;
    /*...*/
} банковская_операция_t;

typedef struct банк_tag
{
    void *handle;
} банк_t;

typedef struct аккаунт_tag
{
    void *handle;
} аккаунт_t;

typedef struct кол_денег_tag
{
    long long m_количество_в_копейках;
} кол_денег_t;

void rollback(банковская_операция_t *банковская_операция);
void commit(банковская_операция_t *банковская_операция);

_Bool снять_деньги(банк_t *банк, аккаунт_t *откуда, кол_денег_t сколько, банковская_операция_t *банковская_операция);
_Bool положить_деньги(банк_t *банк, аккаунт_t *откуда, кол_денег_t сколько, банковская_операция_t *банковская_операция);
_Bool журнализовать(банк_t *банк, банковская_операция_t *банковская_операция, wchar_t *фоматирование, ...);



_Bool снять_положить_записать_на_C(банк_t *банк, аккаунт_t *откуда, аккаунт_t *куда, кол_денег_t сколько) 
{
    банковская_операция_t бо_снять = { 0 };
    банковская_операция_t бо_положить = { 0 };
    банковская_операция_t бо_журнализовать = { 0 };

    if (!снять_деньги(банк, откуда, сколько, &бо_снять)) goto ошибка;
    if (!положить_деньги(банк, куда, сколько, &бо_положить)) goto ошибка;
    if (!журнализовать(
        банк, &бо_журнализовать, локал_строка("TRANSER_MONEY_FROM_TO"), 
        аккунт_имя(откуда), аккунт_имя(куда), сколько.m_количество_в_копейках)) goto ошибка;
    
    commit(&бо_снять);
    commit(&бо_положить);
    commit(&бо_журнализовать);
    return true;
ошибка:
    rollback(&бо_снять);
    rollback(&бо_положить);
    rollback(&бо_журнализовать);
    return false;
}
Правильно работающая программа — просто частный случай Undefined Behavior
Re[31]: Что вы предлагаете на замену эксепшенов?
От: IT Россия linq2db.com
Дата: 30.11.05 14:19
Оценка:
Здравствуйте, vdimas, Вы писали:

IT>>Это просто более громоздкая конструкция. В том же примере можно было создать err в вызывающем методе и передавать его обычным способом. Т.е. out/ref параметров практически всегда можно избежать и их наличие очень точный индикатор проблем в дизайне.


V>Оффтоп, надеюсь на громоздкие value-типы этот принцип не распространяется?


По поводу value типов FxCop скромно молчит Но громоздкости это не убавляет всё равно.
Если нам не помогут, то мы тоже никого не пощадим.
Re[14]: Вот подумалось
От: Sergey J. A. Беларусь  
Дата: 30.11.05 14:23
Оценка:
Здравствуйте, _Winnie, Вы писали:

_W>Вот решение на pure C.

_W>Если можно использовать С++ RAII c автоматическим rollback в деструкторе, то всё еще красивше.
А, если использовать и исключения, то "всё еще красивше" в ещё большей степени.
... << RSDN@Home 1.2.0 alpha rev. 619>>
Re[27]: error is not an exception
От: Sinclair Россия https://github.com/evilguest/
Дата: 30.11.05 14:31
Оценка: 1 (1) +1
Здравствуйте, IT, Вы писали:

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


IT>Лучший способ борьбы с инвариантами — это не асёрты, а изничтожение самих инвариантов, чем их меньше, тем меньше проблем. Но это мы сейчас опять скатимся во флейм stateless vs stateful.


IT>Юнит тесты vs asserts обладают одним существеным преимуществом. Они доказывают, что сделанные изменения не нарушили работоспособность системы. В случае с асёртами мне никто не запрещает изменить условия проверок и сделать чужой код не работоспособным. В случае с юнит-тестами я сразу же получу красную лампочку. В случае с asserts это будет головной болью того, чей код перестанет работать. И, кстати, он может поменять спорный assert назад и сделать это моей проблемой и так до бесконечности.

Ну, мне лично кажется, что ассерты никак не мешают юнит тестам. Как и наоборот.

Зато ассерты без юнит тестов стоят ровно нуль. Потому, что ассерт — это императивная конструкция. Т.е. пока мы не наступили на ассерт, он абсолютно бесполезен. Наступанием на все граничные точки как раз и занимаются юнит тесты.
Ассерты помогут выяснить места косяков — когда падает юнит тест, вообще говоря не очень понятно, кто из стека был неправ. То ли DB, которая не смогла правильно обработать NULL, то ли BL, который этот нулл туда передал, то ли UI, который этот null позволил ввести.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: Вот подумалось
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 30.11.05 14:41
Оценка:
Здравствуйте, _Winnie, Вы писали:

_W>
_W>_Bool снять_положить_записать_на_C(банк_t *банк, аккаунт_t *откуда, аккаунт_t *куда, кол_денег_t сколько) 
_W>{
_W>    банковская_операция_t бо_снять = { 0 };
_W>    банковская_операция_t бо_положить = { 0 };
_W>    банковская_операция_t бо_журнализовать = { 0 };

_W>    if (!снять_деньги(банк, откуда, сколько, &бо_снять)) goto ошибка;
_W>    if (!положить_деньги(банк, куда, сколько, &бо_положить)) goto ошибка;
_W>    if (!журнализовать(
_W>        банк, &бо_журнализовать, локал_строка("TRANSER_MONEY_FROM_TO"), 
_W>        аккунт_имя(откуда), аккунт_имя(куда), сколько.m_количество_в_копейках)) goto ошибка;
    
_W>    commit(&бо_снять);
_W>    commit(&бо_положить);
_W>    commit(&бо_журнализовать);
_W>    return true;
_W>ошибка:
_W>    rollback(&бо_снять);
_W>    rollback(&бо_положить);
_W>    rollback(&бо_журнализовать);
_W>    return false;
_W>}
_W>


Может это нужно было вот в эту ветку: Goto's are evil?
Автор: Cyberax
Дата: 28.11.05
?

Хотя я бы написал что-нибудь подобное без goto:
int commit_transfer_money( transfer_money_trx_state_t * state )
    {
        commit( state->put_trx() );
        commit( state->get_trx() );
        commit( state->log_trx() );

        return 0;
    }
    
int rollback_transfer_money( transfer_money_trx_state_t * state )
    {
        rollback( state->put_trx() );
        rollback( state->get_trx() );
        rollback( state->log_trx() );
        return -1;
    }
    
int transfer_money( bank_t * bank, account_t * from, account_t * to, money_t amount )
    {
        transfer_money_trx_state_t state;
        int (*finisher)( transfer_money_trx_state_t ) = &rollback_transfer_money;
        
        if( 0 == log_transfer( bank, from, amount, &state ) &&
            0 == get_money( bank, from, amount, &state ) &&
            0 == put_money( bank, to, amount, &state ) )
            finisher = &commit_transfer_money;
            
        return (*finisher)( &state );
    }
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[13]: Вот подумалось
От: reductor  
Дата: 30.11.05 14:51
Оценка: 9 (1)
Здравствуйте, vdimas, Вы писали:

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


R>>Но конечно основной смысл в том, что если у нас есть

R>>
R>>// некий абстрактный код
R>>int y = 5;
R>>if (x == 1) return y;
R>>y++;
R>>if (x == 2) return y;
R>>return y + 5
R>>

R>>То понять что здесь происходит невозможно ни компилятору (та самая проблема с автоматами) ни человеку.

V>Я в очередной раз ловлю себя на ступоре после прочтения подобной фразы. Может я чего-то не понимаю? Откуда у компилятора или у человека сложности с прочтением? Насчет обязательности соответствия y<=f(x) мне понятно, насчет каких-то затруднений компиляции — нет. Хочешь, прямо здесь и сейчас граф детерминриованного автомата нарисую по этому куску кода? Не знаю, как другие программисты трассируют исходники в голове, но я, когда трассирую куски алгоритмов именно думаю как автомат — т.е. перехожу из состояние в состояние (состояние — множество значений всех локальных/глобальных переменных и проч. ячеек данных быстрой и медленной памяти, прямо или коссвено принимающих участие в алгоритме) в ответ на некие действия над этими переменными.


http://en.wikipedia.org/wiki/Halting_problem

Нужна функция, которая возьмет код другой функции и даст ответ — даст ли функция ответ в заданном диапазоне значений

Первое пришедшее в голову решение с построением графа (запуском, фактически) проблему не решает, функция может и не завершить свою работу — потому не завершит свою работу и наш анализатор — потому ответ мы так и не получим.
Анализировать построенный граф статически — тут для начала полезно вспомнить про поиск Hamilton Path и следующую из него NP-полноту

Причем, конечно, нет доказательства самого существования как проблемы остановки автомата, так и существования NP-полноты, причем, есть основания утверждать, что существование такого доказательства невозможно, так как его наличие автоматически означает решение самой проблемы остановки, что автоматически приводит нас к доказательству обратного. Корни этого тесно переплетаются с парадоксом Рассела и теоремой Геделя о неполноте, если вы знакомы с ними, то уже сами заметили это сходство парой строк раньше

V>Где фишка? Если можно построить детерминированный автомат, то можно проверифицировать что угодно.


Выше показано почему это не так
Re[13]: Вот подумалось
От: vdimas Россия  
Дата: 30.11.05 14:59
Оценка:
Здравствуйте, Геннадий Васильев, Вы писали:

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


V>>Сделаю акцент еще раз на том факте, что корректное поведение программы в случае использования множественного return в неожиданных местах обычно обеспечивается доп. ср-вами самого языка. В С++ это автоматический вызов деструкторов локальных объектов (что крайне удобно, т.к. эти деструкторы вызываются только для реально инициализированных локальных переменных), в Java/C# есть try-finally для анаолгичных целей (не столь элегантно, но тоже вполне можно использовать).


V>>Таким образом, верификация даже такого кода может быть полной.

ГВ>Погоди, верификация и корректность работы компилятора — не одно и то же. Язык просто даёт нам некие автоматически работающие механизмы и не более того. И там и там есть оговорки использования и т.п. Можно и в деструкторе RAII исключение пульнуть.

Да, не точно выразился в выделенном, следовало так: язык предоставляет ср-ва для построения детерминированных программ (участков кода). Ничего, он размеется не обеспечивает, кроме перевода всего того, что мы накодили — в машинное представление.

ГВ>Другое дело, что верификация невозможна без спецификации, которой должна соответствовать программа. Нет спецификации — верификация невозможна. Вывал AV в некоторых ситуациях может расцениваться как штатное и запланированное поведение. То есть, сначала нужна полная спецификация: что можно, чего нельзя и т.п. Но такая спецификация по сложности сопоставима с самой программой, то есть, получим ту же проблему, но в профиль: языки спецификации, наследование спецификаций (множественное vs одиночное), синтаксический оверхед при специфицировании поведения и т.п.


V>> Другое дело, что мы не сможем верифицировать отдельно взятый участок (вернее можем, поясню далее), т.е. необходимо больше информации, о том, что происходит "неявно" для случая С++.


ГВ>Да ну? А что, в случае Java/C# работа GC уже стала 100% детерминированной?


Речь шла о "невидимом" вызове неких методов (деструкторов). Т.е. я намекал на неявность, хотя эта неявность очень четко специфицирована стандартом, и на нее можно смело опираться (детерминированный момент вызова деструкторов и детерминированный порядок их вызова).

В Java/C#, в отличие от, НЕ ПРИНЯТО логику помещать в финалайзеры. В стандартном Dispose-паттерне финалайзер вызывается только если есть ошибка в программе (т.е. программист забыл вызвать Dispose), как раз хорошо применимо для того, чтобы все-таки вернуть кисть оконной системе или отпустить COM-объект на все 4 стороны (лучше уж поздно, чем никогда). При работе паттерна в штатной ситуации в теле метда Dispose() происходит следующее: GC.SupressFinalize(this). Это означает, что финализатор у объекта вызван не будет, ибо не за чем.

ГВ>А полиморфный код тоже 100% сможем верифицировать?


Да, если полиморфные методы в свою очередь отвечают спецификациям.

В общем, попытаюсь обощить свою мысль. Для того, чтобы само верифицирование стало возможным, необходимо сделать программу детерминированной, т.е. иметь возможность в явном виде описать ее поведение на всем допустимом множестве входных данных. Reductor утверждал (причем, для Си/С++ — небезосновательно), что в некоторых приемах кроется опасность написания недетерминированного кода. Я в ответ показывал, как этот код приводить к детерминированному.


V>>Однако, и здесь нас ожидают бенефиты. Мы можем построить верификацию кода иерархически, т.е. проверифицировать сначала "прочистку", а затем код, использующий ее неявный вызов. Ввиду того, что целевой код неплохо сокращается в случае множественного return (или использования exceptions как продвинутой разновидности), мы можем получить как раз больше возможностей для верификации на "реальных объемах".


ГВ>Не, гиблая затея на любых объёмах. Примерно такое же ожидание панацеи, как её в своё время ожидали от перехода к языкам высокого уровня. Просто проблемы сместятся с одного уровня на другой. Всё равно задач адекватного учёта внешних условий и построения точных формулировок никто не снимет.


Где-то 10-тью постами выше я высказвал мысль о том, что верифицировать весь код просто нереально (лень даже обосновывать ) Вывод был такой, что чем выше повторное использование некоего куска кода, тем больше внимания нужно уделять этому куску. Кое-какие вещи я все же трассирую "пальчиком по экрану" полностью в некоторые моменты разработки, по всем возможным веткам. Заодно хочешь не хочешь, вынужден эти участки писать как можно проще, иначе подобное занятие станет невозможным.

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


------------
Конкретно в нашем проекте, тот участок, где я собираю метаинформацию, которая комбинируется из типов самих полей, типов соотв. полей в базе с учетом ограничений (длина + nullable), с последующим наложением других аттрибутов (в описании полей в C#), вычислением значений по-молчанию и т.д. и т.п. Чтобы обложить это дело юнит-тестами полностью, это нужна весьма длинная простыня кода самих тестов с различными тестовыми комбинациями, которые по объему далеко перепрыгнут нашу предметную модель. Поэтому юнит-тесты написаны на самые "популярные" сочетания, дополнительно довольно много внимания уделил именно этому куску, и в сочетании с первым делаю предположение, что все остальные всевозможные сочетания (их число конечно, просто очень большое) будут работать корректно.

-------------
А панацея все-таки есть.Если принять во внимание комбинаторный взрыв сложности от уровня к уровню наверх, на относительно большое число состояний или разнообразия данных на верхнем уровне прходится относительно малое число состояний для меньшего уровня. Поэтому подход юнит-тестов — это создание достаточного кол-ва тестовых ситуаций для модулей всех уровней (оно, это количетство элементарно недостижимо при комлексном тестировании). Соотвественно, с самого нижнего уровня, где меньше всего инвариантов, и который легче всего тестировать, поднимается некий "базис доверия". Используя этот базис доверия, на каждый следующий в иерархии модуль разрабатывают, скажем, не на порядок-другой больше тестов, а примерно столько же, сколько на предыдущем уровне. Видишь, тут важный момент, что мы расширяем базис доверия каждый раз из предположения, что предыдущие уровни его обеспечивают. Соответственно, вот он и ответ на вопрос, где же именно имеет смысл осуществлять верификацию кода (т.е., где результаты от потраченных ресурсов будут наиболее ощутимы).

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

-------------
Описанный полуэмпирический подход до того популярен, что его уже используют в академических кругах. Ту ОС на .Net, которую разрабатывают пара PhD для Microsoft, они впихнули в единое физическое адресное пространство (!!!), т.е. соседние процессы легко могут наступить друг-другу на лапы... Но это теоретически. А практически, создается некое ядро, компилятор, JIT + верификатор. Эти блоки образуют "пояс целомудрия", тот самый базис доверия, в которую они будут аккуратно запускать пользовательские приложения, нещадно их верифицируя по чем зря. Т.е., они считают, что разработчики ПО все-таки смогут выполнять свою работу не хуже разработчиков железа, и поддержка железки им уже не требуется.

(Раньше этот пояс целомудрия висел на уровне аппаратной реализации защиты памяти. Блин, народ, обидно же... Взрослые дядьки отвели детишкам песочницу, играйтесь, только не поцарапайтесь. И все в ответ: "да-да-да, поцарапаться можно так легко и незаметно, давайте-ка там делайте нам безопасные песочницы для игр".)

В общем посмотрим на эту Сингулярити. Мне пока кажется, что слова "теоретически" и "практически" запросто можно менять местами на данном этапе
Re[14]: Вот подумалось
От: vdimas Россия  
Дата: 30.11.05 15:13
Оценка: 21 (1)
Здравствуйте, _Winnie, Вы писали:

Вот у тебя очень показательная ошибка. Надо писать так:
goto ошибка_снять;
...
goto ошибка_положить;
...
goto ошибка_журналировать;


а последний кусок вообще надо в обратном порядке:
ошибка_журналировать:
    rollback(&бо_журнализовать);
ошибка_положить:
    rollback(&бо_положить);
ошибка_снять:
    rollback(&бо_снять);
    return false;



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


----------
Сам я всячески отстаиваю свое право на использование goto по собственному усмотрению, но тут такой неожиданный пример с граблями во всей красе... мне надо было внимательней прислушиваться к оппонентам
Re[28]: Что вы предлагаете на замену эксепшенов?
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 30.11.05 15:31
Оценка:
Здравствуйте, IT, Вы писали:

IT> Непонятно почему ты пропустил целый лэйер, но да бог с ним.


Прости, пропустил потому что там я написал бы тоже самое.

IT> ты совсем забыл сделать Rollback транзакции в случае ошибки.


Нет, не забыл. RollBack спрятан внутри accessor.Close и срабатывает автоматически если не было Commit

IT> Ошибки двух методов StealSomeXXX ты тоже почему-то решил не обрабатывать.


Инициатива наказуема... Хорошо, вот буквальный перевод Вашего кода:
  PROCEDURE (VAR this: AccountService) WithdrawalAndGetBalance (accountNumber: ARRAY OF CHAR; amount: REAL; OUT balance: REAL; VAR err: Errors.List): BOOLEAN, NEW;
    VAR r: BOOLEAN; accessor: AccountDataAccessor;

    PROCEDURE TransAction (): BOOLEAN;
    BEGIN
      IF ~accessor.GetBalance(accountNumber, balance, err) THEN RETURN FALSE END;
      IF balance < amount THEN 
        err := Errors.NewList(Errors.NewError('You are freaking bankrupt!'), err);
        RETURN FALSE
      END;
      balance := balance - amount;
      IF ~accessor.SetBalance(accountNumber, rest, err) THEN RETURN FALSE END;
      IF balance > 1.0E6 THEN
        IF ~accessor.StealSomeForMe(accountNumber, 1.15, err) THEN RETURN FALSE END;
        balance := balance - 1.15
      END;
      IF balance > 1.0E7 THEN
        IF ~accessor.StealSomeForHomelessPeople(accountNumber, 1.15, err) THEN RETURN FALSE END;
        balance := balance - 1.15
      END;
      IF ~accessor.ChargeForService(accountNumber, 10.0, err) THEN RETURN FALSE END;
      balance := balance - 10.0;
      RETURN TRUE
    END TransAction;

  BEGIN r := ValidateAccountNumber(accountNumber, err) & ValidateAmount(amount, err);
    IF r THEN
      accessor.Init;
      r := accessor.BeginTransaction(err) & TransAction() & accessor.CommitTransaction(err);
      accessor.Close  (* <------ RollBack спрятан здесь внутри accessor.Close и срабатывает автоматически если не было Commit *)
    END;
    RETURN r
  END WithdrawalAndGetBalance;
Re[28]: error is not an exception
От: IT Россия linq2db.com
Дата: 30.11.05 15:33
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Ну, мне лично кажется, что ассерты никак не мешают юнит тестам. Как и наоборот.


Подветка у нас началась вообще-то с того, что асёрты могут быть использованы вместо некоторых фич отладчика. О мешают/не мешают никто и не говорил. Я лишь пытаюсь утверждать, что, так как любое решение имеет как свои достоинства так и недостатки, то на сегодняшний день, при наличии современных средств отладки и тестирования, важность и достоинства асёртов легко ими покрываются. При этом вместе с асёртами уходят и их недостатки — замусоривание кода и спец. эффекты от использования макросов.

Вот и всё, что я хотел сказать. Надеюсь, я высказался достаточно осторожно, чтобы не дать повод к очередному флейму
Если нам не помогут, то мы тоже никого не пощадим.
Re[26]: error is not an exception
От: reductor  
Дата: 30.11.05 16:25
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:


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


Это персональное сообщения и вовсе не для флейма.

Я конечно понимаю полную бесперспективность этого совета, но
Вам, совершенно очевидно, просто необходимо познакомиться с язками, произведенными от ML и ISWIM — Haskell или на худой конец SML — хотя бы чтобы потом уметь тут же доказать вот это ваше утверждение (а доказательство этому существует уже лет 50 вообще и 40 в программировании)
Опять же, в Хаскеле вы, как теорфизик (если я ничего не путаю), найдете там немало знакомого.

Прошу прощения, я не смог не сказать этого :)
Re[27]: error is not an exception
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 30.11.05 16:34
Оценка:
Здравствуйте, reductor, Вы писали:

R>Вам, совершенно очевидно, просто необходимо познакомиться с язками, произведенными от ML и ISWIM — Haskell или на худой конец SML — хотя бы чтобы потом уметь тут же доказать вот это ваше утверждение (а доказательство этому существует уже лет 50 вообще и 40 в программировании)


Не совсем понял... Из этого следует, что уже 40 лет есть способ писать безошибочные программы?
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[29]: Что вы предлагаете на замену эксепшенов?
От: IT Россия linq2db.com
Дата: 30.11.05 16:51
Оценка:
Здравствуйте, Сергей Губанов, Вы писали:

IT>> ты совсем забыл сделать Rollback транзакции в случае ошибки.


СГ>Нет, не забыл. RollBack спрятан внутри accessor.Close и срабатывает автоматически если не было Commit


Ах, ну да, как я мог об этом не подумать, это же так очевидно из твоего кода

СГ>Инициатива наказуема... Хорошо, вот буквальный перевод Вашего кода:


Давай его ещё раз переведём, на этот раз твой код в твоём стиле, но с исключениями (синтаксиса исключений паскаля не знаю, поэтому буду сочинять).

PROCEDURE (VAR this: AccountService) WithdrawalAndGetBalance (accountNumber: ARRAY OF CHAR; amount: REAL): REAL, NEW;
  VAR balance: REAL; accessor: AccountDataAccessor;
BEGIN ValidateAccountNumber(accountNumber); ValidateAmount(amount); accessor.Init; accessor.BeginTransaction;
  balance := ~accessor.GetBalance(accountNumber);
  IF balance < amount THEN throw Errors.NewList(Errors.NewError('You are freaking bankrupt!')); END;
  balance := balance - amount;
  ~accessor.SetBalance(accountNumber, balance);
  IF balance > 1.0E6 THEN ~accessor.StealSomeForMe(accountNumber, 1.15); balance := balance - 1.15 END;
  IF balance > 1.0E7 THEN ~accessor.StealSomeForHomelessPeople(accountNumber, 1.15); balance := balance - 1.15 END;
  ~accessor.ChargeForService(accountNumber, 10.0);
  balance := balance - 10.0;
  accessor.CommitTransaction;
  RETURN balance;
END WithdrawalAndGetBalance;

Может посчитать количество строчек. Выигрышь более чем в два раза при гораздо более высокой наглядности (если слово наглядность вообще уместно при таком стиле).
Если нам не помогут, то мы тоже никого не пощадим.
Re[28]: error is not an exception
От: reductor  
Дата: 30.11.05 17:13
Оценка:
Здравствуйте, eao197, Вы писали:

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


R>>Вам, совершенно очевидно, просто необходимо познакомиться с язками, произведенными от ML и ISWIM — Haskell или на худой конец SML — хотя бы чтобы потом уметь тут же доказать вот это ваше утверждение (а доказательство этому существует уже лет 50 вообще и 40 в программировании)


E>Не совсем понял... Из этого следует, что уже 40 лет есть способ писать безошибочные программы?



Да.
Насколько это возможно в условиях существования основного ограничения теории автоматов и теоремы геделя о неполноте.
Но для большинства случаев (А вариантов ошибок и того что считать таковыми несколько больше, чем просто ошибка в программе и ошибка в данных) ответ — Да. 40 лет и последние лет 15 еще и очень практичный способ (аппарат расширили).

Но, заренее хочу сказать, здесь и сейчас доказывать это и спорить по этому поводу я не буду. Нет интереса.
Re[29]: error is not an exception
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 30.11.05 17:28
Оценка:
Здравствуйте, reductor, Вы писали:

E>>Не совсем понял... Из этого следует, что уже 40 лет есть способ писать безошибочные программы?


R>Да.


А мужики-то и не знают...


R>Насколько это возможно в условиях существования основного ограничения теории автоматов и теоремы геделя о неполноте.

R>Но для большинства случаев (А вариантов ошибок и того что считать таковыми несколько больше, чем просто ошибка в программе и ошибка в данных) ответ — Да. 40 лет и последние лет 15 еще и очень практичный способ (аппарат расширили).

R>Но, заренее хочу сказать, здесь и сейчас доказывать это и спорить по этому поводу я не буду. Нет интереса.


Лично со мной на эту тему спорить бесполезно. Разве что опубликовать этот способ.

Вспоминается, как на одной научной конференции довелось послушать одну тетеньку, которая продвигала какой-то частотный резонатор, как панацею от всех болезней. Типа того, что у всех клеток есть своя частота колебаний. И все больные или болезнетворные клетки (вирусы там всякие) находятся в других диапазонах, чем колебания здоровых клеток. Отсюда простое решение -- нужно взять генератор волн с такой же частотой, но противоположной фазой, разместить рядом больным и все -- больные клетки разрушатся в результате частотного резонанса. Эту тетеньку еще по НТВ в какой-то передаче показывали, с такими же росказнями.

Чей-то мне она вспомнилась.
... << RSDN@Home 1.1.4 stable rev. 510>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.