суббота, 12 декабря 2009 г.

[comp.prog.flame] Samsung Bada – маразматическая C++ платформа

Не так давно 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 главный приз). Редиски, блин. Их бы энергию, да в мирных целях.

16 комментариев:

CrystaX комментирует...

Это все особенно умиляет на фоне реализации обработки исключений в gcc. Там, как известно, давно уже поддержка исключений не вызывает runtime cpu overhead-а в случае, если исключение не выброшено (table approach). А я готов биться о заклад, что Samsung использует gcc для target-компиляции. И тем не менее, опять и опять встречаешь унылые рассуждения о том, что мол, исключения вызывают непозволительный overhead даже в случае, если они не используются и т.д. Клоуны, одним словом.

gp комментирует...

Да, реально страшно. Особенно когда представляешь, сколько на это было (впустую) потрачено денег.

Единственное, как мне кажется, нормальное объяснение не использовать исключения -- это http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml?showone=Exceptions#Exceptions

eao197 комментирует...

Действительно интересно, сколько Samsung потратил на разработку этой платформы. У меня есть впечатление (возможно, обманчивое), что если бы Samsung вложил те самые $2.7M в какой-нибудь толковый СНГ-овский стартап, то мог бы получить гораздо более современную платформу.

Роман комментирует...

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

В защиту же этой библиотеки пришло в голову только одно: эта библиотека расчитана на использование в различных языках, а не только в С++. Вот как например из другого языка поймать эксепшн С++ если он вдруг выскочит? Хотя с другой стороны так сразу то и не придумаешь какой еще язык может быть использован кроме С++, какойнибудь Go?..

eao197 комментирует...

2Роман:

>Ну и гугл тудаже...

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

>Вот как например из другого языка поймать эксепшн С++ если он вдруг выскочит?

Ну в JRuby как-то ловят Java-исключения, который возникают в Java-библиотеках :) Способы интеграции C++ с другими языками уже давно отработаны.

>В защиту же этой библиотеки пришло в голову только одно: эта библиотека расчитана на использование в различных языках, а не только в С++.

Дык как я понимаю, это не столько библиотека, сколько платформа. Нельзя на Bada писать приложения без ее использования. Т.е. хочешь написать приложение для Bada-телефона -- берешь C++, берешь Bada SDK и вперед. Другого выбора просто нет. Хочешь портировать Python туда -- берешь C++, берешь Bada SDK и портируешь исходники Python-а, заменяя POSIX-овые вызовы вызовами Bada. По крайней мере у меня такое впечатление сложилось.

Роман комментирует...

>У гугла, помнится, одно из объяснений было в том, что они используют старый C-шный и C++ный код, в котором исключений нет. Поэтому проще не использовать исключения в новом коде, чем заниматься интеграцией "кода с исключениями" и "кода без исключений". Поскольку такая интеграция, действительно, не есть хорошо и удобно.

Неужели при такой интеграции появляются такие уж страшные проблемы, что боязнь с ними столкнуться заставляет гугл отказаться от исключений?

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

Предположим С код использует С++ код. Вот тут сложнее, придется писать обертки для функций бросающих исключения. Но такая ситуация довольно редкая как мне думается.

В принципе если гугл сталкивается с этим чаще, чем нормальные люди тогда да...

eao197 комментирует...

>Неужели при такой интеграции появляются такие уж страшные проблемы, что боязнь с ними столкнуться заставляет гугл отказаться от исключений?

Не знаю, гугловских исходников не видел :) Когда на RSDN-е дали ссылку на гугловские правила я так же вопрошал, почему же в гугле, где работают не самые глупые разработчики, исключений не используют.

Не понимаю и до сих пор. Однако, их объяснение хотя бы выглядит более убедительным, чем лепет самсунговцев об оверхеде.

В принципе, проблемы интеграции понять можно. Вот у меня в SObjectizer, к сожалению, была допущена серьезная ошибка -- исключения не использовались. Это выливается в то, что обработчики событий не могут выпускать исключения наружу. Что приводит к тому, что любой обработчик событий, который использует внутри себе что-нибудь с исключениями, должен завершаться catch-ами.

У нас достаточно catch(const std::exception&). Но если, не дай Бог, придется использовать сторонние библиотеки, которые выбрасывают исключения по указателю, или исключения, не производные от std::exception, или int-ы вместо классов-исключений, то ситуация сильно усложнится.

Видимо, гугловцы (опять же, как очень неглупые люди), решили присечь все это на корню.

Понять это можно. Но лично я все равно их точку зрения не разделяю. Исключения, имхо, это пока лучший способ информированния об ошибках, доступный в мейнстримовых языках. И хотя в C++ с исключениями чуть хуже, чем в языках со сборкой мусора (сложно делать вложение исключений), но все равно исключения гораздо лучше, чем коды возврата.

Alexander P. комментирует...

Я плохой программист. Я бы всё время забывал делать конктруктора ещё и Сonstruct.
Писал бы вот так: http://www.everfall.com/paste/id.php?e2n8dyp7qs7t с тремя замечаниями (которые только для этого куска кода!):
1) мне не нравится Bada-вское Get/SetLastError. Лучше уж возвращать errorCode везде :).
2) отлавливать out-of-memory (по-моему, (насколько позволяет моя эрудиция)) часто бессмысленно - упадёт не там :).
3) Ну только не говорите, что будете использовать собственный аллокатор памяти :).

eao197 комментирует...

>1) мне не нравится Bada-вское Get/SetLastError. Лучше уж возвращать errorCode везде :).

Согласен. Если нет исключений, то пусть уж лучше везде будут коды ошибок.

Более того, поскольку в Bada шаблоны не запрещены окончательно, можно было бы сделать шаблон Result<T,int> для возврата значения и кода ошибки сразу (по аналогии с тем, как это сделано в Go). Тогда можно было бы писать как-то так:

Result<MyClass,int> r = MyClass::Construct(...);
if( E_OK == r.error ) { ... }
else { ... }

Rustam комментирует...

За эти деньги наверно можно было тот же D до ума довести и под платформу адаптировать.

eao197 комментирует...

2Rustam:

Или заплатить их Браму Мулинару, чтобы только Zimb-ой занимался :)

А у D, имхо, только один шанс -- если его кто-нибудь форкнет под LLVM и отстранит Брайта с Александреску от управления развитием языка.

Роман комментирует...

Кстати по части Go мысль пришла.

У них код ошибки возвращается объединенным с результатом. В принципе это интересный подход, но есть маленький недостаток. Обработка ошибок в таком коде будет перемешиваться с кодом алгоритма, что мешает его читабельности. Думаю в этой ситуации некоторый "сахарок" Go бы не повредил. Нужно то всего лишь скрыть переменную ошибки от программиста, чтобы каждый раз на нее не заморачиваться.

Например вот так (псевдокод): http://codepad.org/9xIm7ZKv

Rustam комментирует...

Роман я вообще не понимаю почему в Go отказались от исключений, ведь в таком языке (без C++ раскрутки стека) их можно сделать очень дешевыми, как пример OCaml в котором оверхеда от них практически нет.

Rustam комментирует...

Насчет D ты конечно сурово :)
А так да кандидатов полно.

eao197 комментирует...

2Роман: >Например вот так (псевдокод): http://codepad.org/9xIm7ZKv

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

А ваше предложение напоминает то, как сейчас с errno работа идет.

eao197 комментирует...

2Rustam:

>ведь в таком языке (без C++ раскрутки стека)

Ну там все-таки есть некий аналог D-шного scope(exit) под названием defer. Так что раскрутка стека там все-таки есть.

А не сделали исключений в Go, вероятно, по причине глубоких C-шных корней у авторов языка. Ну или во всем Google в принципе не любят исключений -- в C++ запрещают использовать, в Go их не сделали ;)