Здравствуйте, igna, Вы писали:
I>Здравствуйте, MasterZiv, Вы писали:
MZ>>Без иключений нельзя использовать STL, потому что оно их кидает.
I>Какие исключения кидает STL?
I>(Именно STL, а не Standard Library; и именно сама STL, а не пользовательские функции, вызываемые из STL.)
Здравствуйте, sts, Вы писали:
sts>как минимум operator new который stl использует и который по умолчанию не является пользовательской функцией может кидать исключение
А пользовательским аллокатором нельзя это запретить? Если можно, кидает ли STL еще какие исключения? То есть, действительно ли "Без иключений нельзя использовать STL".
Здравствуйте, igna, Вы писали:
I>А пользовательским аллокатором нельзя это запретить?
А как? Даже если он не стрельнет исключением и вернёт, скажем, 0, как заставить тот же vector не пройтись по памяти в попытке инициализации значений?
I>Если можно, кидает ли STL еще какие исключения?
Кидает, но к сути вопроса это относится не будет (std::vector::at бросает std::out_of_range — метод можно просто не использовать).
Здравствуйте, igna, Вы писали:
I>Здравствуйте, sts, Вы писали:
sts>>как минимум operator new который stl использует и который по умолчанию не является пользовательской функцией может кидать исключение
I>А пользовательским аллокатором нельзя это запретить? Если можно, кидает ли STL еще какие исключения? То есть, действительно ли "Без иключений нельзя использовать STL".
а как сообщать об ошибках, что памяти нет ?
все равно интерфейс stl рассчитан на исключения.
иначе придется все время какой--нибудь свой собственный errno проверять.
тогда уж проще исполльзовать c-style контейнеры (и все остальное тоже) которые будут возвращать код ошибки.
Здравствуйте, MasterZiv, Вы писали:
MZ>Главное даже не if-ы, а корректность программы.
MZ>Вот протокол (сигнатура) метода:
MZ>class Complex MZ>{ MZ> ... MZ> Complex divide( const Complex& r ) const; MZ> ... MZ>};
MZ>Ну и что ему возвращать при таком вызове:
MZ>Complex res = Complex( 2, 3 ).divide( 0 );
MZ>?
Ну и что вы будете делать с исключением, которое можно здесь бросить ?
( Чем такое поведение принципиально будет отличатся от exit(some_code)? )
Здравствуйте, wander, Вы писали:
W>Здравствуйте, igna, Вы писали:
I>>(Именно STL, а не Standard Library; и именно сама STL, а не пользовательские функции, вызываемые из STL.)
W>Конструктор std::string в GCC бросает исключение, если передать туда ноль.
Здравствуйте, sts, Вы писали:
sts>Здравствуйте, wander, Вы писали:
W>>Здравствуйте, igna, Вы писали:
I>>>(Именно STL, а не Standard Library; и именно сама STL, а не пользовательские функции, вызываемые из STL.)
W>>Конструктор std::string в GCC бросает исключение, если передать туда ноль.
sts>как я понимаю, это не стандартное поведение
втом смысле, что не все так делают
некоторые могут падать
Здравствуйте, sts, Вы писали:
sts>Здравствуйте, wander, Вы писали:
W>>Здравствуйте, igna, Вы писали:
I>>>(Именно STL, а не Standard Library; и именно сама STL, а не пользовательские функции, вызываемые из STL.)
W>>Конструктор std::string в GCC бросает исключение, если передать туда ноль.
sts>как я понимаю, это не стандартное поведение
Да. Но тем не менее.
К тому же не любой код кросскомпиляторный. Есть проекты, которые живут только на MSVC или GCC и используют их языковые расширения и особенности.
Здравствуйте, MasterZiv, Вы писали: MZ>Ну и что ему возвращать при таком вызове: MZ>Complex res = Complex( 2, 3 ).divide( 0 ); MZ>? MZ>Если что-то возвращать, программа просто тупо становится MZ>некорректной. А если вводить "флаги успешности выполнения MZ>операции" -- сразу становится неуклюжей и неестественной.
Ну, в данном конкретном случае есть INF и NaN. А в общем же случае заводят внутренние флаги корректности/ошибки, которые необходимо проверять внутри при каждой операции (чтобы всё окончательно не развалилось). И публичный метод доступа, который пользователю нужно проверять время от времени. Пример есть в стандартной библиотеке C++ — iostreams.
Здравствуйте, B0FEE664, Вы писали:
MZ>>Ну и что ему возвращать при таком вызове: MZ>>Complex res = Complex( 2, 3 ).divide( 0 ); MZ>>?
BFE>Ну и что вы будете делать с исключением, которое можно здесь бросить ? BFE>( Чем такое поведение принципиально будет отличатся от exit(some_code)? )
Зависит от приложения. В REPL-интерпретаторе калькулятора/ЯП достаточно распечатать "Division by zero error at bla-bla-bla..." и вернуться к циклу read-eval-print (конечно, вместо конкретных чисел — переменные).
Здравствуйте, B0FEE664, Вы писали:
BFE>Ну и что вы будете делать с исключением, которое можно здесь бросить ?
поймаю его на границе слоя, и сообщу пользователю что операция не удалась
например
— пользователь жмет на кнопку, программа пытается выполнить операцию — возникает исключение, пользователь получает уведомление что операция не удалась — пользователь может изменить входные данные \ нажать на другую кнопку
— на веб-сервис приходит запрос — ... — сервис возвращает "500 internal error" и продолжает работу
— исключение ловится в catch(any_exception) в main loop, молча выводится в лог, делается minidump. потом можно проанализировать ситуацию и пофиксить баг.
Здравствуйте, MasterZiv, Вы писали:
MZ>Преимуществ нет. В конструкторе тоже можно вызывать виртуальные методы.
полиморфно — нельзя.
>> Однако, я не уверен что это действительно хорошее решение, может есть лучше. MZ>Использовать исключения.
сказано "без исключений" — значит *без исключений* .
постарайтесь обойтись без высказываний типа "сабж не нужен".
не знаете как решить проблему — лучше помолчите.
MZ>ага, тогда давай на С сразу пиши. Нафига тебе С++ ? MZ>Без иключений нельзя использовать STL, потому что оно их кидает. MZ>Любая НОРМАЛЬНАЯ библиотека будет тоже на исключениях.
см. LLVM
class Foo
{
public:
static Foo* create(..args..)
{
scoped_ptr<Bar> bar(Bar::create());
if (!bar) return;
scoped_handle h(CreateSome(...));
if (!h) return;
return new(std::nothrow) Foo(bar.release(), h.release());
}
Foo(Bar* bar, HANDLE h) : bar(bar), h(h) {}
private:
scoped_ptr<Bar> bar;
scoped_handle h;
};
Преимущества
— нет отдельно висящего метода init(), create выдает гарантированно валидный настроенный объект, либо NULL
— ???
Недостатки
— чтобы создать объект на стеке, надо скопипастить всё содержимое create() кроме new
— в конструктор вытаскиваются *все* члены класса с нетривиальной инициализацией — а это нарушает инкапсуляцию
— непонятно как быть с базовым классом
Или — классическая фабрика — функция или (виртуальный) метод
class Foo
{
public:
Foo(Bar* bar, HANDLE h) : bar(bar), h(h) {}
private:
scoped_ptr<Bar> bar;
scoped_handle h;
};
Foo* createFoo(...args..)
{
scoped_ptr<Bar> bar(Bar::create());
if (!bar) return;
scoped_handle h(CreateSome(...));
if (!h) return;
return new(std::nothrow) Foo(bar.release(), h.release());
}
Преимущества — труъ ООП, dependency injection, etc
В остальном всё так же как и у статического метода
Нe только в main — на границах модулей тоже лучше преобразовывать исключения в коды ошибок — при проблемах часть функционала отвалится, но хоть остальное похромает дальше, если сможет. Более чем актуально, когда модули самые что ни на есть модульные, и создаются разными компиляторами.
Здравствуйте, gegMOPO4, Вы писали:
MOP>Ну, в данном конкретном случае есть INF и NaN. А в общем же случае заводят внутренние флаги корректности/ошибки, которые необходимо проверять внутри при каждой операции (чтобы всё окончательно не развалилось). И публичный метод доступа, который пользователю нужно проверять время от времени. Пример есть в стандартной библиотеке C++ — iostreams.
iostreams — дерьмовая вообщем-то библиотека.
Проверять корректность каждой операции в общем случае значит умножать кол-во кода в 3-10 раз, плюс ошибки на неизбежном в таком случае копипасте. Я чтою на позиции, что внутри модудей — исключения, на границах — код ошибки + доп.нформация о ней, в main — все коды ошибок опять же преобразовываются в исключения, и выводим что поймаем
Здравствуйте, Abyx, Вы писали:
A>в обоих случая варианты без исключений существенно снижают читаемость и надежность кода (1)- исключения приходится заменять на член класса (isValid, whatHappened) или глобальную thread-local переменную (2),
которые можно без проблем проигнорировать на любом уровне и получит проблемы на абсолютно другом уровне (к п2).
По п. 1 Сами по себе не снижают, либо снижается надежность игнором проверки ошибок, либо читаемость раздуванием кода в несколько раз за счет этих проверок.
Здравствуйте, мыщъх, Вы писали:
М>это да. на плюсах нет возможности корректно обработать исключение. скажем, функция foo выполнила кучу операций и в какой-то момент решила открыть файл bar. и тут выяснилось, что файл открыть невозможно. что делать? кидем исключение. ловим его и видим, что (допустим) юзер вытащил флешку или cd/dvd. вот было бы здорово сказать юзеру -- воткни флешку обратно и продолжить выполнние функции foo с того места, в котором было брошено исключение. но увы... это невозможно (если только не реализовать свой диспетчер исключений).
Ты говоришь о "семантике возобновления", которой действительно нет в C++. Но это не "корректная обработка исключений".
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!