#pragma once #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) #include #endif namespace SafeExecutor { static constexpr auto EXCEPTION_CPP_MAGIC = 0xE06D7363; template static constexpr auto StackString(const T(&str)[N]) { std::array ret{}; std::copy(std::begin(str), std::end(str), ret.begin()); return ret; } // Enum class for possible error codes enum class ResultCode : uint8_t { Success, DivisionByZero, InvalidArgument, OutOfRange, LogicError, SystemError, RuntimeError, BadAlloc, CppException, UnknownException, SEHException, UnknownResult, FutureError }; // Convert result to string constexpr const char* ResultCodeToString(ResultCode code) { switch (code) { case ResultCode::Success: return "Success"; case ResultCode::DivisionByZero: return "DivisionByZero"; case ResultCode::InvalidArgument: return "InvalidArgument"; case ResultCode::OutOfRange: return "OutOfRange"; case ResultCode::LogicError: return "LogicError"; case ResultCode::SystemError: return "SystemError"; case ResultCode::RuntimeError: return "RuntimeError"; case ResultCode::BadAlloc: return "BadAlloc"; case ResultCode::CppException: return "CppException"; case ResultCode::UnknownException: return "UnknownException"; case ResultCode::SEHException: return "SEHException"; case ResultCode::UnknownResult: return "UnknownResult"; default: return "Unknown"; } } // Helper type trait to check if a type is void template struct is_void_type : std::is_void> {}; // Specialization of TResultOf for void return type template using TResultOf = std::conditional_t< is_void_type>::value, std::monostate, std::invoke_result_t >; // Handler type of a function using TExceptionHandler = std::function; // Execute function as C++ exception protected code inside SEH handler function(execute_seh_safe) template std::optional > ExecuteCPPExceptionSafe(const TExceptionHandler& handler, Fn&& fn, Args&&... args) noexcept { using ResultType = TResultOf ; try { if constexpr (std::is_same_v || std::is_same_v ) { std::invoke(std::forward(fn), std::forward(args)...); return std::nullopt; } else { return std::make_optional(std::invoke(std::forward(fn), std::forward(args)...)); } } catch (const std::invalid_argument& e) { handler(static_cast(ResultCode::InvalidArgument), e.what()); return std::nullopt; } catch (const std::out_of_range& e) { handler(static_cast(ResultCode::OutOfRange), e.what()); return std::nullopt; } catch (const std::logic_error& e) { handler(static_cast(ResultCode::LogicError), e.what()); return std::nullopt; } catch (const std::system_error& e) { handler(static_cast(ResultCode::SystemError), e.what()); return std::nullopt; } catch (const std::runtime_error& e) { handler(static_cast(ResultCode::RuntimeError), e.what()); return std::nullopt; } catch (const std::bad_alloc& e) { handler(static_cast(ResultCode::BadAlloc), e.what()); return std::nullopt; } catch (const std::exception& e) { handler(static_cast(ResultCode::CppException), e.what()); return std::nullopt; } catch (...) { handler(static_cast(ResultCode::UnknownException), StackString("Unhandled Exception").data()); return std::nullopt; } return std::nullopt; } inline uint32_t SEHExceptionParser(const uint32_t code, const EXCEPTION_POINTERS* pEP) { // TODO: Give more information about the exception return code == EXCEPTION_CPP_MAGIC ? EXCEPTION_CONTINUE_SEARCH : EXCEPTION_EXECUTE_HANDLER; } // Executes the given function and returns the result or an exception template inline auto ExecuteSafeEx(TExceptionHandler handler, Fn&& fn, Args&&... args) -> std::pair >, ResultCode> { using ResultType = TResultOf ; std::pair , ResultCode> result; std::optional optResult; auto ExecuteSafeWorkerImpl = [&]() -> void { optResult = ExecuteCPPExceptionSafe(handler, std::forward(fn), std::forward(args)...); }; #ifdef _WIN32 // HACK: Bypass for 'Cannot use __try in functions that require object unwinding'. auto ExecuteSafeImpl = [&]() { __try { ExecuteSafeWorkerImpl(); if constexpr (std::is_same_v || std::is_same_v ) result = std::make_pair(optResult, ResultCode::Success); else { if (!optResult) result = std::make_pair(std::nullopt, ResultCode::UnknownResult); else result = std::make_pair(optResult, ResultCode::Success); } } __except (SEHExceptionParser(GetExceptionCode(), GetExceptionInformation())) { handler(GetExceptionCode(), StackString("SEH Exception").data()); result = std::make_pair(std::nullopt, ResultCode::SEHException); } }; ExecuteSafeImpl(); #else ExecuteSafeWorkerImpl(); #endif return result; } template inline auto ExecuteSafe(Fn&& fn, Args&&... args) -> std::pair >, ResultCode> { return ExecuteSafeEx([](uint32_t code, const char* name) -> void { std::cout << StackString("Error").data() << name << '(' << code << ')' << std::endl; }, std::forward(fn), std::forward(args)...); } template inline auto ExecuteSafeAsyncEx(TExceptionHandler handler, Fn&& fn, Args&&... args) -> std::future >, ResultCode>> { try { return std::async(std::launch::async, [&]() { return ExecuteSafeEx(handler, std::forward(fn), std::forward(args)...); }); } catch (const std::future_error&) { return std::async(std::launch::async, []() -> std::pair >, ResultCode> { return std::make_pair(std::nullopt, ResultCode::FutureError); }); } } template inline auto ExecuteSafeAsync(Fn&& fn, Args&&... args) -> std::future >, ResultCode>> { return ExecuteSafeAsyncEx([](uint32_t code, const char* name) -> void { std::cout << StackString("Future Error").data() << name << '(' << code << ')' << std::endl; }, std::forward(fn), std::forward(args)...); } }