Descriptive exceptions
Hello!
Some time ago I wrote a one-header class implementing Java-like
exception system. I don't want to discuss which is better, but AFAIK
there's a bug in the Error class. If, by any reason, memory gets
exhausted during construction of my exception, the program will get
terminated, won't it?
The problem is with stringstream and string classes. To give it no-throw
policy, I think I need to write a custom streambuf. Or do I need to
provide my own allocator for stringstream and strings too? Is there a
book which has a decription or example of implementing this or discusses
the problem?
The code follows. Some names are intentionally longish, so they don't
fit into 72 columns boundary, sorry.
Stefan
//
// Descriptive Exceptions
//
// You are allowed to use and modify this code in any way.
// NO WARRANTY POLICY
//
// Author: Stefan Chrobot
// Elblag, July, December 2006
//
// WARNING: This file may cause embedding of source file names
// into the resulting binary file. See below for more information.
//
// Example:
//
// 51: void checkIntegrity()
// 52: {
// 53: reportFunc();
// 54: if(...)
// 55: throw Error(msg << "Invalid value = " << value);
// 56: // ...
// 57: }
//
// 89: int main()
// 90: {
// 91: reportFunc();
// 91: try
// 92: {
// 93: checkIntegrity();
// 94: }
// 95: catch(exception& e)
// 96: {
// 97: cout << e.what() << endl;
// 98: }
//
// This will produce exception with message similar to this:
//
// Exception:
// Invalid value = 497
// at void checkIntegrity() (file: c:\project\source.cpp, line: 51)
//
// EXCEPTION_FUNCTION_MACRO - platform-specific macro that is
substituted
// with current function name
// EXCEPTION_NO_FUNCTION_MACRO - define this if your platform does not
// support __FUNCTION__ macro
// EXCEPTION_HIDE_SOURCE_LOCATION - disables embedding of file names
into binary
// EXCEPTION_STACK_SIZE - size of the stack, depth of recursion
// EXCEPTION_FUNCTION_OFFSET - offset (in lines) from function
signature to reportFunc();
//
#ifndef ERROR_H
#define ERROR_H
#include <stdexcept>
#include <sstream>
#include <ostream>
#include <cstdlib>
#include <string>
#ifdef EXCEPTION_NO_FUNCTION_MACRO
#define EXCEPTION_HIDE_SOURCE_LOCATION
#endif
#ifndef EXCEPTION_FUNCTION_MACRO
#define EXCEPTION_FUNCTION_MACRO __FUNCSIG__
#endif
#ifndef EXCEPTION_STACK_SIZE
#define EXCEPTION_STACK_SIZE 128
#endif
#ifndef EXCEPTION_FUNCTION_OFFSET
#define EXCEPTION_FUNCTION_OFFSET 2
#endif
namespace Project
{
class Error;
class ExceptionStream
{
public:
static ExceptionStream& getStream()
{
static ExceptionStream exceptionStream;
return exceptionStream;
}
template<typename T>
ExceptionStream& operator<<(const T& t)
{
stream << t;
return *this;
}
ExceptionStream& operator<<(std::ostream&
(*manipulator)(std::ostream&))
{
manipulator(stream);
return *this;
}
friend class Error;
private:
ExceptionStream() { }
ExceptionStream(const ExceptionStream&);
ExceptionStream& operator=(const ExceptionStream&);
std::stringstream stream;
};
static ExceptionStream& msg = ExceptionStream::getStream();
class ExceptionDebugFunctionStack
{
public:
explicit ExceptionDebugFunctionStack(const char* function,
const char* file,
int lineNumber)
{
int i = getIndex();
if(i < EXCEPTION_STACK_SIZE)
{
getStack()[i].function = function;
getStack()[i].file = file;
getStack()[i].lineNumber = lineNumber;
}
++getIndex();
}
~ExceptionDebugFunctionStack()
{
--getIndex();
}
friend class Error;
private:
static int index;
struct LocationInfo
{
const char* function;
const char* file;
int lineNumber;
};
static int& getIndex()
{
static int index = 0;
return index;
}
static LocationInfo* getStack()
{
static LocationInfo stack[EXCEPTION_STACK_SIZE];
return stack;
}
};
class Error : public std::exception
{
public:
Error(ExceptionStream& message, int lineNumber)
: exception(), str()
{
std::stringstream ss;
ss << "Exception in line " << lineNumber << ":\n" <<
message.stream.rdbuf();
#ifndef EXCEPTION_HIDE_SOURCE_LOCATION
ss << "\n";
int max = EXCEPTION_STACK_SIZE;
if(ExceptionDebugFunctionStack::getIndex() - 1 <
EXCEPTION_STACK_SIZE)
max = ExceptionDebugFunctionStack::getIndex();
else
ss << " at <debug stack exhausted>\n";
for(int i = max - 1; i >= 0; --i)
{
ss << " at " <<
ExceptionDebugFunctionStack::getStack()[i].function
<< " (file: " <<
ExceptionDebugFunctionStack::getStack()[i].file
<< ", line: " <<
ExceptionDebugFunctionStack::getStack()[i].lineNumber
- EXCEPTION_FUNCTION_OFFSET
<< ")\n";
}
#endif
str = ss.str();
}
virtual ~Error() throw()
{
}
virtual const char* what() const throw()
{
return str.c_str();
}
private:
std::string str;
};
}
#ifdef EXCEPTION_HIDE_SOURCE_LOCATION
#define reportFunc()
#endif
#ifndef EXCEPTION_HIDE_SOURCE_LOCATION
#define reportFunc() ExceptionDebugFunctionStack
tmpEDFS(EXCEPTION_FUNCTION_MACRO, __FILE__, __LINE__)
#endif
#define Error(message) Error(message, __LINE__)
#endif
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]