powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / exception-safe код
16 сообщений из 16, страница 1 из 1
exception-safe код
    #38395244
TimeCoder
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Привет, народ!
Тема обширная, понимаю, но хотя бы азы затронуть интересно. В частности,
Код: plaintext
1.
2.
3.
4.
5.
6.
void Swap(Foo& a, Foo& b)
{
    Foo t(a);
    a = b;                 
    b = t;
}


Допустим, a=b бросит исключение (в операторе присваивания), получается, что в a уже записали результат, в b - нет. Как гарантировать, что swap либо не выполнится вообще, либи полностью? Гуглил, нашел какие-то невнятные примеры со спецификацией исключений (которая не везде работает, и тот же Саттер не рекомендует ею вообще пользоваться).
...
Рейтинг: 0 / 0
exception-safe код
    #38395279
sherzod_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
TimeCoder,

То чего вы хотите добиться называется "сильной гарантией безопасности по исключениям". Во-первых вам нужно понять нужно ли это вообще, так как затратно. Возможно хватит и "слабой". (Поищите статью David Abrahams по сабжу, а так же есть глава из книжки Страуструпа, после прочтения этих двух источников все станет ясно).

Чтобы добиться "сильной гарантии" нужно чтобы любые используемые операции (конструкторы всякие, присваивания, все все) давали сильную гарантию. Конструкторы и присваивание обычно дают сильную гарантию. Если же нет, а так же в вашем случае нужно заворачивать каждую строчку кода в try catch, и при возникновении исключения, отменять все вручную (для этого придется использовать временные переменные) и прокидывать его дальше вверх по стеку.

Код: plaintext
1.
 tmp_a = a; try { a = b; } catch (...) { a = tmp_a; throw; } 


Вообще сильную гарантию очень сложно достичь и часто не нужно. Например в приведенном выше коде, tmp_a = a; также может выбросить исключение.
...
Рейтинг: 0 / 0
exception-safe код
    #38395293
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Семантику swap() практически всегда можно реализовать без бросания исключений.
Для этого вместо присвоения через временую переменную надо swap сделать friend для класса, и прямо в ней рекурсивно вызывать swap для каждого поля.
На самом нижнем уровне рекурсии будут готовые std::swap для встроенных типов которые и так не бросают исключений.

Данный подход требует дополнительного допиливания если полях присутствуют ссылки друг на друга или на контейнеры. Правда эта же проблема есть и у исходного варианта :)
...
Рейтинг: 0 / 0
exception-safe код
    #38395439
TimeCoder
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Anatoly Moskovsky,
ну swap - просто как пример, даже если тут уйти от вызова конструктора копирования и оператора присваивания, в других случаях так не получится. Ну хотя бы реализация самого оператора присваивания: есть класс, в его полях могут быть другие составные типы, указатели, и нужно реализовать глубокую копию. Запросто может оказаться так, что на копировании одного из полей прилетит исключение, причем нам код того класса не доступен, и сделать его exception-safe мы не можем.

Первой напрашивается идея sherzod_, оборачивать каждую операцию присваивания в try/catch, и если произошло - откатывать назад уже присвоенные. Но если при откате будут исключения (тоже ведь присваиваем) - то все, приплыли.

Пишу, и по ходу такая идея пришла:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
bool Swap(Foo& a, Foo& b)
{
	try
	{
		Foo pre_a = b, pre_b = a;

		// Если добрались до сюда - все, исключений уже не будет, 
		// оригинальные объекты мы не испортили, а в памяти у нас 
		// подготовлен результат обмена, который надо побитово скопировать

		memcpy(&a, &pre_a, sizeof(pre_a));
		memcpy(&b, &pre_b, sizeof(pre_b));

		/*
		   Проблема в том, что для pre_a и pre_b сработает деструктор,  
		   который испортит память a и b  .....
		*/

		return true;
	}
	catch(...)
	{
	}

	return false;
}
...
Рейтинг: 0 / 0
exception-safe код
    #38395722
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
TimeCoder,

По спецификации исключений все, можно забыть навсегда.
...
Рейтинг: 0 / 0
exception-safe код
    #38395771
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
TimeCoder,

По теме — моя первая мысль была, что же это за такое присваивание, что должно выбрасывать исключение?

В присваивает слева валидный объект, и справа тоже валидный объект. Что может заставить присваивание выбросить исключение?

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

В общем, данный пример надуман.

В общем C++ не имеет механизмов решения такой проблемы.

Но в частности, можно было бы сделать две копии объектов, a и b.
И затем поймать исключение и вернуть все на начальную позицию.
...
Рейтинг: 0 / 0
exception-safe код
    #38395779
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
TimeCoder,



Первой напрашивается идея sherzod_, оборачивать каждую операцию присваивания в try/catch, и если произошло - откатывать назад уже присвоенные. Но если при откате будут исключения (тоже ведь присваиваем) - то все, приплыли.

так и что делать-то, это язык программирования, а не субд, он это не умеет.
...
Рейтинг: 0 / 0
exception-safe код
    #38395793
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
TimeCoderAnatoly Moskovsky,
ну swap - просто как пример, даже если тут уйти от вызова конструктора копирования и оператора присваивания, в других случаях так не получится. Ну хотя бы реализация самого оператора присваивания: есть класс, в его полях могут быть другие составные типы, указатели, и нужно реализовать глубокую копию. Запросто может оказаться так, что на копировании одного из полей прилетит исключение, причем нам код того класса не доступен, и сделать его exception-safe мы не можем.

Первой напрашивается идея sherzod_, оборачивать каждую операцию присваивания в try/catch, и если произошло - откатывать назад уже присвоенные. Но если при откате будут исключения (тоже ведь присваиваем) - то все, приплыли.

Пишу, и по ходу такая идея пришла:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
bool Swap(Foo& a, Foo& b)
{
	try
	{
		Foo pre_a = b, pre_b = a;

		// Если добрались до сюда - все, исключений уже не будет, 
		// оригинальные объекты мы не испортили, а в памяти у нас 
		// подготовлен результат обмена, который надо побитово скопировать

		memcpy(&a, &pre_a, sizeof(pre_a));
		memcpy(&b, &pre_b, sizeof(pre_b));

		/*
		   Проблема в том, что для pre_a и pre_b сработает деструктор,  
		   который испортит память a и b  .....
		*/

		return true;
	}
	catch(...)
	{
	}

	return false;
}




Почему тут не будет исключений — не понятно. Они могут быть.
Использовать memcpy для копирования любых объектов нельзя, только POD.

Тебе осталось сделать только ещё один шаг всего до правильного решения, но ты почему-то его не сделал.
...
Рейтинг: 0 / 0
exception-safe код
    #38395824
?
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
?
Гость
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
void Swap(Foo& a, Foo& b)
{
   char t[sizeof(Foo)];
   memcpy(&t, &a, sizeof(foo));
   memcpy(&a, &b, sizeof(foo));
   memcpy(&b, &t, sizeof(foo));
}


Но как уже сказали использовать memcpy для копирования объектов нельзя.
...
Рейтинг: 0 / 0
exception-safe код
    #38396103
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MasterZivПо спецификации исключений все, можно забыть навсегда.
Че это забыть?
Есть noexcept (по сути тот же throw() ), которого вполне хватает.
...
Рейтинг: 0 / 0
exception-safe код
    #38396115
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly MoskovskyMasterZivПо спецификации исключений все, можно забыть навсегда.
Че это забыть?
Есть noexcept (по сути тот же throw() ), которого вполне хватает.

Ну и всё. Все спецификации.
...
Рейтинг: 0 / 0
exception-safe код
    #38396136
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
MasterZivНу и всё. Все спецификации.
Так а больше все равно никто и не использовал, ну разве что дурачки какие
...
Рейтинг: 0 / 0
exception-safe код
    #38396499
TimeCoder
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
гм.. так а какой шаг? Для каждого поля данных вызвать std::swap ?
...
Рейтинг: 0 / 0
exception-safe код
    #38396523
noexcept
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
TimeCoderгм.. так а какой шаг? Для каждого поля данных вызвать std::swap ?
именно!
Ну и все swap и операторы и конструкторы перемещения должны быть noexcept.
...
Рейтинг: 0 / 0
exception-safe код
    #38396631
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
TimeCoderгм.. так а какой шаг? Для каждого поля данных вызвать std::swap ?

Ну я лично имел в виду обратное присваивание в блоке catch.
...
Рейтинг: 0 / 0
exception-safe код
    #38396686
std::swap
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
void Swap(Foo& a, Foo& b) noexcept
{
    Foo t( std::move(a) );
    a = std::move(b);                 
    b = std::move(t);
}


int main() {
 Foo a, b;

 // 1
 std::swap(a, b);

 // 2
 Swap(a, b);

 return 0;
}



http://www.cplusplus.com/reference/algorithm/swap/
...
Рейтинг: 0 / 0
16 сообщений из 16, страница 1 из 1
Форумы / C++ [игнор отключен] [закрыт для гостей] / exception-safe код
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]