This is very similar to:
Turning GetLastError() into an exception
I also want to be able to add an std::string to the error:
class Win32Exception : public std::system_error
{
public:
Win32Exception(std::string ErrorDesc) : std::system_error(GetLastError(), std::system_category(), ErrorDesc)
{
}
};
The problem is that at least in DEBUG builds of VS2015 the construction of std::string resets GetLastError(). So by the time the Win32Exception calls GetLastError() it always gets zero/no error.
I could use const char*, but I also want to use std::wstring, which means I need to convert it to std::string or const char* (because the std::system_error expects std::string or const char*), and that leads me back to the same problem that the error gets reset.
Is there some elegant solution on how to throw Win32 errors easily, having the exception class capture GetLastError() and being able to add arbitrary info with std::string's?
You need to rewrite exception class constructor so it will take error code as parameter so error code:
Win32Exception
(
char const * const psz_description
, ::DWORD const error_code = ::GetLastError()
)
: std::system_error(error_code, std::system_category(), psz_description)
{
}
Related
I'm writing a library and want to return error codes whenever an error is returned by a remote system. The problem is that these are identified by strings, eg, "0A01" and also contain a message, and error code requires an integer as value.
What is the best way to implement an error code, with all the functionality that std::error_code provides but that uses strings as the value? How do I add an external error string to the std::error_code or std::error_category?
As mentioned in the comments, you must know the error codes, which could be received from the remote server.
The std::string which you receive from a remote server contains 2 parts as you said,
The problem is that these are identified by strings, eg, "0A01" and also contain a message, and error code requires an integer as value.
As you haven't shared the format of the error message, I am not adding the code for spiting it, split your string into 2 parts,
Error Code
Error Message
Now you can convert Error Code of type std::string to int by using std::stoi(error_code), So lets say
int error_code_int = std::stoi(string_to_hexadecimal(error_code));
And for std::error_category which serves as base class for our custom error messages, do this,
std::string message_received = "This is the message which received from remote server.";
struct OurCustomErrCategory : std::error_category
{
const char* name() const noexcept override;
std::string message(int ev) const override;
};
const char* OurCustomErrCategory::name() const noexcept
{
return "Error Category Name";
}
std::string OurCustomErrCategory::message(int error_code_int) const
{
switch (error_code_int)
{
case 1:
return message_received;
default:
return "(unrecognized error)";
}
}
const OurCustomErrCategory ourCustomErrCategoryObject;
std::error_code make_error_code(int e)
{
return {e, ourCustomErrCategoryObject};
}
int main()
{
int error_code_int = std::stoi(string_to_hexadecimal(error_code)); // error_code = 0A01
ourCustomErrCategoryObject.message(error_code_int);
std::error_code ec(error_code_int , ourCustomErrCategoryObject);
assert(ec);
std::cout << ec << std::endl;
std::cout << ec.message() << std::endl;
}
The output for above working example is
Error Category Name : 0A01
This is the message which received from remote server.
You can use function string_to_hexadecimal() from this post.
I hope that now you can modify the above code according to your needs.
Edit 1:
As you said that:
This assumes the dynamic message is a global value. How do I pass it
to an std::error_category object?
You can see that both std::error_code::assign and constructor std::error_code::error_code are taking parameters of int for error code number and error_category. So It is obvious that std::error_code can't take the dynamic message.
But wait, I said std::error_code are taking error_category as an argument in constructor, so is there any way, we can assign the dynamic message there ?
std::error_category states that:
std::error_category serves as the base class for specific error
category types.
So it means that the struct we derived from std::error_category at the following line
struct OurCustomErrCategory : std::error_category
can have a data member and we can assign it via member function, so our struct will become like that,
struct OurCustomErrCategory : std::error_category
{
std::string message_received;
OurCustomErrCategory(std::string m) : message_received(m) {}
const char* name() const noexcept override;
std::string message(int ev) const override;
};
and you can assign it like that wherever you want,
const OurCustomErrCategory ourCustomErrCategoryObject("This is the message which received from remote server.");
This is native C++. No .NET framework involved.
I am trying to figure out which exception is thrown when the CListBox gets an invalid parameter. Turns out MFC uses this exception quite a lot but I can't determine the actual exception type thrown. I have tried a lot of different types on the catch (int, const char , std:) but the only one besides catch(...) that catches it is the (const void *). Looking the memory structure, I still don't have a clue what is actually thrown.
Does any one know what it is or how to determine what it thrown?
This is a sample MFC application. ListBox is a CListBox. The application is nothing more than the default DialogBox based MFC application that VS builds automatically. The only change is that I added a list box and the code you see below in the OK button handler.
void CMFCApplication1Dlg::OnBnClickedOk()
{
try
{
CString Value;
ListBox.GetText( -1, Value );
Value = "none";
}
catch ( CException & exception )
{
exception.Delete();
}
catch ( const void * e )
{
}
catch (...)
{
}
CDialogEx::OnOK();
}
To explain why you get the exception, it looks like when you use the CString version of CListBox::GetText() it will throw an E_INVALIDARG exception if the passed index is not valid. Tracing through the MFC code is a bit of work but CListBox::GetText() looks like:
void CListBox::GetText(int nIndex, CString& rString) const
{
ASSERT(::IsWindow(m_hWnd));
GetText(nIndex, rString.GetBufferSetLength(GetTextLen(nIndex)));
rString.ReleaseBuffer();
}
CListBox::GetTextLen(-1) will return LB_ERR which is -1. If you follow the code for CString::GetBufferSetLength() you eventually end up in CString::SetLength():
void SetLength(_In_ int nLength)
{
ATLASSERT( nLength >= 0 );
ATLASSERT( nLength <= GetData()->nAllocLength );
if( nLength < 0 || nLength > GetData()->nAllocLength)
AtlThrow(E_INVALIDARG);
GetData()->nDataLength = nLength;
m_pszData[nLength] = 0;
}
with nLength == -1 and thus the exception.
The answer is:
catch ( const COleException * e )
I was using catch ( const COleException & e ) as all the text books say one should. Oh well.
Details can be found at Exception Handling in MFC, and Exceptions: Catching and Deleting Exceptions in particular.
Note that this invalid argument exception can now happen through a call to UpdateAllViews(). I traced the code deep through the MFC libraries. It results from a macro that only generates the throw for release builds.
Since it is so deep, it can occur from several other functions.
I came across the following code on VS2008
if (!CreateProcess( NULL,
const_cast<LPWSTR>(ss.str().c_str()),
NULL,
NULL,
FALSE,
CREATE_NO_WINDOW|NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi))
{
throw std::exception("Unable to format Device");
}
Now I am porting the code to mingw gcc and I get the error
error: no matching function for call to 'std::exception::exception(const char [23])'
Investigating the issue I noticed that Visual Studio has a file exception which does have an exception class and does take in char*. Some of the definitions look like this
__CLR_OR_THIS_CALL exception();
__CLR_OR_THIS_CALL exception(const char *const&);
__CLR_OR_THIS_CALL exception(const char *const&, int);
__CLR_OR_THIS_CALL exception(const exception&);
exception& __CLR_OR_THIS_CALL operator=(const exception&);
virtual __CLR_OR_THIS_CALL ~exception();
virtual const char * __CLR_OR_THIS_CALL what() const;
My question is how should I circumvent this build issue on mingw gcc ? Should I create a new class that inherits from std::runtime_error and throw that instead ?
Opinion plays a role here. The problem is that std::exception does not have a constructor that takes a string argument; this is an MSVC extension. I see two ways to go about it:
Don't pass a string argument
Don't use std::exception
The first case is straightforward; just use
throw std::exception();
The drawback is that you don't get a descriptive error message.
If the error message is important, using std::exception directly is not an option. In this case, you could use either std::logic_error or std::runtime_error, which inherit std::exception and do have constructors taking a string argument, so
throw std::runtime_error("Unable to format Device");
might already solve the problem. catch clauses that caught the std::exception will also catch the std::runtime_error. There is one potential problem, though: catch clauses that catch std::runtime_error would not have caught the std::exception but will catch this one.
This seems like a bit of a corner case, and it is entirely possible that it is not a problem for you. If, however, there is a possibility that along the call stack there is a catch clause that catches std::runtime_error but should not catch the exception thrown by this code, you could derive your own exception class from std::exception that does take a string argument. Because the class is new, it will not be caught by existing catch clauses. For example:
class descriptive_exception : public std::exception {
public:
descriptive_exception(std::string const &message) : msg_(message) { }
virtual char const *what() const noexcept { return msg_.c_str(); }
private:
std::string msg_;
}
And then
throw descriptive_exception("Unable to format Device");
This is arguably not very pretty, and it is unlikely that it is necessary, so the more probable solution is to use std::runtime_error or std::logic_error (or a class derived from one of them).
Whether std::logic_error or std::runtime_error is more appropriate is not very clear-cut; in this case I'd probably go with std::runtime_error because the error does not seem even theoretically predictable, but given that std::domain_error and std::future_error derive from std::logic_error, it would not be entirely out of place in that hierarchy. This is, I think, a matter of opinion.
In paragraph "How should I design my exception classes?" in this "Error and Exception Handling" Boost web page, it reads:
[...] 3. Don't embed a std::string object or any other data member or base class whose copy constructor could throw an exception.
I have to define an exception class to represent some form of run-time error on file access, so I was thinking to derive it from std::runtime_error, and add a FileName() attribute to get access to the file name for which the error occurred.
For simplicity sake, my intention was to add a std::wstring data member to store the file name (in Unicode), but the aforementioned suggestion kind of stopped me.
So, should I use a simple wchar_t buffer as a data member?
On modern desktop systems (which are my target platforms for this project), is it really important to pay attention to a dynamic string allocation for a file name? What is the likelihood of such allocation to fail? I can understand Boost's suggestion for limited-resource systems like embedded systems, but is it valid also for modern desktop PC's?
//
// Original design, using std::wstring.
//
class FileIOError : public std::runtime_error
{
public:
FileIOError(HRESULT errorCode, const std::wstring& filename, const char* message)
: std::runtime_error(message),
m_errorCode(errorCode),
m_filename(filename)
{
}
HRESULT ErrorCode() const
{
return m_errorCode;
}
const std::wstring& FileName() const
{
return m_filename;
}
private:
HRESULT m_errorCode;
std::wstring m_filename;
};
//
// Using raw wchar_t buffer, following Boost's guidelines.
//
class FileIOError : public std::runtime_error
{
public:
FileIOError(HRESULT errorCode, const wchar_t* filename, const char* message)
: std::runtime_error(message),
m_errorCode(errorCode)
{
// Safe string copy
// EDIT: use wcsncpy_s() with _TRUNCATE, as per Hans Passant's suggestion.
wcsncpy_s(m_filename, filename, _TRUNCATE);
}
HRESULT ErrorCode() const
{
return m_errorCode;
}
const wchar_t* FileName() const
{
return m_filename;
}
private:
HRESULT m_errorCode;
wchar_t m_filename[MAX_PATH]; // circa 260 wchar_t's
};
What is the likelihood of such allocation to fail?
Pretty low.
Usually for the purpose of code correctness you only really care whether that likelihood is zero or non-zero. Even on a system where the built in ::operator new never fails due to rampant over-commit, consider the likelihood that your code will be used in a program that replaces ::operator new. Or maybe an OS will externally limit the amount of memory a process is permitted to allocate, by ulimit -v or otherwise.
Next consider the consequences of it failing. terminate() is called. Maybe you can live with that, especially since it's unlikely to actually happen.
Basically, do you want your program to even try to exit cleanly with a reasonable error message in the case where you can't allocate memory? If so, write the extra code and accept the limit on the length of the error message. Because Boost is general-purpose library code, it doesn't assume on behalf of its users that they don't want to try.
If you are using C++11, you can use move semantics and move constructor of std::wstring, which is noexcept.
I have a Visual Studio 2008 C++ project that uses a Win32Exception class in cases where there is an exceptional error. The Win32Exception class looks like this:
/// defines an exception based on Win32 error codes. The what() function will
/// return a formatted string returned from FormatMessage()
class Win32Exception : public std::runtime_error
{
public:
Win32Exception() : std::runtime_error( ErrorMessage( &error_code_ ) )
{
};
virtual ~Win32Exception() { };
/// return the actual error code
DWORD ErrorCode() const throw() { return error_code_; };
private:
static std::string ErrorMessage( DWORD* error_code )
{
*error_code = ::GetLastError();
std::string error_messageA;
wchar_t* error_messageW = NULL;
DWORD len = ::FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
*error_code,
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
reinterpret_cast< LPWSTR >( &error_messageW ),
0,
NULL );
if( NULL != error_messageW )
{
// this may generate a C4244 warning. It is safe to ignore.
std::copy( error_messageW,
error_messageW + len,
std::back_inserter( error_messageA ) );
::LocalFree( error_messageW );
}
return error_messageA;
};
/// error code returned by GetLastError()
DWORD error_code_;
}; // class Win32Exception
The class works well in the situations it has been used in. What I would like to know is if there are any obvious cases where this will fail that I should be aware of. Any other gotchas, caveats, or general suggestions on improvements are welcome.
Please note that the boost library is not an option for this code.
This has already done by several people, including yours truly
https://github.com/BillyONeal/Instalog/blob/master/LogCommon/Win32Exception.hpp
https://github.com/BillyONeal/Instalog/blob/master/LogCommon/Win32Exception.cpp
Ironically, your code is not exception safe.
if( NULL != error_messageW )
{
// this may generate a C4244 warning. It is safe to ignore.
std::copy( error_messageW,
error_messageW + len,
std::back_inserter( error_messageA ) );
::LocalFree( error_messageW );
}
Note that if the back_inserter causes std::bad_alloc to be thrown, the memory allocated inside FormatMessage is leaked.
What a coincidence! I use a similar code in all my projects! It is actually a good idea.
This code is problematic:
// this may generate a C4244 warning. It is safe to ignore.
std::copy( error_messageW,
error_messageW + len,
std::back_inserter( error_messageA ) );
It just trancates WCHARs to chars. Your can either use FormatMessageA explicitly to get a message in the current code-page (ok, you can't as you said), or make convention that all your stings are UTF-8 encoded. I chose the later, see this why.
Error message by itself may be not useful. Capturing the stack trace may be a good idea.
Realize this is old, but at least with VC++ 2015 you can throw a system_error that will do all this with the system_category() function:
try
{
throw system_error(E_ACCESSDENIED, system_category(), "Failed to write file");
}
catch (exception& ex)
{
cout << ex.what();
}
This would print: "Failed to write file: Access is denied"
FormatMessage may itself fail. Some neutral "Unknown error with code %d" might be in order for such case.
Some error codes are not really errors (ERROR_ALREADY_EXISTS), depending on what user is expecting.
Some system functions return their own error codes (notable example being SHFileOperation) that you must handle separately. If you want them to be handled, that is.
Consider having additional information inside exception: where is exception being thrown from (source file and line), what system function caused exception, what were the parameters of the function (at least the identifying ones, like file name, handle value, or some such). Stack trace is also good.
What I would like to know is if there
are any obvious cases where this will
fail that I should be aware of. Any
other gotchas, caveats, or general
suggestions on improvements are
welcome.
The main I've problem I've had with such message retrieval has been ERROR_SUCCESS. It's rather perplexing when some operation fails, accompanied by error message "The operation succeeded". One wouldn't think that could happen, but it does.
I guess this is a special case of what Dialecticus noted, that "Some error codes are not really errors", but for most of those codes at least the message is generally acceptable.
The second problem is that most Windows system error message have a carriage return + linefeed at the end. It's problematic for insertion of messages into other text, and it breaks the convention for C++ exception messages. So, good idea to remove those chars.
Now, instead of repeating all that others have already noted, a few words about the design.
The ErrorMessage function would much more usable if was made public or moved out of the class, and took the error code by value, instead of taking pointer argument. This is the principle of keeping separate responsibilities separate. Promotes reuse.
The code in ErrorMessage would be more clear and safe and efficient if you used a destructor to deallocate the memory. Then you could also just construct the string directly in the return statement instead of using a copy loop with back inserter.
Cheers & hth.,
I was recently working on a very similar class and after reading this thread tried to make the copying part exception-safe. I introduced a little helper class that does nothing but hold the pointer to the string returned by ::FormatMessage and free it with ::LocalFree in its destructor. Copying, assigning and moving is not allowed, so one cannot get into trouble.
Here is what I came up with in total:
class windows_error {
public:
windows_error(wchar_t const* what);
// Getter functions
unsigned long errorCode() const { return _code; }
wchar_t const* description() const { return _what; }
std::wstring errorMessage() const { return _sys_err_msg; }
private:
unsigned long _code;
wchar_t const* _what;
std::wstring _sys_err_msg;
};
// This class outsources the problem of managing the string which
// was allocated with ::LocalAlloc by the ::FormatMessage function.
// This is necessary to make the constructor of windows_error exception-safe.
class LocalAllocHelper {
public:
LocalAllocHelper(wchar_t* string) : _string(string) { }
~LocalAllocHelper() {
::LocalFree(_string);
}
LocalAllocHelper(LocalAllocHelper const& other) = delete;
LocalAllocHelper(LocalAllocHelper && other) = delete;
LocalAllocHelper& operator=(LocalAllocHelper const& other) = delete;
LocalAllocHelper& operator=(LocalAllocHelper && other) = delete;
private:
wchar_t* _string;
};
windows_error::windows_error(wchar_t const* what)
: _code(::GetLastError()),
_what(what) {
// Create a temporary pointer to a wide string for the error message
LPWSTR _temp_msg = 0;
// Retrieve error message from error code and save the length
// of the buffer which is being returned. This is needed to
// implement the copy and assignment constructor.
DWORD _buffer_size = ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, _code, 0, _temp_msg, 0, NULL);
if(_buffer_size) {
// When calling _sys_err_msg.resize an exception could be thrown therefore
// the _temp_msg needs to be a managed resource.
LocalAllocHelper helper(_temp_msg);
_sys_err_msg.resize(_buffer_size + 1);
std::copy(_temp_msg, _temp_msg + _buffer_size, _sys_err_msg.begin());
}
else {
_sys_err_msg = std::wstring(L"Unknown error. (FormatMessage failed)");
}
}
Maybe this will be useful for some of you.