📄 syserr.cpp
字号:
// syserr.cpp// code for syserr.h// copyright SafeTP Development Group, Inc., 2000 Terms of use are as specified in license.txt#include "syserr.h" // this module// ---------------- portable code ----------------char const * const xSysError::reasonStrings[] = { "No error occurred", "File not found", "Path not found", "Access denied", "Out of memory (maybe)", // always a suspicious message "Invalid pointer address", "Invalid data format", "Invalid argument", "Attempt to modify read-only data", "The object already exists", "Resource is temporarily unavailable", "Resource is busy", "Unknown or unrecognized error", "(invalid reason code)" // corresponds to NUM_REASONS};STATICDEF char const *xSysError:: getReasonString(xSysError::Reason r){ // at compile-time, verify consistency between enumeration and string array // (it's in here because, at least on Borland, the preprocessor respects // the member access rules (strangely..)) #ifdef __BORLANDC__ #if TABLESIZE(reasonStrings) != NUM_REASONS+1 #error table and enumeration do not match #endif #endif if ((unsigned)r < NUM_REASONS) { return reasonStrings[r]; } else { return reasonStrings[NUM_REASONS]; }}xSysError::xSysError(xSysError::Reason r, char const *syscall, char const *ctx) : xBase(constructWhyString(r, syscall, ctx)), reason(r), reasonString(getReasonString(r)), sysErrorCode(0), syscallName(syscall), context(ctx){}STATICDEF string xSysError:: constructWhyString(xSysError::Reason r, char const *syscall, char const *ctx){ xassert(syscall); if (ctx != NULL) { return stringb(syscall << ": " << getReasonString(r) << ", " << ctx); } else { return stringb(syscall << ": " << getReasonString(r)); }}xSysError::xSysError(xSysError const &obj) : xBase(obj), reason(obj.reason), reasonString(obj.reasonString), sysErrorCode(obj.sysErrorCode), syscallName(obj.syscallName), context(obj.context){}xSysError::~xSysError(){}STATICDEF void xSysError:: xsyserror(char const *syscallName, char const *context){ // retrieve system error code int code = getSystemErrorCode(); // translate it into one of ours Reason r = portablize(code); // construct an object to throw xSysError obj(r, syscallName, context); obj.sysErrorCode = code; // toss it THROW(obj);}string sysErrorCodeString(int systemErrorCode, char const *syscallName, char const *context){ return xSysError::constructWhyString( xSysError::portablize(systemErrorCode), syscallName, context);}// ----------------------- Win32 code ------------------------------------#ifdef __WIN32__#ifdef USE_MINWIN_H# include "minwin.h" // api#else# include <windows.h> // api#endif#include <errno.h> // errnoSTATICDEF int xSysError::getSystemErrorCode(){ int ret = GetLastError(); // update: The confusing behavior I observed was with the Borland 4.5 // runtime libraries. When I linked with the non-multithreaded versions, // GetLastError worked as expected. But when I linked with the // multithreaded versions, GetLastError always returned 0, and I had to // consult errno instead. Further, the errno values didn't coincide // exactly with expected values. Therefore, the solution (at least for // now) is to link only with the non-multithreaded versions, and not // look to errno for anything. #ifdef MT # error something is fishy with multithreaded.. #endif // I thought something was happening, but now it seems // it's not.. #if 0 // ? if (ret == ERROR_SUCCESS) { // for some calls, like mkdir, GetLastError is not // set, but errno is; fortunately, MS decided to // (mostly) overlap GetLastError codes with errno codes, // so let's try this: return errno; } #endif // 0 return ret;}STATICDEF xSysError::Reason xSysError::portablize(int sysErrorCode){ // I'd like to put this into a static class member, but then // the table would have to prepend R_ constants with xSysError::, // which is a pain. static struct S { int code; Reason reason; } const arr[] = { { ERROR_SUCCESS, R_NO_ERROR }, { ERROR_FILE_NOT_FOUND, R_FILE_NOT_FOUND }, { ERROR_PATH_NOT_FOUND, R_PATH_NOT_FOUND }, { ERROR_ACCESS_DENIED, R_ACCESS_DENIED }, { ERROR_NOT_ENOUGH_MEMORY, R_OUT_OF_MEMORY }, { ERROR_OUTOFMEMORY, R_OUT_OF_MEMORY }, { ERROR_INVALID_BLOCK, R_SEGFAULT }, { ERROR_BAD_FORMAT, R_FORMAT }, { ERROR_INVALID_DATA, R_INVALID_ARGUMENT }, { ERROR_WRITE_PROTECT, R_READ_ONLY }, { ERROR_ALREADY_EXISTS, R_ALREADY_EXISTS }, // ??? R_AGAIN { ERROR_BUSY, R_BUSY }, }; loopi(TABLESIZE(arr)) { if (arr[i].code == sysErrorCode) { // found it return arr[i].reason; } } // I don't know return R_UNKNOWN;}// ---------------------- unix ---------------------------#else // unix#include <errno.h> // errno// mappings to a set of error codes I can use below// (I am sure I've already done this somewhere else, but I// may have lost that file)#ifndef EZERO# define EZERO 0#endif#ifndef ENOFILE# define ENOFILE ENOENT#endif#ifndef ENOPATH# define ENOPATH ENOENT#endif#ifndef EINVMEM# define EINVMEM EFAULT#endif#ifndef EINVFMT# define EINVFMT 0 // won't be seen because EZERO is first#endifSTATICDEF int xSysError::getSystemErrorCode(){ return errno; // why was this "errno()"??}STATICDEF xSysError::Reason xSysError::portablize(int sysErrorCode){ // I'd like to put this into a static class member, but then // the table would have to prepend R_ constants with xSysError::, // which is a pain. static struct S { int code; Reason reason; } const arr[] = { { EZERO, R_NO_ERROR }, { ENOFILE, R_FILE_NOT_FOUND }, { ENOPATH, R_PATH_NOT_FOUND }, { EACCES, R_ACCESS_DENIED }, { ENOMEM, R_OUT_OF_MEMORY }, { EINVMEM, R_SEGFAULT }, { EINVFMT, R_FORMAT }, { EINVAL, R_INVALID_ARGUMENT }, { EROFS, R_READ_ONLY }, { EEXIST, R_ALREADY_EXISTS }, { EAGAIN, R_AGAIN }, { EBUSY, R_BUSY }, }; loopi(TABLESIZE(arr)) { if (arr[i].code == sysErrorCode) { // found it return arr[i].reason; } } // I don't know return R_UNKNOWN;}#endif // unix// ------------------ test code ------------------------#ifdef TEST_SYSERR#include "test.h" // various#include "nonport.h" // changeDirectory// this is a macro because failingCall needs to be// call-by-name so I can wrap it in a try#define TRY_FAIL(failingCall, expectedCode) \ try { \ if (failingCall) { \ cout << "ERROR: " #failingCall " should have failed\n"; \ } \ else { \ /* got an error to test */ \ xsyserror(#failingCall); \ } \ } \ catch (xSysError &x) { \ if (x.reason != xSysError::expectedCode) { \ cout << "ERROR: " #failingCall " returned '" \ << x.reasonString << "' but '" \ << xSysError::getReasonString(xSysError::expectedCode) \ << "' was expected\n"; \ errors++; \ } \ }void entry(){ int errors = 0; xBase::logExceptions = false; //nonportFail = xSysError::xsyserror; //TRY_FAIL(createDirectory("/tmp\\Scott\\"), // R_ALREADY_EXISTS); TRY_FAIL(changeDirectory("some.strange.name/yadda"), R_PATH_NOT_FOUND); TRY_FAIL(createDirectory("/tmp"), R_ALREADY_EXISTS); TRY_FAIL(isDirectory("doesnt.exist"), R_FILE_NOT_FOUND); if (errors == 0) { cout << "success!\n"; } else { cout << errors << " error(s)\n"; }}USUAL_MAIN#endif // TEST_SYSERR
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -