Commit 12eccba0 authored by Kenton Varda's avatar Kenton Varda

Improve Win32 error-handling macros.

Especially, this adds KJ_WIN32_HANDLE_ERRORS() to parallel KJ_SYSCALL_HANDLE_ERRORS().
parent 418e0ad4
...@@ -355,7 +355,7 @@ void Debug::Fault::init( ...@@ -355,7 +355,7 @@ void Debug::Fault::init(
#if _WIN32 #if _WIN32
void Debug::Fault::init( void Debug::Fault::init(
const char* file, int line, Win32Error osErrorNumber, const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) { const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
LPVOID ptr; LPVOID ptr;
// TODO(soon): Why doesn't this work for winsock errors? // TODO(soon): Why doesn't this work for winsock errors?
...@@ -400,8 +400,8 @@ int Debug::getOsErrorNumber(bool nonblocking) { ...@@ -400,8 +400,8 @@ int Debug::getOsErrorNumber(bool nonblocking) {
} }
#if _WIN32 #if _WIN32
Debug::Win32Error Debug::getWin32Error() { uint Debug::getWin32ErrorCode() {
return Win32Error(::GetLastError()); return ::GetLastError();
} }
#endif #endif
......
...@@ -168,18 +168,18 @@ namespace kj { ...@@ -168,18 +168,18 @@ namespace kj {
#if _WIN32 #if _WIN32
#define KJ_WIN32(call, ...) \ #define KJ_WIN32(call, ...) \
if (::kj::_::Debug::isWin32Success(call)) {} else \ if (auto _kjWin32Result = ::kj::_::Debug::win32Call(call)) {} else \
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \ for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
::kj::_::Debug::getWin32Error(), #call, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) _kjWin32Result, #call, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal())
#define KJ_WINSOCK(call, ...) \ #define KJ_WINSOCK(call, ...) \
if ((call) != SOCKET_ERROR) {} else \ if (auto _kjWin32Result = ::kj::_::Debug::winsockCall(call)) {} else \
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \ for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
::kj::_::Debug::getWin32Error(), #call, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) _kjWin32Result, #call, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal())
#define KJ_FAIL_WIN32(code, errorNumber, ...) \ #define KJ_FAIL_WIN32(code, errorNumber, ...) \
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \ for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
::kj::_::Debug::Win32Error(errorNumber), code, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal()) ::kj::_::Debug::Win32Result(errorNumber), code, "" #__VA_ARGS__, __VA_ARGS__);; f.fatal())
#endif #endif
...@@ -247,18 +247,23 @@ namespace kj { ...@@ -247,18 +247,23 @@ namespace kj {
#if _WIN32 #if _WIN32
#define KJ_WIN32(call, ...) \ #define KJ_WIN32(call, ...) \
if (::kj::_::Debug::isWin32Success(call)) {} else \ if (auto _kjWin32Result = ::kj::_::Debug::win32Call(call)) {} else \
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \ for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
::kj::_::Debug::getWin32Error(), #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) _kjWin32Result, #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
// Invoke a Win32 syscall that returns either BOOL or HANDLE, and throw an exception if it fails.
#define KJ_WINSOCK(call, ...) \ #define KJ_WINSOCK(call, ...) \
if ((call) != SOCKET_ERROR) {} else \ if (auto _kjWin32Result = ::kj::_::Debug::winsockCall(call)) {} else \
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \ for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
::kj::_::Debug::getWin32Error(), #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) _kjWin32Result, #call, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
// Like KJ_WIN32 but for winsock calls which return `int` with SOCKET_ERROR indicating failure.
//
// Unfortunately, it's impossible to distinguish these from BOOL-returning Win32 calls by type,
// since BOOL is in fact an alias for `int`. :(
#define KJ_FAIL_WIN32(code, errorNumber, ...) \ #define KJ_FAIL_WIN32(code, errorNumber, ...) \
for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \ for (::kj::_::Debug::Fault f(__FILE__, __LINE__, \
::kj::_::Debug::Win32Error(errorNumber), code, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal()) ::kj::_::Debug::Win32Result(errorNumber), code, #__VA_ARGS__, ##__VA_ARGS__);; f.fatal())
#endif #endif
...@@ -309,6 +314,29 @@ namespace kj { ...@@ -309,6 +314,29 @@ namespace kj {
// handleSuccessCase(); // handleSuccessCase();
// } // }
#if _WIN32
#define KJ_WIN32_HANDLE_ERRORS(call) \
if (uint _kjWin32Error = ::kj::_::Debug::win32Call(call).number) \
switch (uint error = _kjWin32Error)
// Like KJ_WIN32, but doesn't throw. Instead, the block after the macro is a switch block on the
// error. Additionally, the int value `error` is defined within the block. So you can do:
//
// KJ_SYSCALL_HANDLE_ERRORS(foo()) {
// case ERROR_FILE_NOT_FOUND:
// handleNoSuchFile();
// break;
// case ERROR_FILE_EXISTS:
// handleExists();
// break;
// default:
// KJ_FAIL_WIN32("foo()", error);
// } else {
// handleSuccessCase();
// }
#endif
#define KJ_ASSERT KJ_REQUIRE #define KJ_ASSERT KJ_REQUIRE
#define KJ_FAIL_ASSERT KJ_FAIL_REQUIRE #define KJ_FAIL_ASSERT KJ_FAIL_REQUIRE
#define KJ_ASSERT_NONNULL KJ_REQUIRE_NONNULL #define KJ_ASSERT_NONNULL KJ_REQUIRE_NONNULL
...@@ -334,10 +362,10 @@ public: ...@@ -334,10 +362,10 @@ public:
typedef LogSeverity Severity; // backwards-compatibility typedef LogSeverity Severity; // backwards-compatibility
#if _WIN32 #if _WIN32
struct Win32Error { struct Win32Result {
// Hack for overloading purposes.
uint number; uint number;
inline explicit Win32Error(uint number): number(number) {} inline explicit Win32Result(uint number): number(number) {}
operator bool() const { return number == 0; }
}; };
#endif #endif
...@@ -363,7 +391,7 @@ public: ...@@ -363,7 +391,7 @@ public:
Fault(const char* file, int line, int osErrorNumber, Fault(const char* file, int line, int osErrorNumber,
const char* condition, const char* macroArgs); const char* condition, const char* macroArgs);
#if _WIN32 #if _WIN32
Fault(const char* file, int line, Win32Error osErrorNumber, Fault(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs); const char* condition, const char* macroArgs);
#endif #endif
~Fault() noexcept(false); ~Fault() noexcept(false);
...@@ -377,7 +405,7 @@ public: ...@@ -377,7 +405,7 @@ public:
void init(const char* file, int line, int osErrorNumber, void init(const char* file, int line, int osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues); const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
#if _WIN32 #if _WIN32
void init(const char* file, int line, Win32Error osErrorNumber, void init(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues); const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
#endif #endif
...@@ -400,9 +428,10 @@ public: ...@@ -400,9 +428,10 @@ public:
static int syscallError(Call&& call, bool nonblocking); static int syscallError(Call&& call, bool nonblocking);
#if _WIN32 #if _WIN32
static bool isWin32Success(int boolean); static Win32Result win32Call(int boolean);
static bool isWin32Success(void* handle); static Win32Result win32Call(void* handle);
static Win32Error getWin32Error(); static Win32Result winsockCall(int result);
static uint getWin32ErrorCode();
#endif #endif
class Context: public ExceptionCallback { class Context: public ExceptionCallback {
...@@ -495,18 +524,22 @@ inline Debug::Fault::Fault(const char* file, int line, kj::Exception::Type type, ...@@ -495,18 +524,22 @@ inline Debug::Fault::Fault(const char* file, int line, kj::Exception::Type type,
} }
#if _WIN32 #if _WIN32
inline Debug::Fault::Fault(const char* file, int line, Win32Error osErrorNumber, inline Debug::Fault::Fault(const char* file, int line, Win32Result osErrorNumber,
const char* condition, const char* macroArgs) const char* condition, const char* macroArgs)
: exception(nullptr) { : exception(nullptr) {
init(file, line, osErrorNumber, condition, macroArgs, nullptr); init(file, line, osErrorNumber, condition, macroArgs, nullptr);
} }
inline bool Debug::isWin32Success(int boolean) { inline Debug::Win32Result Debug::win32Call(int boolean) {
return boolean; return boolean ? Win32Result(0) : Win32Result(getWin32ErrorCode());
} }
inline bool Debug::isWin32Success(void* handle) { inline Debug::Win32Result Debug::win32Call(void* handle) {
// Assume null and INVALID_HANDLE_VALUE mean failure. // Assume null and INVALID_HANDLE_VALUE mean failure.
return handle != nullptr && handle != (void*)-1; return win32Call(handle != nullptr && handle != (void*)-1);
}
inline Debug::Win32Result Debug::winsockCall(int result) {
// Expect a return value of SOCKET_ERROR means failure.
return win32Call(result != -1);
} }
#endif #endif
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment