From e8d86d76e564fd24b5594a1c3eb2e3705ac09cea Mon Sep 17 00:00:00 2001 From: Laura Hermanns Date: Sat, 25 Jan 2025 16:51:15 -0500 Subject: [PATCH] [Misc] Renamed Throw.h to Trap.h and made Trap() publicly available. This builds upon the new way to better handle exceptions in the public interface (671c74ed). Instead of only calling abort() when exceptions are disabled, this change makes use of the already implemented Trap() function that provides a full callstack and other functionality. Since LLGL does not handle but only throws exceptions, we accept that this only provides a handful of predefined exceptions for the few cases they are used in the public header files. --- include/LLGL/Throw.h | 27 --------- include/LLGL/Trap.h | 73 +++++++++++++++++++++++++ include/LLGL/TypeInfo.h | 8 +-- include/LLGL/Utils/Color.h | 8 +-- include/LLGL/Utils/ColorRGB.h | 8 +-- include/LLGL/Utils/ColorRGBA.h | 8 +-- sources/Core/Exception.cpp | 64 +++++++++++----------- sources/Core/Exception.h | 14 +++-- sources/Renderer/CheckedCast.h | 14 ++--- sources/Renderer/RenderSystem.cpp | 6 +- sources/Renderer/RenderSystemModule.cpp | 6 +- 11 files changed, 140 insertions(+), 96 deletions(-) delete mode 100644 include/LLGL/Throw.h create mode 100644 include/LLGL/Trap.h diff --git a/include/LLGL/Throw.h b/include/LLGL/Throw.h deleted file mode 100644 index 18aecdd847..0000000000 --- a/include/LLGL/Throw.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Throw.h - * - * Copyright (c) 2015 Lukas Hermanns. All rights reserved. - * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). - */ - -#ifndef LLGL_THROW_H -#define LLGL_THROW_H - -#if __EXCEPTIONS || __cpp_exceptions == 199711 - #define LLGL_COMPILER_EXCEPTIONS_ENABLED 1 -#endif - -#if LLGL_ENABLE_EXCEPTIONS && LLGL_COMPILER_EXCEPTIONS_ENABLED - #define LLGL_VERIFY_OR_THROW(CONDITION, EXCEPTION) if (!(CONDITION)) { throw (EXCEPTION); } -#else - #include - - #define LLGL_VERIFY_OR_THROW(CONDITION, EXCEPTION) if (!(CONDITION)) { std::abort(); } -#endif - -#endif - - - -// ================================================================================ diff --git a/include/LLGL/Trap.h b/include/LLGL/Trap.h new file mode 100644 index 0000000000..a74923a7be --- /dev/null +++ b/include/LLGL/Trap.h @@ -0,0 +1,73 @@ +/* + * Trap.h + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +#ifndef LLGL_TRAP_H +#define LLGL_TRAP_H + + +#include + + +/** +\brief Helper macro to trap execution when the condition fails. +\remarks LLGL only throws exceptions if it was built with \c LLGL_ENABLE_EXCEPTIONS. +\see LLGL::Trap +*/ +#define LLGL_VERIFY(CONDITION, EXCEPTION) \ + if (!(CONDITION)) \ + { \ + LLGL::Trap(LLGL::Exception::EXCEPTION, __FUNCTION__, "assertion failed: %s", #CONDITION); \ + } + + +namespace LLGL +{ + + +/** +\brief Enumeration of all exception classes Trap() can throw. +\remarks LLGL only throws exceptions if it was built with \c LLGL_ENABLE_EXCEPTIONS. +\see Trap +*/ +enum class Exception +{ + //! Refers to std::runtime_error. + RuntimeError, + + //! Refers to std::out_of_range. + OutOfRange, + + //! Refers to std::bad_cast. + BadCast, + + //! Refers to std::invalid_argument. + InvalidArgument, +}; + + +/** +\brief Primary function to trap execution from an unrecoverable state. +\param[in] exception Specifies what type of exception this function should throw if exceptions are enabled. +\param[in] origin Specifies the origin where execution is trapped. This can be the special preprocessor macro \c __FUNCTION__ for instance. +\param[in] format Specifies the formatted string as used with \c printf. +\remarks This might either throw an exception, abort execution, or break the debugger depending on the configuration LLGL was built with. +LLGL does not handle exceptions of any kind but can throw exceptions (if built with \c LLGL_ENABLE_EXCEPTIONS) +to let the client programmer exit the application gracefully. +Otherwise, this function simply aborts execution and dumps the callstack to the standard error pipe. +*/ +[[noreturn]] +LLGL_EXPORT void Trap(Exception exception, const char* origin, const char* format, ...); + + +} // /namespace LLGL + + +#endif + + + +// ================================================================================ diff --git a/include/LLGL/TypeInfo.h b/include/LLGL/TypeInfo.h index a255187b58..36dc7a4dc8 100644 --- a/include/LLGL/TypeInfo.h +++ b/include/LLGL/TypeInfo.h @@ -10,7 +10,7 @@ #include -#include +#include namespace LLGL @@ -98,8 +98,7 @@ const LLGL::Window& myWindow = LLGL::CastTo(mySurface); template inline const T& CastTo(const Interface& obj) { - LLGL_VERIFY_OR_THROW(IsInstanceOf(obj), std::bad_cast()); - + LLGL_VERIFY(IsInstanceOf(obj), BadCast); return static_cast(obj); } @@ -118,8 +117,7 @@ LLGL::Window& myWindow = LLGL::CastTo(mySurface); template inline T& CastTo(Interface& obj) { - LLGL_VERIFY_OR_THROW(IsInstanceOf(obj), std::bad_cast()); - + LLGL_VERIFY(IsInstanceOf(obj), BadCast); return static_cast(obj); } diff --git a/include/LLGL/Utils/Color.h b/include/LLGL/Utils/Color.h index 8d0bfb89d0..5ab5ff20f0 100644 --- a/include/LLGL/Utils/Color.h +++ b/include/LLGL/Utils/Color.h @@ -9,9 +9,9 @@ #define LLGL_COLOR_H -#include #include #include +#include #include #include #include @@ -201,8 +201,7 @@ class LLGL_EXPORT Color */ T& operator [] (std::size_t component) { - LLGL_VERIFY_OR_THROW(component < N, std::out_of_range("color component index out of range")); - + LLGL_VERIFY(component < N, OutOfRange); return v_[component]; } @@ -213,8 +212,7 @@ class LLGL_EXPORT Color */ const T& operator [] (std::size_t component) const { - LLGL_VERIFY_OR_THROW(component < N, std::out_of_range("color component index out of range")); - + LLGL_VERIFY(component < N, OutOfRange); return v_[component]; } diff --git a/include/LLGL/Utils/ColorRGB.h b/include/LLGL/Utils/ColorRGB.h index 93ac311870..b3dedbd508 100644 --- a/include/LLGL/Utils/ColorRGB.h +++ b/include/LLGL/Utils/ColorRGB.h @@ -10,7 +10,7 @@ #include -#include +#include namespace LLGL @@ -144,8 +144,7 @@ class LLGL_EXPORT Color */ T& operator [] (std::size_t component) { - LLGL_VERIFY_OR_THROW((component < Color::components), std::out_of_range("color component index out of range (must be 0, 1, or 2)")); - + LLGL_VERIFY((component < Color::components), OutOfRange); return *((&r) + component); } @@ -156,8 +155,7 @@ class LLGL_EXPORT Color */ const T& operator [] (std::size_t component) const { - LLGL_VERIFY_OR_THROW((component < Color::components), std::out_of_range("color component index out of range (must be 0, 1, or 2)")); - + LLGL_VERIFY((component < Color::components), OutOfRange); return *((&r) + component); } diff --git a/include/LLGL/Utils/ColorRGBA.h b/include/LLGL/Utils/ColorRGBA.h index 3ed83fa9e3..2ea4f307d8 100644 --- a/include/LLGL/Utils/ColorRGBA.h +++ b/include/LLGL/Utils/ColorRGBA.h @@ -9,8 +9,8 @@ #define LLGL_COLOR_RGBA_H -#include #include +#include namespace LLGL @@ -180,8 +180,7 @@ class LLGL_EXPORT Color */ T& operator [] (std::size_t component) { - LLGL_VERIFY_OR_THROW((component < Color::components), std::out_of_range("color component index out of range (must be 0, 1, 2, or 3)")); - + LLGL_VERIFY((component < Color::components), OutOfRange); return *((&r) + component); } @@ -192,8 +191,7 @@ class LLGL_EXPORT Color */ const T& operator [] (std::size_t component) const { - LLGL_VERIFY_OR_THROW((component < Color::components), std::out_of_range("color component index out of range (must be 0, 1, 2, or 3)")); - + LLGL_VERIFY((component < Color::components), OutOfRange); return *((&r) + component); } diff --git a/sources/Core/Exception.cpp b/sources/Core/Exception.cpp index f58d6489aa..8993396c72 100644 --- a/sources/Core/Exception.cpp +++ b/sources/Core/Exception.cpp @@ -35,7 +35,7 @@ static void AddOptionalOrigin(std::string& s, const char* origin) } [[noreturn]] -LLGL_EXPORT void Trap(const char* origin, const char* format, ...) +LLGL_EXPORT void Trap(Exception exception, const char* origin, const char* format, ...) { /* Build full report string */ std::string report; @@ -43,14 +43,20 @@ LLGL_EXPORT void Trap(const char* origin, const char* format, ...) LLGL_STRING_PRINTF(report, format); - #if LLGL_ENABLE_EXCEPTIONS + #if LLGL_EXCEPTIONS_SUPPORTED /* Throw exception with report and optional origin */ - throw std::runtime_error(report); + switch (exception) + { + case RuntimeError: throw std::runtime_error(report); + case OutOfRange: throw std::out_of_range(report); + case BadCast: throw std::bad_cast(); + case InvalidArgument: throw std::invalid_argument(report); + } - #else // LLGL_ENABLE_EXCEPTIONS + #endif // /LLGL_EXCEPTIONS_SUPPORTED - # ifdef LLGL_DEBUG + #ifdef LLGL_DEBUG /* Print debug report */ report = DebugStackTrace().c_str() + report; @@ -60,26 +66,22 @@ LLGL_EXPORT void Trap(const char* origin, const char* format, ...) /* Break execution if there's a debugger attached */ LLGL_DEBUG_BREAK(); - # else // LLGL_DEBUG + #endif // /LLGL_DEBUG - # ifdef LLGL_OS_ANDROID + #ifdef LLGL_OS_ANDROID /* Print report to Android specific error log */ (void)__android_log_print(ANDROID_LOG_ERROR, "LLGL", "%s\n", report.c_str()); - # else + #else /* Print report to standard error output */ ::fprintf(stderr, "%s\n", report.c_str()); - # endif - - # endif // /LLGL_DEBUG + #endif // /LLGL_OS_ANDROID /* Abort execution as LLGL is trapped in an unrecoverable state */ ::abort(); - - #endif // /LLGL_ENABLE_EXCEPTIONS } [[noreturn]] @@ -91,69 +93,69 @@ LLGL_EXPORT void TrapAssertionFailed(const char* origin, const char* expr, const LLGL_STRING_PRINTF(detailsStr, details); if (!detailsStr.empty()) - Trap(origin, "assertion failed: '%s'; %s", expr, detailsStr.c_str()); + Trap(Exception::RuntimeError, origin, "assertion failed: '%s'; %s", expr, detailsStr.c_str()); else - Trap(origin, "assertion failed: '%s'", expr); + Trap(Exception::RuntimeError, origin, "assertion failed: '%s'", expr); } else - Trap(origin, "assertion failed: '%s'", expr); + Trap(Exception::RuntimeError, origin, "assertion failed: '%s'", expr); } [[noreturn]] LLGL_EXPORT void TrapFeatureNotSupported(const char* origin, const char* featureName) { - Trap(origin, "%s not supported", featureName); + Trap(Exception::RuntimeError, origin, "%s not supported", featureName); } [[noreturn]] LLGL_EXPORT void TrapRenderingFeatureNotSupported(const char* origin, const char* featureName) { - Trap(origin, "LLGL::RenderingFeatures::%s not supported", featureName); + Trap(Exception::RuntimeError, origin, "LLGL::RenderingFeatures::%s not supported", featureName); } [[noreturn]] LLGL_EXPORT void TrapGLExtensionNotSupported(const char* origin, const char* extensionName, const char* useCase) { if (useCase != nullptr && *useCase != '\0') - Trap(origin, "OpenGL extension '%s' not supported; required for %s", extensionName, useCase); + Trap(Exception::RuntimeError, origin, "OpenGL extension '%s' not supported; required for %s", extensionName, useCase); else - Trap(origin, "OpenGL extension '%s' not supported", extensionName); + Trap(Exception::RuntimeError, origin, "OpenGL extension '%s' not supported", extensionName); } [[noreturn]] LLGL_EXPORT void TrapVKExtensionNotSupported(const char* origin, const char* extensionName, const char* useCase) { if (useCase != nullptr && *useCase != '\0') - Trap(origin, "Vulkan extension '%s' not supported; required for %s", extensionName, useCase); + Trap(Exception::RuntimeError, origin, "Vulkan extension '%s' not supported; required for %s", extensionName, useCase); else - Trap(origin, "Vulkan extension '%s' not supported", extensionName); + Trap(Exception::RuntimeError, origin, "Vulkan extension '%s' not supported", extensionName); } [[noreturn]] LLGL_EXPORT void TrapNotImplemented(const char* origin, const char* useCase) { if (useCase != nullptr && *useCase != '\0') - Trap(origin, "not implemented yet: %s", useCase); + Trap(Exception::RuntimeError, origin, "not implemented yet: %s", useCase); else - Trap(origin, "not implemented yet"); + Trap(Exception::RuntimeError, origin, "not implemented yet"); } [[noreturn]] LLGL_EXPORT void TrapNullPointer(const char* origin, const char* expr) { - Trap(origin, "expression '%s' must not be null", expr); + Trap(Exception::RuntimeError, origin, "expression '%s' must not be null", expr); } [[noreturn]] LLGL_EXPORT void TrapParamExceededUpperBound(const char* origin, const char* paramName, int value, int upperBound) { - Trap(origin, "parameter '%s = %d' out of half-open range [0, %d)", paramName, value, upperBound); + Trap(Exception::RuntimeError, origin, "parameter '%s = %d' out of half-open range [0, %d)", paramName, value, upperBound); } [[noreturn]] LLGL_EXPORT void TrapParamExceededMaximum(const char* origin, const char* paramName, int value, int maximum) { - Trap(origin, "parameter '%s = %d' out of range [0, %d]", paramName, value, maximum); + Trap(Exception::RuntimeError, origin, "parameter '%s = %d' out of range [0, %d]", paramName, value, maximum); } [[noreturn]] @@ -162,18 +164,18 @@ LLGL_EXPORT void TrapReport(const char* origin, const Report& report) std::string text = report.GetText(); for (std::size_t n = text.size(); n > 0 && (text[n - 1] == '\n' || text[n - 1] == '\r'); --n) text.pop_back(); - Trap(origin, "%s", text.c_str()); + Trap(Exception::RuntimeError, origin, "%s", text.c_str()); } LLGL_EXPORT std::nullptr_t ReportException(Report* report, const char* format, ...) { - #if LLGL_ENABLE_EXCEPTIONS + #if LLGL_EXCEPTIONS_SUPPORTED std::string errorStr; LLGL_STRING_PRINTF(errorStr, format); throw std::runtime_error(errorStr); - #else // LLGL_ENABLE_EXCEPTIONS + #else // LLGL_EXCEPTIONS_SUPPORTED if (report != nullptr) { @@ -184,7 +186,7 @@ LLGL_EXPORT std::nullptr_t ReportException(Report* report, const char* format, . return nullptr; - #endif // /LLGL_ENABLE_EXCEPTIONS + #endif // /LLGL_EXCEPTIONS_SUPPORTED } diff --git a/sources/Core/Exception.h b/sources/Core/Exception.h index de07a50570..86ad486df4 100644 --- a/sources/Core/Exception.h +++ b/sources/Core/Exception.h @@ -11,13 +11,21 @@ #include #include +#include #include #include #include "MacroUtils.h" +// Only support exceptions if enabled and the compiler supports it, too. +#if LLGL_ENABLE_EXCEPTIONS && (__EXCEPTIONS || __cpp_exceptions == 199711) +# define LLGL_EXCEPTIONS_SUPPORTED 1 +#else +# define LLGL_EXCEPTIONS_SUPPORTED 0 +#endif + #define LLGL_TRAP(FORMAT, ...) \ - LLGL::Trap(__FUNCTION__, (FORMAT) LLGL_VA_ARGS(__VA_ARGS__)) + LLGL::Trap(LLGL::Exception::RuntimeError, __FUNCTION__, (FORMAT) LLGL_VA_ARGS(__VA_ARGS__)) #define LLGL_TRAP_NOT_IMPLEMENTED(...) \ LLGL::TrapNotImplemented(__FUNCTION__ LLGL_VA_ARGS(__VA_ARGS__)) @@ -35,10 +43,6 @@ namespace LLGL class Report; -// Primary function to trap execution from an unrecoverable state. This might either throw an exception, abort execution, or break the debugger. -[[noreturn]] -LLGL_EXPORT void Trap(const char* origin, const char* format, ...); - // Traps program execution with the message that the specified assertion that failed. [[noreturn]] LLGL_EXPORT void TrapAssertionFailed(const char* origin, const char* expr, const char* details = nullptr, ...); diff --git a/sources/Renderer/CheckedCast.h b/sources/Renderer/CheckedCast.h index ac07ecf905..4aaad4b4b0 100644 --- a/sources/Renderer/CheckedCast.h +++ b/sources/Renderer/CheckedCast.h @@ -12,7 +12,7 @@ #include "../Platform/Debug.h" #if LLGL_ENABLE_CHECKED_CAST -# if LLGL_ENABLE_EXCEPTIONS +# if LLGL_EXCEPTIONS_SUPPORTED # include # else # include @@ -30,7 +30,7 @@ namespace LLGL template inline TDst& ObjectCast(TSrc& obj) { - #if LLGL_ENABLE_EXCEPTIONS + #if LLGL_EXCEPTIONS_SUPPORTED try { @@ -42,11 +42,11 @@ inline TDst& ObjectCast(TSrc& obj) throw; } - #else // LLGL_ENABLE_EXCEPTIONS + #else // LLGL_EXCEPTIONS_SUPPORTED return dynamic_cast(obj); - #endif // /LLGL_ENABLE_EXCEPTIONS + #endif // /LLGL_EXCEPTIONS_SUPPORTED } template @@ -55,7 +55,7 @@ inline TDst ObjectCast(TSrc* obj) if (obj == nullptr) return nullptr; - #if LLGL_ENABLE_EXCEPTIONS + #if LLGL_EXCEPTIONS_SUPPORTED try { @@ -70,13 +70,13 @@ inline TDst ObjectCast(TSrc* obj) throw; } - #else // LLGL_ENABLE_EXCEPTIONS + #else // LLGL_EXCEPTIONS_SUPPORTED TDst objInstance = dynamic_cast(obj); LLGL_ASSERT(objInstance != nullptr); return objInstance; - #endif // /LLGL_ENABLE_EXCEPTIONS + #endif // /LLGL_EXCEPTIONS_SUPPORTED } #else // LLGL_ENABLE_CHECKED_CAST diff --git a/sources/Renderer/RenderSystem.cpp b/sources/Renderer/RenderSystem.cpp index 035528638f..b4613172d8 100644 --- a/sources/Renderer/RenderSystem.cpp +++ b/sources/Renderer/RenderSystem.cpp @@ -127,7 +127,7 @@ RenderSystemPtr RenderSystem::Load(const RenderSystemDescriptor& renderSystemDes if (module->BuildID() != LLGL_BUILD_ID) return ReportException(report, "build ID mismatch in render system module"); - #if LLGL_ENABLE_EXCEPTIONS + #if LLGL_EXCEPTIONS_SUPPORTED try #endif { @@ -160,13 +160,13 @@ RenderSystemPtr RenderSystem::Load(const RenderSystemDescriptor& renderSystemDes return renderSystem; } - #if LLGL_ENABLE_EXCEPTIONS + #if LLGL_EXCEPTIONS_SUPPORTED catch (const std::exception& e) { /* Throw with new exception, otherwise the exception's v-table will be corrupted since it's part of the module */ return ReportException(report, e.what()); } - #endif // /LLGL_ENABLE_EXCEPTIONS + #endif // /LLGL_EXCEPTIONS_SUPPORTED #endif // /LLGL_BUILD_STATIC_LIB } diff --git a/sources/Renderer/RenderSystemModule.cpp b/sources/Renderer/RenderSystemModule.cpp index 210b120b96..70cb13cac8 100644 --- a/sources/Renderer/RenderSystemModule.cpp +++ b/sources/Renderer/RenderSystemModule.cpp @@ -76,20 +76,20 @@ RenderSystemModulePtr RenderSystemModule::Load(const char* name, Report* outRepo std::string moduleFilename = Module::GetModuleFilename(name); std::unique_ptr module; - #if LLGL_ENABLE_EXCEPTIONS + #if LLGL_EXCEPTIONS_SUPPORTED Report moduleReport; module = Module::Load(moduleFilename.c_str(), &moduleReport); if (!module) TrapReport(__FUNCTION__, moduleReport); - #else // LLGL_ENABLE_EXCEPTIONS + #else // LLGL_EXCEPTIONS_SUPPORTED module = Module::Load(moduleFilename.c_str(), outReport); if (!module) return nullptr; - #endif // /LLGL_ENABLE_EXCEPTIONS + #endif // /LLGL_EXCEPTIONS_SUPPORTED /* Allocate new module wrapper */ return RenderSystemModulePtr{ new RenderSystemModule{ name, std::move(moduleFilename), std::move(module) } };