Не так давно Samsung анонсировал собственную платформу для разработки приложений для мобильных устройств – Bada. Основным языком разработки для нее является C++. Но какой C++? C++ без исключений, без умных указателей, без использования RAII, без STL. И это в XXI-ом веке! Маразм крепчал, однако.
С подачи одного из разработчиков Poco я пролистал презентацию по платформе Bada. Желание смотреть на Bada пропадает после двух первых примеров: использования двухфазной инициализации объектов и кодов возврата+макросов вместо исключений.
Объекты в Bada нужно инициализировать посредством явного вызова метода Construct:
class TwoPhaseClass { public: TwoPhaseClass(void) : p1(null), p2(null) { } ~TwoPhaseClass(void) { delete p1; delete p2; } result Construct(void) { SimpleClass* p1 = new SimpleClass(); SimpleClass* p2 = new SimpleClass(); // Out-of-memory error. } private: SimpleClass* p1; SimpleClass* p2; }; void MyClass::SomeFunction() { // Calls the constructor which does not throw an exception. TwoPhaseClass a; // Calls the Construct() method which allocates two SimpleClass objects. // Destructor is called because ‘a’ itself is fully constructed. result r = a.Construct(); ... } |
Какая религия им запретила использовать для p1 и p2 класс std::auto_ptr – не понятно.
А вот работа без исключений еще прикольнее:
result r = E_SUCCESS; ... // Case 1: The method returns a result. r = list.Construct(...); if (r != E_SUCCESS) // identical to 'if (IsFailed(r))' { // Process the error condition. } // Case 2: The method sets the result in the method body or returns null. pObj= list.GetAt(...); if (GetLastResult() != E_SUCCESS) // or 'if (pObj== null)' { // Process the error condition. } // Case 3 r = pObj2->Construct(..); TryCatch(r == E_SUCCESS, , "[%s] Service could not be initialized.", GetErrorMessage(r)); ... CATCH: delete pObj1; delete pObj2; return; |
Здесь следует обратить внимание на TryCatch и CATCH – это как раз то, что предлагается вместо нормальных C++ исключений. А следующий пример демонстрирует всю мощь и силу корейской программистской науки:
result r = E_SUCCESS; ... // Case 1: Use a `goto CATCH'. r = pList->Construct(...); TryCatch(r == E_SUCCESS, delete pList, "[%s] Propagated.", GetErrorMessage(r)); ... CATCH: SetLastResult(r); return null; // Case 2: Return a result. r = list.Construct(...); TryReturn(r == E_SUCCESS, r, "[%s] Propagated.", GetErrorMessage(r); // Case 3: Return null. r = list.Construct(...); TryReturn(r == E_SUCCESS, null, "[%s] Propagated.", GetErrorMessage(r); // Case 4: Convert an error condition into another error condition. r = list.indexOf(...); TryReturn(r == E_SUCCESS, E_INVALID_ARG, "[E_INVALID_ARG] converted from '%s'.", GetErrorMessage(r)); |
Ну что тут скажешь… Опыт разработчиков первых версий Symbian им явно не указ. Наличие большого количества C++ных библиотек и приложений, написанных на “нормальном” C++ с исключениями, STL и даже с Boost-ом – тем более.
Особо умиляет мотивация отказа от исключений – мол очень высокий run-time overhead. Трындец, просто. Во времена, когда у смартфонов устанавливаются гигагерцные процессоры и гигабайты памяти, накладные расходы на обработку C++ исключений кто-то рассматривает всерьез? Афигеть. Мы, в свое время, на 386-х и 486-х как-то умудрялись и C++ исключения использовать, и умные указатели, и даже STL. И все это на 40MHz процессорах с 4Mb памяти на борту. В общем, чего-то я не понимаю :-/
Желающие могут пролистать презентацию до конца. Там еще много забавного. Например, собственные контейнеры. Но по мне вывод однозначен – в топку такую платформу.
PS. А ведь, по слухам, Samsung собирается провести конкурс среди разработчиков с призовым фондом в несколько миллионов долларов ($2.7M призовой фонд и $300K главный приз). Редиски, блин. Их бы энергию, да в мирных целях.
Это все особенно умиляет на фоне реализации обработки исключений в gcc. Там, как известно, давно уже поддержка исключений не вызывает runtime cpu overhead-а в случае, если исключение не выброшено (table approach). А я готов биться о заклад, что Samsung использует gcc для target-компиляции. И тем не менее, опять и опять встречаешь унылые рассуждения о том, что мол, исключения вызывают непозволительный overhead даже в случае, если они не используются и т.д. Клоуны, одним словом.
ОтветитьУдалитьДа, реально страшно. Особенно когда представляешь, сколько на это было (впустую) потрачено денег.
ОтветитьУдалитьЕдинственное, как мне кажется, нормальное объяснение не использовать исключения -- это http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Exceptions#Exceptions
Действительно интересно, сколько Samsung потратил на разработку этой платформы. У меня есть впечатление (возможно, обманчивое), что если бы Samsung вложил те самые $2.7M в какой-нибудь толковый СНГ-овский стартап, то мог бы получить гораздо более современную платформу.
ОтветитьУдалитьХм, заставило задуматься... В общем-то странно, что в 21 веке многие разработчики используют этот стиль. Уже много раз видел, особенно он распространен в геймдеве. Ну и гугл тудаже... Ну да ладно, это их трудный "путь нинзя".
ОтветитьУдалитьВ защиту же этой библиотеки пришло в голову только одно: эта библиотека расчитана на использование в различных языках, а не только в С++. Вот как например из другого языка поймать эксепшн С++ если он вдруг выскочит? Хотя с другой стороны так сразу то и не придумаешь какой еще язык может быть использован кроме С++, какойнибудь Go?..
2Роман:
ОтветитьУдалить>Ну и гугл тудаже...
У гугла, помнится, одно из объяснений было в том, что они используют старый C-шный и C++ный код, в котором исключений нет. Поэтому проще не использовать исключения в новом коде, чем заниматься интеграцией "кода с исключениями" и "кода без исключений". Поскольку такая интеграция, действительно, не есть хорошо и удобно.
>Вот как например из другого языка поймать эксепшн С++ если он вдруг выскочит?
Ну в JRuby как-то ловят Java-исключения, который возникают в Java-библиотеках :) Способы интеграции C++ с другими языками уже давно отработаны.
>В защиту же этой библиотеки пришло в голову только одно: эта библиотека расчитана на использование в различных языках, а не только в С++.
Дык как я понимаю, это не столько библиотека, сколько платформа. Нельзя на Bada писать приложения без ее использования. Т.е. хочешь написать приложение для Bada-телефона -- берешь C++, берешь Bada SDK и вперед. Другого выбора просто нет. Хочешь портировать Python туда -- берешь C++, берешь Bada SDK и портируешь исходники Python-а, заменяя POSIX-овые вызовы вызовами Bada. По крайней мере у меня такое впечатление сложилось.
>У гугла, помнится, одно из объяснений было в том, что они используют старый C-шный и C++ный код, в котором исключений нет. Поэтому проще не использовать исключения в новом коде, чем заниматься интеграцией "кода с исключениями" и "кода без исключений". Поскольку такая интеграция, действительно, не есть хорошо и удобно.
ОтветитьУдалитьНеужели при такой интеграции появляются такие уж страшные проблемы, что боязнь с ними столкнуться заставляет гугл отказаться от исключений?
Вот предположим С++ код использует С код. Имеем те же самые проверки, но вместо того чтобы возвращать код ошибки бросаем исключение.
Предположим С код использует С++ код. Вот тут сложнее, придется писать обертки для функций бросающих исключения. Но такая ситуация довольно редкая как мне думается.
В принципе если гугл сталкивается с этим чаще, чем нормальные люди тогда да...
>Неужели при такой интеграции появляются такие уж страшные проблемы, что боязнь с ними столкнуться заставляет гугл отказаться от исключений?
ОтветитьУдалитьНе знаю, гугловских исходников не видел :) Когда на RSDN-е дали ссылку на гугловские правила я так же вопрошал, почему же в гугле, где работают не самые глупые разработчики, исключений не используют.
Не понимаю и до сих пор. Однако, их объяснение хотя бы выглядит более убедительным, чем лепет самсунговцев об оверхеде.
В принципе, проблемы интеграции понять можно. Вот у меня в SObjectizer, к сожалению, была допущена серьезная ошибка -- исключения не использовались. Это выливается в то, что обработчики событий не могут выпускать исключения наружу. Что приводит к тому, что любой обработчик событий, который использует внутри себе что-нибудь с исключениями, должен завершаться catch-ами.
У нас достаточно catch(const std::exception&). Но если, не дай Бог, придется использовать сторонние библиотеки, которые выбрасывают исключения по указателю, или исключения, не производные от std::exception, или int-ы вместо классов-исключений, то ситуация сильно усложнится.
Видимо, гугловцы (опять же, как очень неглупые люди), решили присечь все это на корню.
Понять это можно. Но лично я все равно их точку зрения не разделяю. Исключения, имхо, это пока лучший способ информированния об ошибках, доступный в мейнстримовых языках. И хотя в C++ с исключениями чуть хуже, чем в языках со сборкой мусора (сложно делать вложение исключений), но все равно исключения гораздо лучше, чем коды возврата.
Я плохой программист. Я бы всё время забывал делать конктруктора ещё и Сonstruct.
ОтветитьУдалитьПисал бы вот так: http://www.everfall.com/paste/id.php?e2n8dyp7qs7t с тремя замечаниями (которые только для этого куска кода!):
1) мне не нравится Bada-вское Get/SetLastError. Лучше уж возвращать errorCode везде :).
2) отлавливать out-of-memory (по-моему, (насколько позволяет моя эрудиция)) часто бессмысленно - упадёт не там :).
3) Ну только не говорите, что будете использовать собственный аллокатор памяти :).
>1) мне не нравится Bada-вское Get/SetLastError. Лучше уж возвращать errorCode везде :).
ОтветитьУдалитьСогласен. Если нет исключений, то пусть уж лучше везде будут коды ошибок.
Более того, поскольку в Bada шаблоны не запрещены окончательно, можно было бы сделать шаблон Result<T,int> для возврата значения и кода ошибки сразу (по аналогии с тем, как это сделано в Go). Тогда можно было бы писать как-то так:
Result<MyClass,int> r = MyClass::Construct(...);
if( E_OK == r.error ) { ... }
else { ... }
За эти деньги наверно можно было тот же D до ума довести и под платформу адаптировать.
ОтветитьУдалить2Rustam:
ОтветитьУдалитьИли заплатить их Браму Мулинару, чтобы только Zimb-ой занимался :)
А у D, имхо, только один шанс -- если его кто-нибудь форкнет под LLVM и отстранит Брайта с Александреску от управления развитием языка.
Кстати по части Go мысль пришла.
ОтветитьУдалитьУ них код ошибки возвращается объединенным с результатом. В принципе это интересный подход, но есть маленький недостаток. Обработка ошибок в таком коде будет перемешиваться с кодом алгоритма, что мешает его читабельности. Думаю в этой ситуации некоторый "сахарок" Go бы не повредил. Нужно то всего лишь скрыть переменную ошибки от программиста, чтобы каждый раз на нее не заморачиваться.
Например вот так (псевдокод): http://codepad.org/9xIm7ZKv
Роман я вообще не понимаю почему в Go отказались от исключений, ведь в таком языке (без C++ раскрутки стека) их можно сделать очень дешевыми, как пример OCaml в котором оверхеда от них практически нет.
ОтветитьУдалитьНасчет D ты конечно сурово :)
ОтветитьУдалитьА так да кандидатов полно.
2Роман: >Например вот так (псевдокод): http://codepad.org/9xIm7ZKv
ОтветитьУдалитьМне не очень нравится. Все-таки с явными кодами возврата легко сохранять код ошибки какой-то функции, потом делать несколько операций, а затем возвращать код ошибки от первой функции. Все это будет хорошо просматриваться в коде.
А ваше предложение напоминает то, как сейчас с errno работа идет.
2Rustam:
ОтветитьУдалить>ведь в таком языке (без C++ раскрутки стека)
Ну там все-таки есть некий аналог D-шного scope(exit) под названием defer. Так что раскрутка стека там все-таки есть.
А не сделали исключений в Go, вероятно, по причине глубоких C-шных корней у авторов языка. Ну или во всем Google в принципе не любят исключений -- в C++ запрещают использовать, в Go их не сделали ;)