Как стать автором
Обновить
5
0
Артём Селиванов @broly

Разработчик игр

Отправить сообщение

Это и имел в виду: отключить исключения в пользу использования expected, и, как следствие, нет нужды в расставлении noexcept. То есть некое сравнение использования двух механизмов, в том числе и по визуальному виду кода, если бы пришлось выбрать один из них.

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

Я подразумевал именно отключенные исключения в проекте. Собственно и написал эту статью, чтобы рассказать о приближении сего инструмента к исключениям: новомодный стандартный expected, но с привычным похожим интерфейсом :)

С концептами тоже не выйдет. Даже если добавить функциям return_value и return_void requires, то при компиляции ругнётся: C3782 (обещание сопрограммы не может содержать return_value и return_void одновременно):

	template<typename T>
	concept CIsNotVoid = !TIsSame<T, void>::Value;
		
	template<typename T>
	concept CIsVoid = TIsSame<T, void>::Value;

	template<typename ReturnType, typename TaskType>
	struct TPromise_Base : FPromise_Exception
	{
		TMulticastDelegate<void(ReturnType)> OnDone;

		template<typename T = ReturnType>
		void return_value(T Result) requires CIsNotVoid<T>
		{
			if (CurrentException)
			{
				OnException.ExecuteIfBound(CurrentException);
				OnException.Unbind();
			}
			else if (OnDone.IsBound())
				OnDone.Broadcast(Result);
		}
	
		template<typename T = ReturnType>
		void return_void() const requires CIsVoid<T>
		{
			if (CurrentException)
			{
				OnException.ExecuteIfBound(CurrentException);
				OnException.Unbind();
			}
			else if (OnDone.IsBound())
				OnDone.Broadcast();
		}
	};

Да по сути тот же Future, только в левой руке.

Future (тот же awaitable), на мой взгляд, даёт возможность инкапсулировать использование coroutine_handle и ряда специальных методов:

По SetResult вызываем resume тем самым передавая выполнение обратно с подкидыванием возвращаемого значения в await_resume. Функция, которая возвращает Future инициирует какое-либо отложенное действие, а затем, в колбеке (у меня это лямбда) выставляет результат. Ну как вариант.

Не позиционирую себя как человека, который точно знает как должно быть (технология относительно новая в C++ и, насколько слышал, спорная), но я тоже стараюсь обобщить на разные юзкейсы. Хотя тут есть некоторая привязка к фреймворку, так как использую UE делегаты, для того, чтобы аудитории понятнее было. В остальном, данная реализация даёт возможность писать асинхронный код используя только типы CoroTasks::TTask<> и CoroTasks::TFuture<> , а далее просто манипулировать ими. Ну и речь об однопоточной асинхронщине, так как её предоставляет сам движок (многопоточное вполнение он сам синкает в MainThrd), позволяя вешаться на неё пользовательскими колбеками.

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

На каждый чих? В статье за awaiter выступает использование Future. Можно писать функции, которые возвращают Future и использовать в тасках с возвращаемым значением.

В плагине UE5Coro до момента написании статьи не было возможности использования co_return (т.е. не было нормальных тасков), а в реализации от EpicGames их таски предназначены для многопоточного исполнения без возможности их запуска в основном потоке.

Информация

В рейтинге
Не участвует
Дата рождения
Зарегистрирован
Активность

Специализация

Software Developer, Game Developer
Lead
Python
C++
PostgreSQL
OOP
OpenGL
GLSL
Assembler