Обработка ошибок в деструкторе своего умного указателя
От: Sm0ke Россия ksi
Дата: 23.12.22 06:04
Оценка:
Всем доброго времени суток.
Написал свой класс умного указателя. В нём есть приватный метод clear(), в котором использую std::set и std::list как тип локальных переменных. Их значения не выходят за пределы этого метода, а служат просто для сбора данных.

Собственно при работе с этой динамической памятью могут быть выкинуты исключения, которые я в самом clear() не ловлю. Но мне нужно вызвать clear() из деструктора класса.

Пока что внутри деструктора Обернул вызов clear() в try, и при catch пишу инфу в статическое свойство s_state — структура с полем std::exception_ptr.

Это решение мне не очень нравится, ведь надо думать на каком моменте проверять этот статик s_state и как на это реагировать. А при разрушении контейнера с моими указателями он может перезатераться...

Посоветуйте как лучше сделать? Не кидать же исключения из деструктора?
Кста: нужен-ли язык, где Деструктор может вернуть значение?

Доп инфа: в некоторых случаях clear() удаляет сам target object без локальных list set, в других вызывает у таргета unset(), но сперва типо убирает своего хоста у target.
Re: Обработка ошибок в деструкторе своего умного указателя
От: sergii.p  
Дата: 23.12.22 08:56
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Посоветуйте как лучше сделать? Не кидать же исключения из деструктора?


Чего? Я кидаю

Проверяем на std::uncaught_exceptions() и если 0 то бросаем своё исключение. Если не 0, то начинаем извращаться с логированием ошибки. Но вообще не унываем и надеемся на лучшее.
Re: Обработка ошибок в деструкторе своего умного указателя
От: rg45 СССР  
Дата: 23.12.22 09:18
Оценка: +1
Здравствуйте, Sm0ke, Вы писали:

S>Посоветуйте как лучше сделать? Не кидать же исключения из деструктора?


Боюсь, тут общего хорошего решения не существует. Обработка ошибок в деструкторах вообще вещь проблемная, какое бы решени ты ни выбрал у него будут какие-то минусы. Возможно, тебе подойдет вариант вообще никак не обрабатывать информацию об ошибке, просто блокировать исключение и все. По крайней мере, не накрутишь костылей, об которые потом все будут спотыкаться.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Обработка ошибок в деструкторе своего умного указателя
От: andyp  
Дата: 23.12.22 09:50
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Пока что внутри деструктора Обернул вызов clear() в try, и при catch пишу инфу в статическое свойство s_state — структура с полем std::exception_ptr.

S>Это решение мне не очень нравится, ведь надо думать на каком моменте проверять этот статик s_state и как на это реагировать. А при разрушении контейнера с моими указателями он может перезатераться...

Т.е. внутри деструктора в определенной точке тебе надо убедиться, что бросил твой clear а не просто что ты находишься в процессе stack unwinding или нормального завершения? Тогда на std::uncought_exceptions() и всякие scоpe guard посмотри.

S>Посоветуйте как лучше сделать? Не кидать же исключения из деструктора?

S>Кста: нужен-ли язык, где Деструктор может вернуть значение?

Боже упаси
Re[2]: Обработка ошибок в деструкторе своего умного указателя
От: Sm0ke Россия ksi
Дата: 23.12.22 13:28
Оценка:
Здравствуйте, andyp, Вы писали:

A>Здравствуйте, Sm0ke, Вы писали:


S>>Пока что внутри деструктора Обернул вызов clear() в try, и при catch пишу инфу в статическое свойство s_state — структура с полем std::exception_ptr.

S>>Это решение мне не очень нравится, ведь надо думать на каком моменте проверять этот статик s_state и как на это реагировать. А при разрушении контейнера с моими указателями он может перезатераться...

A>Т.е. внутри деструктора в определенной точке тебе надо убедиться, что бросил твой clear а не просто что ты находишься в процессе stack unwinding или нормального завершения? Тогда на std::uncought_exceptions() и всякие scоpe guard посмотри.


Сам clear() напрямую ничего не кидает. Но в нём set и list могут бросить.

Вот код деструктора:
try {
  clear();
} catch( ... ) {
  s_state.m_target = m_target;
  s_state.m_exception = std::current_exception();
}
Re[3]: Обработка ошибок в деструкторе своего умного указател
От: andyp  
Дата: 23.12.22 14:20
Оценка:
Здравствуйте, Sm0ke, Вы писали:


S>Сам clear() напрямую ничего не кидает. Но в нём set и list могут бросить.


Ну да, это и имел в виду. Вместо try-catch надо сделать scope guard, который в конструкторе запомнит std::uncaught_exceptions() перед вызовом clear, а после, в своем деструкторе, почистит что надо, если счетчик std::uncaught_exceptions() увеличился.

Пример такой штуки есть в https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf
Отредактировано 23.12.2022 14:39 andyp . Предыдущая версия .
Re[3]: Обработка ошибок в деструкторе своего умного указателя
От: flаt  
Дата: 23.12.22 14:50
Оценка:
Здравствуйте, Sm0ke, Вы писали:



S>Сам clear() напрямую ничего не кидает. Но в нём set и list могут бросить.


По какой именно причине они могут бросить исключение?
Re: Обработка ошибок в деструкторе своего умного указателя
От: B0FEE664  
Дата: 23.12.22 16:22
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Всем доброго времени суток.

S>Написал свой класс умного указателя. В нём есть приватный метод clear(), в котором использую std::set и std::list как тип локальных переменных. Их значения не выходят за пределы этого метода, а служат просто для сбора данных.
S>Собственно при работе с этой динамической памятью могут быть выкинуты исключения, которые я в самом clear() не ловлю. Но мне нужно вызвать clear() из деструктора класса.
S>Пока что внутри деструктора Обернул вызов clear() в try, и при catch пишу инфу в статическое свойство s_state — структура с полем std::exception_ptr.

Допустим исключение было поймано. Как ваше приложение должно на него отреагировать? Что именно следует сделать?

Если моя телепатия меня не подводит, то единственным разумным решением будет поймать исключение и сделать запись о том, что сбор статистики невозможен ввиду отсутствия достаточного количества доступной памяти. После чего продолжить выполнение программы.
И каждый день — без права на ошибку...
Re[4]: Обработка ошибок в деструкторе своего умного указател
От: Sm0ke Россия ksi
Дата: 23.12.22 17:55
Оценка:
Здравствуйте, andyp, Вы писали:

A>Здравствуйте, Sm0ke, Вы писали:



S>>Сам clear() напрямую ничего не кидает. Но в нём set и list могут бросить.


A>Ну да, это и имел в виду. Вместо try-catch надо сделать scope guard, который в конструкторе запомнит std::uncaught_exceptions() перед вызовом clear, а после, в своем деструкторе, почистит что надо, если счетчик std::uncaught_exceptions() увеличился.


A>Пример такой штуки есть в https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf


Что мне это даст? Я должен решить вызывать ли метод unset() у m_target или нет. Но это будет известно только когда clear() отработает до конца.
Re[4]: Обработка ошибок в деструкторе своего умного указателя
От: Sm0ke Россия ksi
Дата: 23.12.22 18:03
Оценка:
Здравствуйте, flаt, Вы писали:

F>Здравствуйте, Sm0ke, Вы писали:




S>>Сам clear() напрямую ничего не кидает. Но в нём set и list могут бросить.


F>По какой именно причине они могут бросить исключение?


Я в них добавляю элементы. Соотв вызывается метод std::allocator<T>::allocate(), который может кинуть std::bad_alloc
Re[2]: Обработка ошибок в деструкторе своего умного указател
От: Sm0ke Россия ksi
Дата: 23.12.22 18:19
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, Sm0ke, Вы писали:


S>>Всем доброго времени суток.

S>>Написал свой класс умного указателя. В нём есть приватный метод clear(), в котором использую std::set и std::list как тип локальных переменных. Их значения не выходят за пределы этого метода, а служат просто для сбора данных.
S>>Собственно при работе с этой динамической памятью могут быть выкинуты исключения, которые я в самом clear() не ловлю. Но мне нужно вызвать clear() из деструктора класса.
S>>Пока что внутри деструктора Обернул вызов clear() в try, и при catch пишу инфу в статическое свойство s_state — структура с полем std::exception_ptr.

BFE>Допустим исключение было поймано. Как ваше приложение должно на него отреагировать? Что именно следует сделать?


Метод clear() должен решить следует ли вызвать m_target->unset(). Чтобы целевой объект корректно очистился, при необходимости.
Для этого в set<> кладутся возможные тупики, а list хранит пару итераторов для обхода. Есть спец условие досрочной остановки обхода (в этом случае unset() не вызывается). Но когда всё обошли, то нужно вызвать unset() из clear().

BFE>Если моя телепатия меня не подводит, то единственным разумным решением будет поймать исключение и сделать запись о том, что сбор статистики невозможен ввиду отсутствия достаточного количества доступной памяти. После чего продолжить выполнение программы.


При исключении нельзя сказать наверняка следует ли вызвать этот unset(). Что может привести к утечке памяти (а может и нет).
Думаю сделаю счётчик possible_leak_count, чтобы в конце записать его в лог.

Ещё мб придётся в таргете хранить позицию строчки где он был создан... Это потеря производительности И больше памяти
Отредактировано 23.12.2022 18:22 Sm0ke . Предыдущая версия . Еще …
Отредактировано 23.12.2022 18:20 Sm0ke . Предыдущая версия .
Re[5]: Обработка ошибок в деструкторе своего умного указател
От: andyp  
Дата: 23.12.22 18:52
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Что мне это даст? Я должен решить вызывать ли метод unset() у m_target или нет. Но это будет известно только когда clear() отработает до конца.


Даст возможность отдельно обработать ситуацию, когда что-то вылетело из clear


template<typename Callable>
class my_scope_failure_guard{
    Callable _ftn;
    int _cnt;    
public:
    my_scope_failure_guard(Callable f):_ftn(f), _cnt(std::uncaught_exceptions()){}
    ~my_scope_failure_guard()
    {
        if(std::uncaught_exceptions() > _cnt ) _ftn();        
    }
};

~MySmartyPtr()
{

    try{
    //здесь не важно, пришли мы сюда, раскручивая стек или просто при вызове деструктора,
    //можно снова кидать
        {
          my_scope_failure_guard guard([&](){unset();});    
          clear();
          //лямда вызовется здесь, только если исключение вылетело из clear().    
        }
        //здесь еще можно кидать         
    }
    catch(...)
    {
    
    
    }

}
Отредактировано 23.12.2022 18:55 andyp . Предыдущая версия .
Re[3]: Обработка ошибок в деструкторе своего умного указател
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 24.12.22 04:22
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Ещё мб придётся в таргете хранить позицию строчки где он был создан... Это потеря производительности И больше памяти

Смело терминейт делай и умирай на бэд аллок. Если памяти нет, то ловить больше нечего, скорее всего всё очень плохо в системе, а приложению надо закрыться. Другое дело если конструктор копирования внутри твоих листов и сетов кинул исключение, то тут.. ну выставить тогда каунтеры/флаги/логи ошибок свои. Попробуй ещё пересмотреть свой класс и структуры, возможно изначально неверное проектирование было, например, есть проблемы с владением/созданием объектов из-а чего пришлось городить огород чтобы в деструкторе было вот так.
Sic luceat lux!
Re[5]: Обработка ошибок в деструкторе своего умного указателя
От: flаt  
Дата: 24.12.22 09:04
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Я в них добавляю элементы. Соотв вызывается метод std::allocator<T>::allocate(), который может кинуть std::bad_alloc


Это теория, которая в жизни не проявляется — можно и аборт кинуть.

Ты же аллокатор пишешь? Или лишь смартпоинтер? В конструкторе выделяй нужную память (если вылетит исключение — в конструкторе это ок), а в деструкторе работай с ней.

А вообще, гадать можно долго. Более подробно опиши саму задачу.
Re[4]: Обработка ошибок в деструкторе своего умного указател
От: qaz77  
Дата: 24.12.22 11:36
Оценка:
Здравствуйте, Kernan, Вы писали:
K>Смело терминейт делай и умирай на бэд аллок. Если памяти нет, то ловить больше нечего, скорее всего всё очень плохо в системе, а приложению надо закрыться.

Как раз bad_alloc не показатель, что памяти нет и все караул.
Если запросить большой непрерывный блок, то из-за фрагментации адресного пространства можно получить bad_alloc и при наличии свободных гигабайтов.
Просто не нашлось непрерывного куска в 200 мб...
Re[5]: Обработка ошибок в деструкторе своего умного указател
От: Sm0ke Россия ksi
Дата: 24.12.22 17:08
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Здравствуйте, Kernan, Вы писали:

K>>Смело терминейт делай и умирай на бэд аллок. Если памяти нет, то ловить больше нечего, скорее всего всё очень плохо в системе, а приложению надо закрыться.

Q>Как раз bad_alloc не показатель, что памяти нет и все караул.

Q>Если запросить большой непрерывный блок, то из-за фрагментации адресного пространства можно получить bad_alloc и при наличии свободных гигабайтов.
Q>Просто не нашлось непрерывного куска в 200 мб...

Какой непрерывный кусок в set и list? В list у меня там pair<iterator, iterator>, в set тупо указатель.
Re[5]: Обработка ошибок в деструкторе своего умного указател
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 25.12.22 21:36
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Как раз bad_alloc не показатель, что памяти нет и все караул.

Q>Если запросить большой непрерывный блок, то из-за фрагментации адресного пространства можно получить bad_alloc и при наличии свободных гигабайтов.
Ты такое встречал хоть раз не на велосипедных аллокаторах которые не умеют такое обрабатывать?
Q>Просто не нашлось непрерывного куска в 200 мб...
Отлично, отлично... а теперь придумай что в этом случае делать? Вот надо что-то сделать же, да? Вот напиши 3 вариант кроме падения с терминейтом.
Sic luceat lux!
Re[6]: Обработка ошибок в деструкторе своего умного указател
От: qaz77  
Дата: 26.12.22 08:30
Оценка:
Здравствуйте, Kernan, Вы писали:

Q>>Как раз bad_alloc не показатель, что памяти нет и все караул.

Q>>Если запросить большой непрерывный блок, то из-за фрагментации адресного пространства можно получить bad_alloc и при наличии свободных гигабайтов.
K>Ты такое встречал хоть раз не на велосипедных аллокаторах которые не умеют такое обрабатывать?

Конечно, встречал.
std::vector<int> v(10000000, 0);

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

Q>>Просто не нашлось непрерывного куска в 200 мб...

K>Отлично, отлично... а теперь придумай что в этом случае делать? Вот надо что-то сделать же, да? Вот напиши 3 вариант кроме падения с терминейтом.

Если программа может взаимодействовать с пользователем, например через GUI, то можно сказать что-то типа "недостаточно памяти для выполнения операции",
"попробуйте задать более узкий фильтр для обработки данных" и т.п.
В целом, ситуация аналогична "недостаточно места на диске".

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

А может просто пользователь начудил и задал какие-то огромные значения входных параметров, программа ему говорит "не шмогла", а он такой — ну и ладно, задам параметры поменьше.

Непонятно, зачем именно bad_alloc так демонизировать, что сразу в терминейт...
Re: optr
От: Sm0ke Россия ksi
Дата: 27.12.22 21:10
Оценка:
Идея такова. Есть любая сеть разных типов.
Любой тип может иметь свойства как указатели на промежуточное значение value. Оно может быть скаляр, или указатель на compound.
Ну compound — любой тип.

Программа работает с этим, допустим интерпретатор, и нужно вовремя удалять объект, на который может несколько ptr быть.
compound со стаким ptr будет иметь поле owner m_host. ptr знает указатель на хост. compound в памяти не двигается. Это условие использование модуля.

текущий исходник пока не тестировал.

  схемка
Re[2]: ring
От: Sm0ke Россия ksi
Дата: 27.12.22 21:15
Оценка:
ring нужен для случаев исключений, брошенных из функции clear(), вызванной в дуструкторе.
В такой ситуации сцепляется compound с кольцом, через raw указатели m_prev m_next обязательно в начало (думаю).

Кольцо может быть static as default или локальная переменная, а в прочем... Но оно одно на всю сеть!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.