How to avoid placing std :: string in an exception class?

Here is my exception class:

class Win32Failure : public std::exception { public: Win32Failure( char const* win32_function_name, LONG error_code ); char const* win32_function_name() const { return win32_function_name_; } LONG error_code() const { return error_code_; } virtual char const* what() const; private: std::string GetFormattedMessage() const; char const* win32_function_name_; LONG error_code_; std::string error_text_; }; Win32Failure::Win32Failure( char const* win32_function_name, LONG error_code ) : error_code_(error_code) , win32_function_name_(win32_function_name) { std::stringstream error_msg; error_msg << win32_function_name << " failed with code: " << error_code << " (" << GetFormattedMessage() << ")" ; error_text_ = error_msg.str(); } std::string Win32Failure::GetFormattedMessage() const { TCHAR message_buffer[1000]; FormatMessage( //FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error_code_, 0, // Default language reinterpret_cast<LPTSTR>(&message_buffer), sizeof(message_buffer) / sizeof(TCHAR), NULL ); return std::string(message_buffer); } char const* Win32Failure::what() const { return error_text_.c_str(); } 

recommendations for eliminating exceptions recommend not placing any objects that allocate memory as members of my exception class. In this case, using std::string violates this. I respect the rule for this, however I cannot think of a way to implement an override of what() without using std :: string to manage the memory (as opposed to the caller having to manage it for me).

I could use a fixed-size buffer as a member and use C library functions (e.g. snprintf() ) to do the job, but this is not very idiomatic for C ++ and therefore not an ideal solution.

Is this a suitable implementation of the exception class? If not, what improvements can be made?

+8
c ++ design exception exception-handling
source share
3 answers

For what it's worth, all exception types defined in <stdexcept> take std::string as arguments. Library designers may interpret this as “good.” I think the main argument against this is that if you are in a memory-limited environment, you may not be able to allocate memory to throw your exception.

+6
source share

Exceptions should protect against potential depletion of resources during their construction. Using dynamic arrays in exceptions is not good (unless it is done with pointers, and if distribution is not done). Using std::string to transfer some information to the user is doubly: firstly, it is a dynamic array, the second useless basically - the function that throws an exception does not know the circumstances to try to explain reasonably why it failed.

Exceptions should do useful things, such as stack tracing + restoring function parameters (if possible) + identifier to an external message (for example, a dynamic library resource) if a low-level formatted message is needed for the end user. If exceptions are made to the end user, he should make it in the form of a coherent formatted error message from an external row table template (possibly adding some runtime parameters). If an exception is used to help debugging and makes it to the developer, then the stack trace, function parameters, machine state is useful, some hard-coded common line is not.

EDIT: It sounds like you're trying to make C ++ exception wrappers for Windows API calls, right? If so, you should consider a few things:

  • using _ set_se_translator () to handle SE as C ++ exceptions;
  • using dbghelp.dll StackWalk64 () , SymFromAddr () and similar functions to generate a traceable stack trace (only the address is useful for debugging offsite) in the exception constructor;
  • using the built-in common shell to check the status of the error returned by the Windows API, which throws an exception on the specified condition or forwards the return value as the return type. Just make sure that there is no overhead (inline + rvalue templates + links completely eliminate overhead, and also make sure that you DO NOT directly disable the checker exception, delegate it non-inline functions to avoid the compiler overhead for functions that explicitly cause exceptions).
+2
source share

as part of the private data of the class has a static char buffer (consider using TLS if multithreading)

what () just returns a pointer to this buffer.

in GetFormattedMessage () you fill the buffer (as it is now) (and possibly convert from unicode / wchars if necessary, since you are dealing with TCHAR)

as a static member of a class, the buffer is pre-allocated on the stack before any exception occurs.

you do not need to manage the memory as it was allocated on the stack. however, you chew 1000 bytes of stack space (usually this is not a big deal)

there is only 1 buffer for storing the error text, but at any given time there should be only one of your own exception (I think?)

0
source share

All Articles