From 955afc3182df2dbfa4ca9e46237485398104090d Mon Sep 17 00:00:00 2001 From: Connor McLaughlin <stenzek@gmail.com> Date: Fri, 19 Mar 2021 01:51:39 +1000 Subject: [PATCH] Common: Add Error helper class --- src/common/CMakeLists.txt | 2 + src/common/error.cpp | 359 ++++++++++++++++++++++++++++++++++++++ src/common/error.h | 97 ++++++++++ 3 files changed, 458 insertions(+) create mode 100644 src/common/error.cpp create mode 100644 src/common/error.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 220395860..9824b7aa2 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -27,6 +27,8 @@ add_library(common crash_handler.cpp crash_handler.h dimensional_array.h + error.cpp + error.h event.cpp event.h fifo_queue.h diff --git a/src/common/error.cpp b/src/common/error.cpp new file mode 100644 index 000000000..56af691fe --- /dev/null +++ b/src/common/error.cpp @@ -0,0 +1,359 @@ +#include "error.h" +#include <cstdlib> +#include <cstring> +#include <type_traits> + +// Platform-specific includes +#if defined(_WIN32) +#include "windows_headers.h" +static_assert(std::is_same<DWORD, unsigned long>::value, "DWORD is unsigned long"); +static_assert(std::is_same<HRESULT, long>::value, "HRESULT is long"); +#endif + +namespace Common { + +Error::Error() : m_type(Type::None) +{ + m_error.none = 0; +} + +Error::Error(const Error& c) +{ + m_type = c.m_type; + std::memcpy(&m_error, &c.m_error, sizeof(m_error)); + m_code_string.AppendString(c.m_code_string); + m_message.AppendString(c.m_message); +} + +Error::~Error() = default; + +void Error::Clear() +{ + m_type = Type::None; + m_error.none = 0; + m_code_string.Clear(); + m_message.Clear(); +} + +void Error::SetErrno(int err) +{ + m_type = Type::Errno; + m_error.errno_f = err; + + m_code_string.Format("%i", err); + +#ifdef _MSC_VER + strerror_s(m_message.GetWriteableCharArray(), m_message.GetBufferSize(), err); + m_message.UpdateSize(); +#else + const char* message = std::strerror(err); + if (message) + m_message = message; + else + m_message = StaticString("<Could not get error message>"); +#endif +} + +void Error::SetSocket(int err) +{ +// Socket errors are win32 errors on windows +#ifdef _WIN32 + SetWin32(err); +#else + SetErrno(err); +#endif +} + +void Error::SetMessage(const char* msg) +{ + m_type = Type::User; + m_error.user = 0; + m_code_string.Clear(); + m_message = msg; +} + +void Error::SetUser(int err, const char* msg) +{ + m_type = Type::User; + m_error.user = err; + m_code_string.Format("%d", err); + m_message = msg; +} + +void Error::SetUser(const char* code, const char* message) +{ + m_type = Type::User; + m_error.user = 0; + m_code_string = code; + m_message = message; +} + +void Error::SetUserFormatted(int err, const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + + m_type = Type::User; + m_error.user = err; + m_code_string.Format("%d", err); + m_message.FormatVA(format, ap); + va_end(ap); +} + +void Error::SetUserFormatted(const char* code, const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + + m_type = Type::User; + m_error.user = 0; + m_code_string = code; + m_message.FormatVA(format, ap); + va_end(ap); +} + +void Error::SetFormattedMessage(const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + + m_type = Type::User; + m_error.user = 0; + m_code_string.Clear(); + m_message.FormatVA(format, ap); + va_end(ap); +} + +#ifdef _WIN32 + +void Error::SetWin32(unsigned long err) +{ + m_type = Type::Win32; + m_error.win32 = err; + m_code_string.Format("%u", static_cast<u32>(err)); + m_message.Clear(); + + const DWORD r = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, m_error.win32, 0, m_message.GetWriteableCharArray(), + m_message.GetWritableBufferSize(), NULL); + if (r > 0) + { + m_message.Resize(r); + m_message.RStrip(); + } + else + { + m_message = "<Could not resolve system error ID>"; + } +} + +void Error::SetHResult(long err) +{ + m_type = Type::HResult; + m_error.win32 = err; + m_code_string.Format("%08X", static_cast<u32>(err)); + m_message.Clear(); + + const DWORD r = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, m_error.win32, 0, m_message.GetWriteableCharArray(), + m_message.GetWritableBufferSize(), NULL); + if (r > 0) + { + m_message.Resize(r); + m_message.RStrip(); + } + else + { + m_message = "<Could not resolve system error ID>"; + } +} + +#endif + +// constructors +Error Error::CreateNone() +{ + Error ret; + ret.Clear(); + return ret; +} + +Error Error::CreateErrno(int err) +{ + Error ret; + ret.SetErrno(err); + return ret; +} + +Error Error::CreateSocket(int err) +{ + Error ret; + ret.SetSocket(err); + return ret; +} + +Error Error::CreateMessage(const char* msg) +{ + Error ret; + ret.SetMessage(msg); + return ret; +} + +Error Error::CreateUser(int err, const char* msg) +{ + Error ret; + ret.SetUser(err, msg); + return ret; +} + +Error Error::CreateUser(const char* code, const char* message) +{ + Error ret; + ret.SetUser(code, message); + return ret; +} + +Error Error::CreateMessageFormatted(const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + + Error ret; + ret.m_type = Type::User; + ret.m_message.FormatVA(format, ap); + + va_end(ap); + + return ret; +} + +Error Error::CreateUserFormatted(int err, const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + + Error ret; + ret.m_type = Type::User; + ret.m_error.user = err; + ret.m_code_string.Format("%d", err); + ret.m_message.FormatVA(format, ap); + + va_end(ap); + + return ret; +} + +Error Error::CreateUserFormatted(const char* code, const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + + Error ret; + ret.m_type = Type::User; + ret.m_error.user = 0; + ret.m_code_string = code; + ret.m_message.FormatVA(format, ap); + + va_end(ap); + + return ret; +} + +#ifdef _WIN32 +Error Error::CreateWin32(unsigned long err) +{ + Error ret; + ret.SetWin32(err); + return ret; +} + +Error Error::CreateHResult(long err) +{ + Error ret; + ret.SetHResult(err); + return ret; +} + +#endif + +Error& Error::operator=(const Error& e) +{ + m_type = e.m_type; + std::memcpy(&m_error, &e.m_error, sizeof(m_error)); + m_code_string.Clear(); + m_code_string.AppendString(e.m_code_string); + m_message.Clear(); + m_message.AppendString(e.m_message); + return *this; +} + +bool Error::operator==(const Error& e) const +{ + switch (m_type) + { + case Type::None: + return true; + + case Type::Errno: + return m_error.errno_f == e.m_error.errno_f; + + case Type::Socket: + return m_error.socketerr == e.m_error.socketerr; + + case Type::User: + return m_error.user == e.m_error.user; + +#ifdef _WIN32 + case Type::Win32: + return m_error.win32 == e.m_error.win32; + + case Type::HResult: + return m_error.hresult == e.m_error.hresult; +#endif + } + + return false; +} + +bool Error::operator!=(const Error& e) const +{ + switch (m_type) + { + case Type::None: + return false; + + case Type::Errno: + return m_error.errno_f != e.m_error.errno_f; + + case Type::Socket: + return m_error.socketerr != e.m_error.socketerr; + + case Type::User: + return m_error.user != e.m_error.user; + +#ifdef _WIN32 + case Type::Win32: + return m_error.win32 != e.m_error.win32; + + case Type::HResult: + return m_error.hresult != e.m_error.hresult; +#endif + } + + return true; +} + +SmallString Error::GetCodeAndMessage() const +{ + SmallString ret; + GetCodeAndMessage(ret); + return ret; +} + +void Error::GetCodeAndMessage(String& dest) const +{ + if (m_code_string.IsEmpty()) + dest.Assign(m_message); + else + dest.Format("[%s]: %s", m_code_string.GetCharArray(), m_message.GetCharArray()); +} + +} // namespace Common \ No newline at end of file diff --git a/src/common/error.h b/src/common/error.h new file mode 100644 index 000000000..aac45bb9b --- /dev/null +++ b/src/common/error.h @@ -0,0 +1,97 @@ +#pragma once +#include "string.h" +#include "types.h" + +namespace Common { + +// this class provides enough storage room for all of these types +class Error +{ +public: + Error(); + Error(const Error& e); + ~Error(); + + enum class Type + { + None = 0, // Set by default constructor, returns 'No Error'. + Errno = 1, // Error that is set by system functions, such as open(). + Socket = 2, // Error that is set by socket functions, such as socket(). On Unix this is the same as errno. + User = 3, // When translated, will return 'User Error %u' if no message is specified. + Win32 = 4, // Error that is returned by some Win32 functions, such as RegOpenKeyEx. Also used by other APIs through + // GetLastError(). + HResult = 5, // Error that is returned by Win32 COM methods, e.g. S_OK. + }; + + ALWAYS_INLINE Type GetType() const { return m_type; } + ALWAYS_INLINE int GetErrnoCode() const { return m_error.errno_f; } + ALWAYS_INLINE int GetSocketCode() const { return m_error.socketerr; } + ALWAYS_INLINE int GetUserCode() const { return m_error.user; } +#ifdef _WIN32 + ALWAYS_INLINE unsigned long GetWin32Code() const { return m_error.win32; } + ALWAYS_INLINE long GetHResultCode() const { return m_error.hresult; } +#endif + + // get code, e.g. "0x00000002" + ALWAYS_INLINE const String& GetCodeString() const { return m_code_string; } + + // get description, e.g. "File not Found" + ALWAYS_INLINE const String& GetMessage() const { return m_message; } + + // setter functions + void Clear(); + void SetErrno(int err); + void SetSocket(int err); + void SetMessage(const char* msg); + void SetFormattedMessage(const char* format, ...); + void SetUser(int err, const char* msg); + void SetUser(const char* code, const char* message); + void SetUserFormatted(int err, const char* format, ...); + void SetUserFormatted(const char* code, const char* format, ...); +#ifdef _WIN32 + void SetWin32(unsigned long err); + void SetHResult(long err); +#endif + + // constructors + static Error CreateNone(); + static Error CreateErrno(int err); + static Error CreateSocket(int err); + static Error CreateMessage(const char* msg); + static Error CreateMessageFormatted(const char* format, ...); + static Error CreateUser(int err, const char* msg); + static Error CreateUser(const char* code, const char* message); + static Error CreateUserFormatted(int err, const char* format, ...); + static Error CreateUserFormatted(const char* code, const char* format, ...); +#ifdef _WIN32 + static Error CreateWin32(unsigned long err); + static Error CreateHResult(long err); +#endif + + // get code and description, e.g. "[0x00000002]: File not Found" + SmallString GetCodeAndMessage() const; + void GetCodeAndMessage(String& dest) const; + + // operators + Error& operator=(const Error& e); + bool operator==(const Error& e) const; + bool operator!=(const Error& e) const; + +private: + Type m_type = Type::None; + union + { + int none; + int errno_f; // renamed from errno to avoid conflicts with #define'd errnos. + int socketerr; + int user; +#ifdef _WIN32 + unsigned long win32; + long hresult; +#endif + } m_error{}; + StackString<16> m_code_string; + TinyString m_message; +}; + +} // namespace Common \ No newline at end of file