В С++ можно бросить исключение любого типа, хоть int:
throw 100;
Но в различных языках, которые были разработаны позже, практически везде можно бросать только исключения специальных типов (как правило, некоторого базового типа исключения и унаследованных от него).
Конечно, это удобнее, но... существуют ли теоретически такие ситуации, когда целесообразно бросить именно исключение любого, _произвольного_ типа , а не только специально унаследованного ?
И почему в С++ сделали возможность бросать исключение любого типа? Просто не подумали, или были какие-то другие причины?
Здравствуйте, Steamus, Вы писали:
NC>>В С++ можно бросить исключение любого типа, хоть int: NC>>
throw 100;
S>- Штур-рман!, прибор-ры! S>- Триц-цать! S>- Что тридцать? S>- А что приборы?
Ну хорошо,
throw MySuperPuperException("Произошла страшная ошибка в файле main.cpp в строке 666: кто-то обнулил очень важный указатель, а мне нужно записать туда данные!");
Вопрос не об этом: почему все исключения в языках кроме С++ наследуются от специального базового класса, и есть ли — хотя бы теоретически — такие ситуации, где это нежелательно и как-то ограничивает возможности программиста?
Здравствуйте, NeoCode, Вы писали:
NC>Вопрос не об этом: почему все исключения в языках кроме С++ наследуются от специального базового класса, и есть ли — хотя бы теоретически — такие ситуации, где это нежелательно и как-то ограничивает возможности программиста?
Сам вопрос семантически не очень понятен. Если нежелательно — ну бросайти самый базовый класс. Никакой информации — просто флаг. Ситации бросания исключений в виде целых натуральных чисел, это уже к вопросу об ООП и его преимуществах. По сути тот же код, который может быть погружен в класс. Наверное кто-то обоснует, что это намного эффективнее.
Здравствуйте, NeoCode, Вы писали:
NC>И почему в С++ сделали возможность бросать исключение любого типа? Просто не подумали, или были какие-то другие причины?
Если я не ошибаюсь, то в MSIL исключения могут быть типа object, т.е. фактически, любого типа. Но C# искусственно ограничивает базовый тип типом Exception.
Возможно, это связано с тем, что при выполнении throw среда выполнения запоминает текущий стэк и ей просто необходимо его куда-то запихать. Хотя, может и возможно выкинуть исключение типа string, если сгенерить MSIL код вручную. Может быть кто-то проверит это?
Здравствуйте, NeoCode, Вы писали:
NC>И почему в С++ сделали возможность бросать исключение любого типа? Просто не подумали, или были какие-то другие причины?
Исторически. Исключения появились в С++ задолго до стандартной библиотеки с std::exception внутри.
C++ дарует тебе свободу стрелять чем угодно в любую часть тебя. В данном конкретном случае — просто исторически сложилось. В ряду других случаев надо всегда держать в уме вопросы производительности и состыковки с Си.
Здравствуйте, NeoCode, Вы писали:
NC>В С++ можно бросить исключение любого типа, хоть int: NC>
throw 100;
NC>Но в различных языках, которые были разработаны позже, практически везде можно бросать только исключения специальных типов (как правило, некоторого базового типа исключения и унаследованных от него). NC>Конечно, это удобнее, но... существуют ли теоретически такие ситуации, когда целесообразно бросить именно исключение любого, _произвольного_ типа , а не только специально унаследованного ? NC>И почему в С++ сделали возможность бросать исключение любого типа? Просто не подумали, или были какие-то другие причины?
ИХМО, все дело в информации, которую предоставляет перехваченный объект-исключение
try
{
// some code
}
catch(MyAppException ex)
{
_log.Error("Shit happened, to help I have important info for you: " + ex.ImportantInformation);
}
catch(Exception ex)
{
_log.Error("Shit happened, no important info but look the stack trace: " + ex.StackTrace);
}
catch(object ex)
{
_log.Error("Shit happened, the only thing I can provode is: " + ex.ToString()); // насколько я знаю, в c++ ToString() (или аналога) нет
}
catch
{
_log.Error("Shit happened, no additional info, you are on your own with the error");
}
Здравствуйте, NeoCode, Вы писали:
NC> Вопрос не об этом: почему все исключения в языках кроме С++ наследуются от специального базового класса, и есть ли — хотя бы теоретически — такие ситуации, где это нежелательно и как-то ограничивает возможности программиста?
Не только в С++. В Delphi/FreePascal исключения конечно принято наследовать от специального класса Exception, но кинуть можно объект любого класса. Не киллер-фича совсем, т.к. всегда можно засунуть любую мету в наследник от базового класса исключений и получить тот же самый профит
Здравствуйте, NeoCode, Вы писали:
NC>И почему в С++ сделали возможность бросать исключение любого типа? Просто не подумали, или были какие-то другие причины?
Да тут-то всё просто. Всё ради бинарной совместимости lib файлов. Прародитель С обладал общепринятым форматом библиотек. Как результат, подобные библиотеки легко использовались в различных языках, либо библиотеку написанную на другом языке, можно было спокойно исполнять в С коде. С++ проектировался и обладает теми же свойствами.(холивар форматов Microsoft и Borland опущу, как давно поросший пенициллином). А язык, с которым интегрируется C++ библиотека не обязательно является объектно-ориентированным. Собственно, и stl не является вообще обязательным. Для микропроцессоров — это важно.
Примером успешности подобного подхода вполне можно назвать Apple IOS. Objective C ни разу не знает о С++ и в то же время, С++ может неподозревать о существовании NSObject. Между тем, оно вполне нормально интегрируется, и только хкоде с интедженсом, охмуренный знаниями об обоих падает и глючит.
NeoCode wrote:
> Конечно, это удобнее, но... существуют ли теоретически такие > ситуации, когда целесообразно бросить именно исключение любого, > _произвольного_ типа , а не только специально унаследованного ?
В чём проблема то? Можно бросать char* с текстом ошибки.
Да и сценарий с int тоже существует — бросаем код ошибки типа
GetLastError в винде. В идеале бросать коды ошибок более правильно чем
сразу хардкодить строки в исключение.
Здравствуйте, avp_, Вы писали:
_>В чём проблема то? Можно бросать char* с текстом ошибки. _>Да и сценарий с int тоже существует — бросаем код ошибки типа _>GetLastError в винде. В идеале бросать коды ошибок более правильно чем _>сразу хардкодить строки в исключение.
Проблемы нет, я не на форуме с++ а в философии
Сформулирую так: при разработке нового языка программирования целесообразно закладывать возможность бросать исключения любых типов, или только специального типа exception и его наследников?
Интересуют плюсы и минусы обоих подходов именно с философской точки зрения.
Здравствуйте, NeoCode, Вы писали: NC>Сформулирую так: при разработке нового языка программирования целесообразно закладывать возможность бросать исключения любых типов, или только специального типа exception и его наследников? NC>Интересуют плюсы и минусы обоих подходов именно с философской точки зрения.
Думаю лучше использовать один базовый тип исключений, тогда любое исключение сможет быть к нему приведено, т.е. обработчик всегда сможет получить некоторый стандартный минимум информации.
Хотя я выбрал другой подход: исключение это альтернативный выход из функции(а не объект), как и обычный выход он может возвращает результат любого типа, но тип возвращаемого при исключении значения нужно указывать при определении функции(если компилятор не сможет вывести).
Между тем,что я думаю,тем,что я хочу сказать,тем,что я,как мне кажется,говорю,и тем,что вы хотите услышать,тем,что как вам кажется,вы слышите,тем,что вы понимаете,стоит десять вариантов возникновения непонимания.Но всё-таки давайте попробуем...(Э.Уэллс)
NeoCode wrote:
> Сформулирую так: при разработке нового языка программирования > целесообразно закладывать возможность бросать исключения любых типов, > или только специального типа exception и его наследников? Интересуют > плюсы и минусы обоих подходов именно с философской точки зрения.
С философской точки зрения лучше дать больше свободы программисту чем
вводить натянутые ограничения в язык.
Это вопрос не языка а архитектуры конктретного приложения и библиотеки.
Здравствуйте, avp_, Вы писали:
_>NeoCode wrote:
>> Сформулирую так: при разработке нового языка программирования >> целесообразно закладывать возможность бросать исключения любых типов, >> или только специального типа exception и его наследников? Интересуют >> плюсы и минусы обоих подходов именно с философской точки зрения.
_>С философской точки зрения лучше дать больше свободы программисту чем _>вводить натянутые ограничения в язык.
А с практической точки зрения ровно наоборот. Самый лучший вариант — когда только один путь добиться цели, причем он априори "хороший".
_>Это вопрос не языка а архитектуры конктретного приложения и библиотеки.
Не все так однозначно. На библиотеку и архитектуру можно положить, а от языка никуда не денешься.
Здравствуйте, AlexCab, Вы писали:
AC>Здравствуйте, NeoCode, Вы писали: NC>>Сформулирую так: при разработке нового языка программирования целесообразно закладывать возможность бросать исключения любых типов, или только специального типа exception и его наследников? NC>>Интересуют плюсы и минусы обоих подходов именно с философской точки зрения. AC>Думаю лучше использовать один базовый тип исключений, тогда любое исключение сможет быть к нему приведено, т.е. обработчик всегда сможет получить некоторый стандартный минимум информации. AC>Хотя я выбрал другой подход: исключение это альтернативный выход из функции(а не объект), как и обычный выход он может возвращает результат любого типа, но тип возвращаемого при исключении значения нужно указывать при определении функции(если компилятор не сможет вывести).
Еще сделать ОБЯЗАТЕЛЬНЫМ при опри определении указывать и легальный выход, и аварийный (хотя бы и пустой).
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, NeoCode, Вы писали:
NC>И почему в С++ сделали возможность бросать исключение любого типа? Просто не подумали, или были какие-то другие причины?
Потому что в С++ есть внеклассовые данные. В нем можно написать программу вообще без классов, но с выбросом исключений.
Здравствуйте, LaptevVV, Вы писали:
AC>>Хотя я выбрал другой подход: исключение это альтернативный выход из функции(а не объект), как и обычный выход он может возвращает результат любого типа, но тип возвращаемого при исключении значения нужно указывать при определении функции(если компилятор не сможет вывести). LVV>Еще сделать ОБЯЗАТЕЛЬНЫМ при опри определении указывать и легальный выход, и аварийный (хотя бы и пустой).
Это как в Java? Вообще, мне эта идея очень нравится. Но вот в текущем виде она применима далеко не везде. А в Java реализована со значительными ограничениями. Ограничения начинаются на "first-class functions" и их аналогах. Пишу обобщенный пример:
public function doSomething(item : String) : void throws SomeException, SomeOtherException {
}
List<String> items = ...
items.iterate(doSomething)
Вот какой тип исключений будет в выражении items.iterate? В более сложных случаях функция может добавлять и свои типы исключений (например, итерация по result set в sql). Т.е. правильная сигнатура функции итерации (для result set) будет:
Т.е. мы рассматриваем "исключения" в функциях-параметрах как множества и позволяем прямо в сигнатурах выполнять сложение/вычитание. Причем вот эти вот сложения/вычитания нужно учитывать и при выводе типов. Для классических систем типов где "тип выражения определяется типами входящих в него значений" такая схема проблемы давать не должна.
Можно, конечно, применять и что-то формальное вроде Either в haskell. Но все таки для исключений автоматический лифтинг исключений в системах типов стоило бы сделать. Иначе либо будет куча ручных лифтов, либо куча typeclasses для протаскивания функции через разные монады. Ну и не только для этого типа (т.е. не только для исключений), у меня в реальных проектах еще пара кандидатов есть на подобную автоматику.
Вот что делать с каррироваными функциями, даже не представляют. Нужно ли специфицировать, в какой момент могут произойти "исключения"? Как удобно записывать, что исключение может произойти в любой момент для функций "от нескольких аргументов"? Как правильно вычислять типы при частичном применении функции к функции? В общем, вопросов много.
Что еще интересно. Вычисление автоматической "чистоты" (pure) функций работает примерно по тем же правилам. Это делается, например, черезе наличие ImpureException в списке функций аргумента.
Я вот не знаю, исследовались ли где-нибудь подобные системы типов. Т.е. когда тип "выражения" является не константой, а некоторым выражением от исходных типов аргументов. Возможно, каким-нибудь из студентов или аспирантов было бы интересно заняться этой темой. Хотя бы на примере тех же исключений (честных) или pure functions. Обязательно на языках с first-class functions. Если получится, то и вопрос с каррированием решить (будет очень и очень круто!).
Я сам планирую поисследовать этот вопрос. Но, блин, пока не хватает инфраструктуры. Вот почему все компиляторы монолиты? Я хочу собирать компилятор из кусочков. Взять одну систему типов, какой-то готовый синтаксис и т.п. Затем поменять одну часть (используя готовые и проверенные библиотеки для других) и т.п. В идеале я хочу посмотреть на multityping (т.е. одновременно несколько "параллельных" систем типов — обычная (типы значений и аргументов), исключения, purity, константность, "роли" в программе). Для константности очень нужна purity (тогда композицию/итерацию можно вычислять во время компиляции). "Роли" в программе — это чтобы деньги с километрами не складывались, например. Что-то мне кажется, что они по автоматическому вычислению будут чем-то на те же исключения похожи. Потихоньку инфраструктуру набираю, так что посмотрим, во что это в результате выльется.
Здравствуйте, maxkar, Вы писали:
M>Здравствуйте, LaptevVV, Вы писали:
AC>>>Хотя я выбрал другой подход: исключение это альтернативный выход из функции(а не объект), как и обычный выход он может возвращает результат любого типа, но тип возвращаемого при исключении значения нужно указывать при определении функции(если компилятор не сможет вывести). LVV>>Еще сделать ОБЯЗАТЕЛЬНЫМ при опри определении указывать и легальный выход, и аварийный (хотя бы и пустой).
M>Это как в Java? Вообще, мне эта идея очень нравится. Но вот в текущем виде она применима далеко не везде. А в Java реализована со значительными ограничениями. Ограничения начинаются на "first-class functions" и их аналогах.
[...]
Меня этот вопрос интересует не с технической стороны, а с точки зрения разработки-проектирования. В языках средства обработки аварийных ситуаций присутствуют (хороши они или плохи — второй вопрос). Но при разработке программ аварийным ситуациям не уделяют достаточно внимания.
А ведь обработку нештатных ситуаций НУЖНО проектировать точно так же, как и стандартную обработку.
Вот поэтому система должна ЗАСТАВЛЯТЬ программиста задавать обработку аварийных ситуаций обязательно.
Другое дело, что в нынешнем своем виде аппарат исключений мне не очень нравится — это фактически нелокальный переход.
M>Я сам планирую поисследовать этот вопрос. Но, блин, пока не хватает инфраструктуры. Вот почему все компиляторы монолиты? Я хочу собирать компилятор из кусочков. Взять одну систему типов, какой-то готовый синтаксис и т.п. Затем поменять одну часть (используя готовые и проверенные библиотеки для других) и т.п. В идеале я хочу посмотреть на multityping (т.е. одновременно несколько "параллельных" систем типов — обычная (типы значений и аргументов), исключения, purity, константность, "роли" в программе). Для константности очень нужна purity (тогда композицию/итерацию можно вычислять во время компиляции). "Роли" в программе — это чтобы деньги с километрами не складывались, например. Что-то мне кажется, что они по автоматическому вычислению будут чем-то на те же исключения похожи. Потихоньку инфраструктуру набираю, так что посмотрим, во что это в результате выльется.
Интересный подход — надо с народом обсудить подробнее...
Хочешь быть счастливым — будь им!
Без булдырабыз!!!