LINUX.ORG.RU

Аналог стандартной библиотеки C++ для работы без исключений

 , ,


0

3

Приветствую.

Понадобилось сделать небольшой проектик, в котором есть специфическое требование: сборка с флагами -fno-rtti, -fno-exceptions (gcc). В таком режиме бОльшая часть стандартной библиотеки превращается в тыкву. Вместе с тем, как мне кажется, если немного изменить интерфейс контейнеров, то их можно было бы использовать.

Например, возьмём vector и пофантазируем: у него можно оставить только конструктор по-умолчанию, move-конструктор, а операции типа resize() сделать не void и кидающими исключения, а bool и возвращающими false.

Не натыкался ли кто-нибудь на реализацию такой или похожей идеи? То есть: библиотеку контейнеров, которая по возможности копировала бы интерфейс стандартной, но при этом была пригодна для использования в режиме полного отсутствия механизма исключений.

★★

STLport кажись умел такое.

dib2 ★★★★★
()

оставить только конструктор

А если конструктор обломается выделить память, то он должен просто грохнуться?

no-such-file ★★★★★
()

В таком режиме бОльшая часть стандартной библиотеки превращается в тыкву.

Да ладно. Если пользовательский код не кидает исключений, то там мало что их кидает (по соотношению «сущности, которые делают throw»/«количество всех сущностей»).

xaizek ★★★★★
()

Небольшой проектик с такими требованиями должен принести как минимум кило золота. Хотя плюсисты знатные копрофилы, это все знают.

anonymous
()
Ответ на: комментарий от no-such-file

А если конструктор обломается выделить память

Конструктор ПО УМОЛЧАНИЮ. Который без аргументов, который не выделяет память и не резервирует её.

uuwaan ★★
() автор топика

Есть https://github.com/msharov/ustl

И, насколько я помню, это не единственная такая попытка. Вроде бы были еще реализации STL для embedded-разработки. Только вот забыл, как конкретный проект на эту тему назывался.

eao197 ★★★★★
()
Ответ на: комментарий от uuwaan

который не выделяет память и не резервирует её

А на себя ему ничего не надо? К тому же, bool resize(...) это конечно здорово, а вот как быть с простым v[100500]=42, тоже bool возвращать?

no-such-file ★★★★★
()
Ответ на: комментарий от xaizek

мало что их кидает

Назови, пожалуйста, хоть один контейнер кроме array, который можно использовать в условиях, когда у тебя нет bad_alloc.

uuwaan ★★
() автор топика

Ну так пиши просто на С.

UVV ★★★★★
()

Как ты будешь возвращать ошибки при конструировании элементов? Предположим у тебя есть вектор, ты делаешь ему assign, память выделилась без проблем, где-то в середине копирование элемента обломилось.
1) Как ты вообще поймёшь, что оно обломилось?
2) Как ты прокинешь эту информацию наружу?

Там много таких моментов вылезает. В конце концов тебе придётся создавать свою объектную систему с жёстко фиксированными интерфейсами и прочими радостями. Оно точно стоит того?

Gvidon ★★★★
()
Последнее исправление: Gvidon (всего исправлений: 1)
Ответ на: комментарий от uuwaan

Конструктор ПО УМОЛЧАНИЮ
который не выделяет память и не резервирует её.

странное у вас понимание конструктора по-умолчанию. вероятно имеется ввиду placement new?

dib2 ★★★★★
()
Ответ на: комментарий от dib2

Какое отношение placement new имеет к конструкторам по умолчанию?

Gvidon ★★★★
()
Последнее исправление: Gvidon (всего исправлений: 1)
Ответ на: комментарий от Gvidon

Тут я руководствуюсь следующими соображениями: 1) копирование POD-типа не обламывается никогда 2) если это мой нетривиальный класс, то я не буду делать ему нетривиальные operator= и конструктор копирования/перемещения в ситуации, когда у меня нет исключений, потому что нет способа вернуть ошибку

В таком случае assign для нетривиально копируемых типов не скомпилируется, так как нет нужных методов, а копирование их сделаем сами, грубо говоря, через find_if.

uuwaan ★★
() автор топика
Ответ на: комментарий от Oxdeadbeef

Я бы писал на подмножестве C++ «C с классами»

Шаблоны-то чем не угодили? Одно из основных преимуществ С++ над С.

Gvidon ★★★★
()
Ответ на: комментарий от Gvidon

Сами контейнеры никаких исключений не кидают. В том числе и bad_alloc, его кидает аллокатор.

Но они ЖДУТ, что аллокатор либо даст им указатель, либо возбудит исключение. Поэтому не получится написать небросающий аллокатор и подсунуть его под контейнер.

uuwaan ★★
() автор топика

Например, возьмём vector и пофантазируем: у него можно оставить только конструктор по-умолчанию, move-конструктор, а операции типа resize() сделать не void и кидающими исключения, а bool и возвращающими false.

Заменить все экзепшны жирным интом с состоянием и опрашивать по маске... и получить Си только с плюсами в названии.

vector<int> t(50);
if(t.exception & exception::bad_alloc) {
  ...
}
invy ★★★★★
()
Ответ на: комментарий от uuwaan

Насколько я помню, в libstdc++ все «try» и «catch» заменены на макросы, которые в случае отключения исключений ничего не делают.

Gvidon ★★★★
()
Ответ на: комментарий от Gvidon

Шаблоны-то чем не угодили? Одно из основных преимуществ С++ над С.

я их тоже имел ввиду.

Oxdeadbeef ★★★
()
Ответ на: комментарий от invy

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

Gvidon ★★★★
()
Ответ на: комментарий от Gvidon

Почему? Хранить там можно будет что хочется, просто вместо раздолбайского «а, ща этот вектор этому присвоим не глядя» будет более ответственное:

vector v2;

if (!v2.resize(v1.size())) {
 // ошибка
}

auto elem = find_if_not(v1.begin(), v1.end(), v2.begin(), nonTrivialCopyOk);
if (elem != v1.end()) {
// копирование не выполнено, elem указывает на сбойнувший элемент
}

uuwaan ★★
() автор топика
Ответ на: комментарий от uuwaan

Т.е. в самом векторе ты продолжишь копирование даже если несколько элементов отвалились? Ну, можно наверное.

Gvidon ★★★★
()
Ответ на: комментарий от Gvidon

find_if_not останавливается сразу как только предикат станет false. Я либо успешно скопирую весь вектор, либо у меня будет частично заполненный вектор с хорошими копиями и указатель на элемент, который вызвал ошибку копирования. Ни о каком ихом продолжении работы речи не идёт, и копирование идёт вообще вне класса вектора, на моей стороне, где я контролирую процесс.

uuwaan ★★
() автор топика
Последнее исправление: uuwaan (всего исправлений: 1)
Ответ на: комментарий от uuwaan

Моя твоя не понимай. Во-первых, у тебя какая-то своя особая версия find_if_not, неизвестная науке. Может, ты имеешь в виду copy_if? Но он не так работает. Короче, разверни мысль.

Gvidon ★★★★
()
Ответ на: комментарий от invy

Буду работать в стиле C, через operator[], а at() выкину из класса. Невозможно, не имея исключений, воссоздать интерфейс стандартной библиотеки. Но можно сделать его очень похожим, что, на мой взгляд, будет плюсом.

uuwaan ★★
() автор топика
Ответ на: комментарий от Gvidon

Смотрим здесь. Раздел «Possible implementation». Там приведён код:

template<class InputIt, class UnaryPredicate>
InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q)
{
    for (; first != last; ++first) {
        if (!q(*first)) {
            return first;
        }
    }
    return last;
}

Как мы видим, алгоритм идёт строго от начала к концу, сразу останавливаясь по предикату. Важно отметить, что стандарт C++ в части алгоритмов ОБЯЗЫВАЕТ реализацию библиотеки вести себя так же, как эталонная. Опимизируй что хочешь как хочешь, но изволь всё же идти слева направо и останавливаться сразу.

uuwaan ★★
() автор топика

Go on: detail the standard library support for -fno-exceptions.

In sum, valid C++ code with exception handling is transformed into a dialect without exception handling. In detailed steps: all use of the C++ keywords try, catch, and throw in the standard library have been permanently replaced with the pre-processor controlled equivalents spelled __try, __catch, and __throw_exception_again. They are defined as follows.

#if __cpp_exceptions
# define __try      try
# define __catch(X) catch(X)
# define __throw_exception_again throw
#else
# define __try      if (true)
# define __catch(X) if (false)
# define __throw_exception_again
#endif

In addition, for every object derived from class exception, there exists a corresponding function with C language linkage. An example:

#if __cpp_exceptions
  void __throw_bad_exception(void)
  { throw bad_exception(); }
#else
  void __throw_bad_exception(void)
  { abort(); }
#endif

The last language feature needing to be transformed by -fno-exceptions is treatment of exception specifications on member functions. Fortunately, the compiler deals with this by ignoring exception specifications and so no alternate source markup is needed.

By using this combination of language re-specification by the compiler, and the pre-processor tricks and the functional indirection layer for thrown exception objects by the library, libstdc++ files can be compiled with -fno-exceptions.

User code that uses C++ keywords like throw, try, and catch will produce errors even if the user code has included libstdc++ headers and is using constructs like basic_iostream. Even though the standard library has been transformed, user code may need modification. User code that attempts or expects to do error checking on standard library components compiled with exception handling disabled should be evaluated and potentially made conditional.

https://gcc.gnu.org/onlinedocs/libstdc /manual/using_exceptions.html

asaw ★★★★★
()
Ответ на: комментарий от uuwaan

Что вот «решение» твоей задачи... при помощи пары костыликов.

invy ★★★★★
()
Ответ на: комментарий от invy

Нет. Их потому и два метода, что один проверяет и кидается, а второй не проверяет ничего. Если operator[] кидает исключение, это значит, что реализация библиотеки не соответствует стандарту.

uuwaan ★★
() автор топика
Ответ на: комментарий от asaw

Оттуда следует, что при -fno-exceptions конструкции кода, которые отвечают за исключения, меняются на подпорки, чтобы этот код скомпилировался. Оттуда НЕ следует, что контейнеры и другие классы стандартной библиотеки в отсутствие исключений начинают вести себя по-другому, адаптируясь к ситуации. Там просто при возникновении исключения абортируется вся программа целиком. Как говорится, нет человека – нет проблемы.

uuwaan ★★
() автор топика
Ответ на: комментарий от Gvidon

Это я просто отвлёкся и ошибся в коде. Спасибо что заметил.

auto elem = find_if_not(v1.begin(), v1.end(), nonTrivialCopy(v2));
if (elem != v1.end()) {
// копирование не выполнено, elem указывает на сбойнувший элемент
}

А nonTrivialCopy – это соответственно функтор, который знает о v2 и копирует перебираемые элементы из v1.

uuwaan ★★
() автор топика
Последнее исправление: uuwaan (всего исправлений: 1)
Ответ на: комментарий от uuwaan

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

Тебе нужны гарантии того, что стандартная библиотека при этом остается 100% рабочей или что? Вот в следующем абзаце про ограничения:

Some issues remain with this approach (see bugzilla entry 25191). Code paths are not equivalent, in particular catch blocks are not evaluated. Also problematic are throw expressions expecting a user-defined throw handler. Known problem areas in the standard library include using an instance of basic_istream with exceptions set to specific ios_base::iostate conditions, or cascading catch blocks that dispatch error handling or recovery efforts based on the type of exception object thrown.

asaw ★★★★★
()
Ответ на: комментарий от asaw

Мне не нужна исходная стандартная библиотека. Мне нужна сторонняя-библиотека-заменитель, которая была бы похожа на стандартную по строению, но работающая в среде, где не работают исключения.

uuwaan ★★
() автор топика
Ответ на: комментарий от uuwaan

Назови, пожалуйста, хоть один контейнер кроме array, который можно использовать в условиях, когда у тебя нет bad_alloc.

Любой контейнер. У тебя для этого есть 2 возможности: 1. Использовать свои аллокаторы; 2. Переопределить операторы new и delete: http://en.cppreference.com/w/cpp/memory/new/operator_new Делай их такими, чтоб они сообщали об ошибках как тебе нужно. Какие проблемы?

asaw ★★★★★
()
Ответ на: комментарий от asaw

Расскажи, как будешь переопределять. На случай, если ты не совсем понимаешь о чём говоришь, поясню: вышележащий контейнер ждёт от тебя ТОЛЬКО валидный указатель на память, вернуть NULL ты не можешь, бросить исключение ты не можешь, убить всю программу целиком тоже не можешь. С set_new_handler то же самое. В случае отказа выделения памяти хэндлер может только либо кинуть исключение, либо убить программу целиком.

uuwaan ★★
() автор топика
Ответ на: комментарий от uuwaan

убить всю программу целиком тоже не можешь

Конечно не можешь, во всяких линуксах к тебе еще раньше придет белая лисичка дядя OOM Killer и прибьет ее сам.

anonymous
()
Ответ на: комментарий от anonymous

К сожалению, «всякими линуксами» разработка ПО не ограничивается.

Gvidon ★★★★
()

в условиях небольшого проектика, бед аллоками и нулевыми указателями от malloc можно принебречь на линкусах, тем более если за ранее известно сколько памяти будет потреблять софт, вообще дожить до момента когда линукс тебе вернет НУЛЛ в malloc, это мало вероятно, либо должен был зачетный говнокод что бы сожрать абсолютно всю память и своп

anonymous
()
Ответ на: комментарий от anonymous

Нет, не только. Можно ещё долго работать, создавать всякие объектики, удалять их. В результате у тебя память будет «в дырочку», и тут ты просишь ну скажем мегабайт. А у тебя нет нигде непрерывного блока размером в мегабайт. И тут-то и malloc и operator new и скажут тебе: «Извиняй, хозяина, нету!». А так-то свободная память есть, чо.

uuwaan ★★
() автор топика
Ответ на: комментарий от uuwaan

вернуть NULL ты не можешь, бросить исключение ты не можешь, убить всю программу целиком тоже не можешь.

Убить программу целиком ты можешь всегда.

С set_new_handler то же самое. В случае отказа выделения памяти хэндлер может только либо кинуть исключение, либо убить программу целиком.

new_handler может поскрести по сусекам. И вот как раз с помошью этого и можно выкрутиться.

asaw ★★★★★
()
Ответ на: комментарий от asaw

И вот как раз с помошью этого и можно выкрутиться.

Можно ПОПЫТАТЬСЯ выкрутиться. Может, найдёшь сколько нужно, а может нет. И вот эта ветка с обломом возвращает нас к исходному вопросу.

uuwaan ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.