Pull to refresh

Comments 36

Я бы в своём проекте такое бы не использовал.

Обработка ошибок должна быть простой и понятной, а здесь не каждый средний программист вообще поймёт, как оно работает.
Конечно, без крайней необходимости прибегать к такому я бы тоже не стал. Если проектировать систему с нуля, то такие вещи нужно прогнозировать. Однако у меня был уже готовый проект, который нужно было портировать на платформу, где QtXML (именно она и есть та С++ xml-библиотека) не существовало в природе, а был только expat.
И что на тебя наслать в таком случае, сын мой?
Си, кстати, более приятный язык. Да и Си++ не так страшен, только странный он немного. Но привыкнуть можно, при необходимости.
писал я на си/си++
после явы не хочется возвращаться в эти непроходимые доисторические джунгли
А можно поподробнее описать то, какая всё же проблема решается. Что-то совсем не ясно, откуда в коде на Си берутся исключения… А если откуда-то берутся, то какие проблемы с их вылавливанием? Механизм же исключений специально разработан так, чтобы и с библиотеками проблем не было — всякие развёртки стека, всё такое. В чём конкретно проблема?
Ну, давайте немного упростим ситуацию. Представьте, что вы пишете на языке Си и подключаете к проекту библиотеку написанную на С++. Библиотека эта, предположим, экспортирует функции, помеченные как extern «C», то есть такие функции свободно можно из Си вызывать. Так вот. Теперь представьте, что одна из таких функций бросила исключение. Куда оно попадет? И какие в Си есть возможности для его перехвата?
Пример на Си:
void foo(int (*callback)(char *))
{
char * buffer;
int ret;

buffer = malloc(100000);
ret = callback(buffer);
free(buffer);
}

Если сallback — это функция из С++, то она может бросить исключение. При этом будет утечка памяти, так как buffer не будет освобожден.
UFO landed and left these words here
> А зачем бросила?
Предлагается решение, при котором такого вопроса бы не возникало. Исключение не должно покидать пределы С++.

> Предлагается костыль для кривых библиотек?
В C++ исключения — это нормальное явление, new кидает исключение, dynamic_cast кидает искючения, STL кидает исключения. Кривизна библиотеки будет только тогда, когда она позволит исключению попасть в чужую среду.
Как код на Си узнает, что есть проблемы? Если вызванная им функция вернула заведомо неправильный результат?
Так он и не знает. В этом и проблема. И при исключении функция не возращает никакого результата. Она прерывается альтернативно с раскруткой стека. Как это реализовано конкретно — зависит от компилятора.
UFO landed and left these words here
Значит библиотеки boost или stl по-вашему ошибочны по своей природе, раз позволяют исключениям передаваться за свои границы?
так boost и stl — это не библиотеки для Си.
Ну так а я не говорю про С. Я говорю для С++, почему бы не прочитать вопрос habrahabr.ru/blogs/cpp/100623/#comment_3113697, прежде чем дискуссию начинать? :) Я объяснял, что код который вызывается из Си не защищен от С++-исключений. В случае callback`ов в Си библиотеках, это тоже самое, что если бы код на Си вызывал С++ код.

Причем я как раз отметил чем это чревато и почему нельзя такого допускать.

В чем идея? Есть либина XML, к ней есть С++ враппер. С++ враппер С++ библиотека? Да. Почему бы не использовать вместе с ней исключения. Однако в недрах враппера юзаются коллбэки, который выполняются в Си коде. Потому что враппер — над С-XML-либиной. Так как разрешить проблему? СС одной стороны как бы юзер враппера не виноват, он же юзает С++, с другой стороны ему вдруг нельзя использовать исключения. Это решение этой проблемы.
UFO landed and left these words here
И где противоречие с тем, что я написал?
UFO landed and left these words here
Так он это и делает вообще-то. Вы статью читали?
UFO landed and left these words here
Как раз таки не через Си-код. А в обход его. Задача во-первых не допустить попадания в Си-код исключений и нарушения контракта. Во-вторых оставить для пользователя библиотеки возможность их генерировать (в моем случае неизбежно, потому что код уже написан).

Смысл «протаскивания» в том, что обработка исключения должна быть в том месте, где это нужно. Если переносить всю обработку в код враппера — это будет прямое нарушение инкапсуляции. Враппер просто не может знать об особенностях клиентского кода.
я Вам завидую если Вы работаете только с библиотеками в которых нет ошибок
UFO landed and left these words here
Хорошо. Это понятно, но не понятно, как это всё связано с MyLib. И всё-равно общая идея не очень понятно. То есть, если мне надо написать какой-то callback на Си++ и передать его в код на Си… Ну и хорошо, я напишу нужную функцию, отловлю в ней стандартными методами все исключения интересные…

Или речь о том, чтобы запомнить все эти исключения? Ну. Хорошо. А что мне мешает просто занести их в некий список, зачем все эти шаблоны и хитрый try_?

Я, конечно, туплю в последнее время, может быть это у Вас где-то и написано, но понимание не приходит ко мне сего факта.
Да, основная идея в том, чтобы код, который использует MyLib не вынуждать отказываться от исключений. Чтобы этот код претерпел как можно меньше изменений. Для этого надо их где-то запомнить, а прежде чем запомнить, надо их отфильтровать.

Шаблоны — для удобстве фильтрации. Если в mylib не один callback, а 10, и в каждом callback`е написано нечто вроде:
try
{
this_->callback();
}
catch(exception1)
{
}
catch(exceptionN)
{
}

Особенно если нужно добавить новое исключение, все это превращается в жуткую кашу. А у меня достаточно в одном месте дописать нужный тип в список исключений.
Извиняюсь, если задаю глупый вопрос, но почему вы одновременно заботитесь о переносимости программы («Нельзя переносимым способом получить в блоке catch(...) информацию о типе исключения») и в то же время используете reinterpret_cast, который является непортируемой штукой?
В данном контексте с ним все в порядке. Я сам передал указатель this, скастив его к void*, сам его же и забрал и скастил обратно.
pureCLibSetUserData(this);

//...

MyLib * this_ = reinterpret_cast<MyLib*>(userData);

Очевидное решение, именно то, которое я встречал чаще всего, когда только начал заниматься этим вопросом, состоит в полном отказе от исключений в таких callback`ах. Но это, вестимо, не наш случай.


А чем конкретно вас не устроил общепринятый подход, что потребовалось изобретать свой велосипед с не совсем круглыми колесами?
Цитата из статьи:

> Я не зря привел в пример expat. Проблема исключений встала именно при использовании этой
> библиотеки. Задача состояла в следующем:
> * Есть некий код, который раньше использовал библиотеку xml, написанную на С++ и этот код
> активно применял исключения;
> * Есть необходимость заменить библиотеку xml на другую без переписывания остального кода.
а почему вы не можете просто перекомпилировать си-библиотеку в си++?
Даже если я это сделаю — она (си-библиотека) же не написана с учетом exception-safety.
да нет вопросов, сейчас только код выкупим…
если вы используете проприетарный код, то как вы решаете проблему с отладкой? ведь трудно поверить, что все используемые вами библиотеки не содержат ни единой ошибки. а раз так, то иногда возможность заглянуть в сторонний код оказывается очень кстати.
Ещё пример задачи, где приходилось использовал похожий приём с рекурсивным шаблоном: интеграция С-Javascript движка (SpiderMonkey) в С++ приложение. Исключения протаскивались через движок из обратных вызовов. Также, выбросы из Javascript-кода преобразовывались в С++ исключения. И обработчик в скрипте мог ловить С++-исключения по желанию. Красота.
Only those users with full accounts are able to leave comments. Log in, please.