Related
(using windows form C++ Microsoft visual studio)
this is the code of a function, that looks for an object of index pos
in a binary file and returns it.
template <class t>
t Archivos<t>::get(string nombre,int pos)
{
t a;
FILE* p = fopen(nombre.c_str(),"rb+");
fseek(p,pos*sizeof(a),SEEK_SET);
fread(&a,sizeof(a),1,p);
fflush(p);
fclose(p);
return a;
}
the first time I run the program and write the objects in the file
it works without any problem(I use this function to show the objects
stored in the file). But once I close the program and run it again it
crashes, when I try to show the objects stored.
I looked for errors using breakingpoints, and found out that it finds the object
of index pos without any issue and stores it in the variable a, but it crashes
in the return sentence.
the error says:
An unhandled exception of type
'System.AccessViolationException' occurred
in proyectoAlgoritmos.exe
Additional information: Attempted to read or
write protected memory. This is often an indication that
other memory is corrupt.
I would really appreciate some help. thanks in advance
I have the following "first-chance exception" message which is coming from a DLL I wrote which is running inside an executable that I did not write. That is, the DLL is a plugin. The first time this exception fires, an attempt to open a shared memory map file is failing. If I ignore first chance exceptions and just run, the application freezes or crashes eventually.
First-chance exception at 0x76a7c41f in notmyexe.exe: Microsoft C++ exception: boost::interprocess::interprocess_exception at memory location 0x002bc644..
After several hours it appears to be caused by a block of code which is looping endlessly until an expected exception condition clears. It turns out that if it never does clear, and then, eventually, this exception turns into another low-level-exception-condition and/or turns into heap corruption. All of this is just in an effort to open a shared memory area using Boost::interprocess.
The first thing that complicates things is that on my Visual C++ 2008 based project, the first boost::interprocess::interprocess_exception first-chance exception is not thrown and identified as the location where it came from because the Visual C++ 2008 compiler cannot find the complex boost-flavor templates code in question. However by single stepping through the assembly language view, I found the code that blows up.
The top level line of my own code that it all starts to go bad on is:
segment = new managed_shared_memory( open_or_create
, MEMORY_AREA_NAME
, SHARED_AREA_SIZE );
The above managed_shared_memory class is from interprocess_fwd.hpp, and is a standard part of the boost shared memory API/headers. Because it's template based, the above expands into about a 2Kchars long C++ boost template expression, which is truncated at different lengths by the linker, and by the debugger. Visual C++ 2008 has no more source code debugging capabilities, it seems when these limits are in play.
For example, when it blows up I get this call stack:
KernelBase.dll!76a7c41f()
[Frames below may be incorrect and/or missing, no symbols loaded for KernelBase.dll]
KernelBase.dll!76a7c41f()
> msvcr90d.dll!_malloc_dbg(unsigned int nSize=2290875461, int nBlockUse=264, const char * szFileName=0x01fcb983, int nLine=1962999808) Line 160 + 0x1b bytes C++
8bfc4d89()
No actual end-user written source functions appear in the stack dump above.
How should I debug this? Secondly, is there some known problem with boost-interprocess, with Visual C++ 2008? Third, what is the boost code below doing and why must it endlessly loop?
boost::interprocess::basic_managed_shared_memory<char,
boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,
boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,
boost::interprocess::iset_index>::basic_managed_shared_memory<char,boo...
Further layers down, we get to:
basic_managed_shared_memory (open_or_create_t,
const char *name, size_type size,
const void *addr = 0, const permissions& perm = permissions())
: base_t()
, base2_t(open_or_create, name, size, read_write, addr,
create_open_func_t(get_this_pointer(),
ipcdetail::DoOpenOrCreate), perm)
{}
Anyways, don't try to debug this at home kids, here's what happens:
Finally, using my ninja-like ability to single step through several million lines of assembly language I have defeated Visual C++ 2008's evil debugger limitations, and have found the code in question.
This is what is blowing up in fact: create_device<FileBased>(dev....
Some context here:
managed_open_or_create_impl.h line 351...
else if(type == DoOpenOrCreate){
//This loop is very ugly, but brute force is sometimes better
//than diplomacy. If someone knows how to open or create a
//file and know if we have really created it or just open it
//drop me a e-mail!
bool completed = false;
while(!completed){
try{
create_device<FileBased>(dev, id, size, perm, file_like_t()); // <-- KABOOM!
created = true;
completed = true;
}
catch(interprocess_exception &ex){
if(ex.get_error_code() != already_exists_error){
throw;
}
else{
try{
DeviceAbstraction tmp(open_only, id, read_write);
dev.swap(tmp);
created = false;
completed = true;
}
catch(interprocess_exception &e){
if(e.get_error_code() != not_found_error){
throw;
}
}
catch(...){
throw;
}
}
}
catch(...){
throw;
}
thread_yield();
}
}
I believe I've had some of the same issues you are having. Take a look at the function "shared_memory_object::priv_open_or_create" in "\boost\interprocess\shared_memory_object.hpp". At the top of that function is another function call "create_tmp_and_clean_old_and_get_filename" that starts a function chain that winds up deleting the shared memory file. I wound up moving that function call lower in the priv_open_or_create function around where the case statements start. I believe I'm using boost 1.48. Here's the final version of that function that I modified:
inline bool shared_memory_object::priv_open_or_create
(ipcdetail::create_enum_t type, const char *filename, mode_t mode, const permissions &perm)
{
m_filename = filename;
std::string shmfile;
std::string root_tmp_name;
//Set accesses
if (mode != read_write && mode != read_only){
error_info err = other_error;
throw interprocess_exception(err);
}
switch(type){
case ipcdetail::DoOpen:
ipcdetail::get_tmp_base_dir(root_tmp_name);
shmfile = root_tmp_name;
shmfile += "/";
shmfile += filename;
m_handle = ipcdetail::open_existing_file(shmfile.c_str(), mode, true);
break;
case ipcdetail::DoCreate:
ipcdetail::create_tmp_and_clean_old_and_get_filename(filename, shmfile);
m_handle = ipcdetail::create_new_file(shmfile.c_str(), mode, perm, true);
break;
case ipcdetail::DoOpenOrCreate:
ipcdetail::create_tmp_and_clean_old_and_get_filename(filename, shmfile);
m_handle = ipcdetail::create_or_open_file(shmfile.c_str(), mode, perm, true);
break;
default:
{
error_info err = other_error;
throw interprocess_exception(err);
}
}
//Check for error
if(m_handle == ipcdetail::invalid_file()){
error_info err = system_error_code();
this->priv_close();
throw interprocess_exception(err);
}
m_mode = mode;
return true;
}
BTW, if anyone knows the official channels I can go through to try and get this verified and added to boost please let me know as I hate modifying stuff like this without knowing its full effect.
Hope this helps!
Boost is full of both amazing and scary things.
A simple workaround on Windows, could be to switch to managed_windows_shared_memory instead of managed_shared_memory, you can solve a variety of nasty crash/hang problems, and one sort of crash/hang problem appears to be caused, in turn by the differences between Windows file system behaviour and unix file system behaviour, and in particular, it seems that with boost and managed_shared_memory on Windows, it is possible to run afoul of Windows file system locking limitations. I am informed that an effort to deal with this has been completed in BOost 1.53, but I am using Boost 1.53 and I still have this problem.
With regular managed_shared_memory on Windows, you get persistence beyond the life of any of the client or server applications. This might be desirable in some people's cases thus the workaround is not a real fix for those people.
However, in my case, I didn't really need that anyways, although I had thought it would be handy, it turns out to be more pain than it's worth, at least with the current Boost implementation on Windows.
I would like to also point out that deletion of the shared memory file appears to be the root cause of the race condition that is causing the problem experienced in the question above. Proper synchronization around creation and checking, and deletion of the file appears to be essential to a real world implementation of the system, and in particular, it appears to be a devastating problem, if you have your master (server) delete the shared memory file while some clients are still using it. A reboot appears necessary to clear the resulting lock+NTFS-filesystem mess.
If I find a real solution I'll post it, but the above is more information than I could find anywhere else. Be wary of managed_shared_memory and consider using managed_windows_shared_memory and forget about trying to make the "persistent shared memory" idea work. Rather, use non-persistent windows-only managed_windows_shared_memory.
Solving this, while keeping the managed_shared_memory class in my application probably means wrapping all access to the managed_shared_memory object in yet another level of interprocess synchronization primitives, or even with a raw Win32 API mutex. Boost could do something equivalent, but probably would introduce yet more accidental complexity.
(Aside: Am I the only one here who thinks that Template-All-the-things has been carried too far in general use, and especially in Boost, these days?)
Update 2: I have found an alternative way of freezing up managed_shared_memory and this in turn freezes up any app you use it from. I did not expect it to be so easy to create deadlocks with Boost but it is pretty easy to do. The mutex code inside the implementation will freeze forever waiting for a mutex that some other user of the managed shared memory has gone away without releasing. This endless sleep waiting for a mutex that is never going to be released, is another deep design flaw in this boost interprocess implementation that so far, I have counted several serious design flaws in, at least on windows. Maybe it works beautifully on Linux.
The code that exhibits this is the find() method, called like this:
boost::interprocess::managed_shared_memory * segment;
std::pair<MyType*, std::size_t> f = segment->find<MyType>(name);
Here is the stack trace for a mutex deadlock (aka endless wait, frozen task):
Only solution when you get here, is to delete the shared memory area, after stopping or killing all hung processes that are waiting for this mutex.
> myapp.exe!boost::interprocess::winapi::sched_yield() Line 998 C++
myapp.exe!boost::interprocess::ipcdetail::thread_yield() Line 60 + 0xe bytes C++
myapp.exe!boost::interprocess::ipcdetail::spin_mutex::lock() Line 71 C++
myapp.exe!boost::interprocess::ipcdetail::spin_recursive_mutex::lock() Line 91 C++
myapp.exe!boost::interprocess::interprocess_recursive_mutex::lock() Line 161 C++
myapp.exe!boost::interprocess::scoped_lock<boost::interprocess::interprocess_recursive_mutex>::lock() Line 280 C++
myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_get_lock(bool use_lock=true) Line 1340 C++
myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_generic_find<char>(const char * name=0x00394290, boost::interprocess::iset_index<boost::interprocess::ipcdetail::index_config<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0> > > & index={...}, boost::interprocess::ipcdetail::in_place_interface & table={...}, unsigned int & length=1343657312, boost::interprocess::ipcdetail::bool_<1> is_intrusive={...}, bool use_lock=true) Line 854 + 0x11 bytes C++
myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::priv_find_impl<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290, bool lock=true) Line 728 + 0x25 bytes C++
myapp.exe!boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(const char * name=0x00394290) Line 423 + 0x1e bytes C++
myapp.exe!boost::interprocess::ipcdetail::basic_managed_memory_impl<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index,8>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...}) Line 346 + 0x23 bytes C++
myapp.exe!boost::interprocess::basic_managed_shared_memory<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index>::find<boost::container::map<AreaKeyType,DATA_AREA_DESC,std::less<AreaKeyType>,boost::interprocess::allocator<std::pair<AreaKeyType const ,DATA_AREA_DESC>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,boost::interprocess::iset_index> > > >(boost::interprocess::ipcdetail::char_ptr_holder<char> name={...}) Line 208 + 0x10 bytes C++
myapp.exe!CCommonMemory::AllocateOrFindAreaMap(const char * name=0x00394290) Line 128 C++
I am using the Qt script engine in my application as an alternative way for the user to access its functionality. As such, I export some C++ classes to the Qt ScriptEngine, that will serve as the interface to the application. The problem is, these C++ classes can throw exceptions.
I have a "ScriptInterface" class running on its own thread, listening for requests to process scripts. So when I evaluate a user's script, I have a try/catch block around it to handle exceptions, and print the error to the console in the application.
...
try {
m_engine->evaluate(script, name);
}
catch (Exception const& e) {
// deal with it
}
catch (...) {
// scary message
}
This works perfectly in windows... but doesn't work in linux- the program terminates with this message:
terminate called after throwing an instance of 'Basilisk::InvalidArgumentException'
what(): N8Basilisk24InvalidArgumentExceptionE
Aborted
I had a hunch that it was because the exceptions bubbled up to the event handler (since the script engine uses signals to call the functions in my exported classes), so I reimplemented QApplication::notify, to handle exceptions there, but they weren't caught.
My question is, am I doing something fundamentally wrong? Also, as an alternative, is it possible to explicitly throw script exceptions from within my C++ classes?
Thanks in advance
EDIT: fixed the description to include the catch(...) statement.
UPDATE (SOLUTION): I "fixed" this problem by following a strategy similar to the one outlined in the accepted answer. Although I haven't gone to the source of why the exceptions don't get caught on linux (my suspicion now, is that m_engine->evaluate spawn a seperate thread on linux), but I have started using the intended way of exception throwing in Qt Scripts, and that is QScriptContext::throwError().
In cases where my function would look like this: (random example)
void SomeClass::doStuff(unsigned int argument) {
if (argument != 42) {
throw InvalidArgumentException(
"Not the answer to Life, the Universe and Everything.");
}
// function that is not part of the scripting environment,
// and can throw a C++ exception
dangerousFunction(argument);
}
It is now this: (pay particular attention to the return type)
QScriptValue SomeClass::doStuff(unsigned int argument) {
if (argument != 42) {
// assuming m_engine points to an instance of
// QScriptEngine that will be calling this function
return m_engine->currentContext()->throwError(QScriptContext::SyntaxError,
"Not the answer to Life, the Universe and Everything.");
}
try {
// function that is not part of the scripting environment,
// and can throw a C++ exception
dangerousFunction(argument);
} catch (ExpectedException const& e) {
return m_engine->currentContext()->throwError(QScriptContext::UnknownError,
e.message());
}
// if no errors returned, return an invalid QScriptValue,
// equivalent to void
return QScriptValue();
}
So where does one deal with these script errors? After the call to QScriptEngine::evaluate() you can check whether there are any uncaught exceptions, with QScriptEngine::hasUncaughtException(), obtain the error object with uncaughtException(), and now you have the message, the trace, and line number in the script where the error occured!
Hope this helps someone out!
I ran into a similar type of problem when trying to use SWIG with Python to wrap C++ libraries. Eventually what happened was that I made a stub for all the wrapped classes which caught the exception and failed quietly. Luckily I had the luxury of wrapping functionality which only passed container classes and state pattern objects, so I could easily check if something was amiss. May I suggest the same for you?
Wrap the functions you want with another function, same interface except the return value.
Create an object that contains not only the requested return type but also an error indicator.
Have the script make sure to check for the exceptions.
And yes, it's very possible for a script engine to throw C++ exceptions if you've given it access to an exception factory (a class whose sole purpose is to throw C++ exceptions.)
Run your program under a debugger and place a breakpoint inside your runtime library's terminate() function. That way you'll stop on terminate() in the debugger and by inspecting the call stack you will then see from where terminate() was called.
In C++, RAII is often advocated as a superior approach to exception handling: if an exception is thrown, the stack is unwound, all the destructors are called and resources are cleaned up.
However, this presents a problem with error reporting. Say a very generic function fails, the stack is unwound to the top level and all I see in the logs would be:
Couldn't read from socket: connection reset by peer.
...or any equally generic message. This doesn't say much about the context from which the exception is thrown. Especially if I'm running something like an event queue processing loop.
Of course I could wrap every call to socket reads with a try/catch block, catch the exception, construct a new one with more detailed context information and re-throw it, but it defeats the purpose of having RAII, and is slowly but surely becoming worse than handling return error codes.
What's a better way for detailed for error reporting in standard C++? I'm also open to suggestions involving Boost.
As James McNellis suggested here, there is a really neat trick involving a guard object and the std::uncaught_exception facility.
The idea is to write code like this:
void function(int a, int b)
{
STACK_TRACE("function") << "a: " << a << ", b: " << b;
// do anything
}
And have the message logged only in case an exception is actually thrown.
The class is very simple:
class StackTrace: boost::noncopyable // doesn't make sense to copy it
{
public:
StackTrace(): mStream() {}
~StackTrace()
{
if (std::uncaught_exception())
{
std::cout << mStream.str() << '\n';
}
}
std::ostream& set(char const* function, char const* file, unsigned int line)
{
return mStream << file << "#" << line << " - " << function << " - ";
}
private:
std::ostringstream mStream;
};
#define STACK_TRACE(func) \
StackTrace ReallyUnwieldyName; \
ReallyUnwieldyName.set(func, __FILE__, __LINE__)
One can use __PRETTY_FUNC__ or equivalent to avoid naming the function, but I have found in practice that it was too mangled / verbose for my own taste.
Note that you need a named object if you wish it to live till the end of the scope, which is the purpose here. We could think of tricky ways to generate a unique identifier, but I have never needed it, even when protecting narrower scope within a function, the rules of name hiding play in our favor.
If you combine this with an ExceptionManager (something where exceptions thrown register themselves), then you can get a reference to the latest exception and in case of logging you can rather decide to setup your stack within the exception itself. So that it's printed by what and ignored if the exception is discarded.
This is a matter of taste.
Note that in the presence of ExceptionManager you must remain aware that not all exceptions can be retrieved with it --> only the ones you have crafted yourself. As such you still need a measure of protection against std::out_of_range and 3rd party exceptions.
Say a very generic function fails, the stack is unwound to the top level and all I see in the logs would be [...]
What's a better way for detailed for error reporting in standard C++?
Error handling isn't local to a class or library - it is a application level concern.
Best I can answer you question is that the error reporting should be always implemented by looking first and foremost at the error handling. (And the error handling also including the handling of the error by the user.) Error handling is the decision making about what has to be done about the error.
That is one of the reasons why error reporting is an application level concern and strongly depends on the application workflow. In one application the "connection reset by peer" is a fatal error - in another it is a norm of life, error should be silently handled, connection should be reestablished and pending operation retried.
Thus the approach you mention - catch the exception, construct a new one with more detailed context information and re-throw it - is a valid one too: it is up to the top level application logic (or even user configuration) to decide whether the error is really an error or some special (re)action has to taken upon the condition.
What you have encountered is one of the weaknesses of so called out-of-line error handling (aka exceptions). And I'm not aware of any way to do it better. Exceptions create a secondary code path in the application and if error reporting is vital the design of the secondary code path has to treated just like the main code path.
Obvious alternative to the out-of-line error handling is the in-line error handling - good ol' return codes and log messages on the error conditions. That allows for a trick where application saves all low-severity log messages into internal (circular) buffer (of fixed or configurable size) and dumps them into the log only if high-severity error happens. This way more context information is available and different layers of application do not have to be actively aware of each other. That is also standard (and sometimes literally "standard" - mandated by law) way of error reporting in application fields like safety and mission critical software, were one is not allowed to miss errors.
I've never actually done this, but you could roll your own "stacktrace":
struct ErrorMessage {
const char *s;
ErrorMessage(const char *s) : msg(s) {}
~ErrorMessage() { if (s) std::cout << s << "\n"; }
void done() { s = 0; }
};
void someOperation() {
ErrorMessage msg("Doing the first bit");
// do various stuff that could throw
msg = "Doing the second bit";
// do more stuff that could throw
msg.done();
}
You can have several levels of this (although not necessarily use it at every level):
void handleFoo() {
ErrorMessage msg("Handling foo event");
someOperation();
msg.done();
}
And add more constructors and members:
void handleBar(const BarEvent &b) {
ErrorMessage msg(std::stringstream("Handling bar event ") << b.id);
someOperation();
msg.done();
}
And you needn't write the messages to std::cout. It could be to some logging object, and the object could queue them, and not actually output them to the log unless the catch site tells it to. That way, if you catch an exception that doesn't warrant logging, nothing is written.
It's not pretty, but it's prettier than try/catch/throw or checking return values. And if you forget to call done on success (for example if your function has multiple returns and you miss one), then you will at least see your mistake in the logs, unlike a resource leak.
[Edit: oh, and with a suitable macro you can store __FILE__ and __LINE__ in the ErrorMessage.]
You could add a call stack to your exception. I'm not sure how good this works for release builds but works like a charm with debug. You could do this in the constructor of your exception (to encapsulate it). See here for a starting point. This is possible as well on Linux - eventhough I dont remember how exactly.
I use RAII and exceptions and just have various unit-test-like assert statements throughout the code - if they fail, the the stack unwinds to where I can catch and handle them.
#define APP_ASSERT_MSG(Class,Assertion,szDescription) \
if ( !(Assertion) ) \
{ \
throw Class(__LINE__,__FILE__,szDescription); \
}
For most of my particular use case, all I care about is logging debug information, so my exception has the file and line number in it along with an error message (message is optional as I have an assert without it as well). You could easily add FUNCTION or an error code of some type for better handling.
I can then use it like this:
int nRet = download_file(...);
APP_ASSERT_MSG(DownloadException == ERR_OK, "Download failed");
This makes error handling and reporting much easier.
For really nasty debugging, I used GCC's function instrumentation to keep a trace list of what's going on. It works well, but slows down the app quite a bit.
What I do regularly, FWIW, is not use exceptions, but rather explicit error handling in a standard format (ie: using a macro). For example:
result = DoSomething();
CHECK_RESULT_AND_RETURN_ON_ERROR( result );
Now, obviously, this has many limitations design-wise:
Your return codes may need to be somewhat uniform
Code is cluttered with macros
You may need many macros for various check conditions
The upside, though, is as Dummy00001 said: you can effectively generate a stack trace on demand in the event of a serious error, by simply caching the results. I also use this paradigm to log all unexpected error conditions, so I can adjust the code later to handle unexpected conditions which occur "in the wild".
That's my 2c.
Here's how I handle error reporting in my libraries (this may or may not suit your situation).
First, as part of your design you want a "core" or "system" library that all this common logic will sit in. All you other libraries will then link to the core and use its error reporting APIs, so your whole system has a single compact chunk of logic for handling this mess.
Inside the core, provide a set of logging MACROS such as "LogWarning" and "LogFatal" with documented behavior and expected usage- for example, LogFatal should trigger a hard abort of the process but anything lower than "LogError" is simply advisory (does nothing). The macros can provide a "printf" interface that automatically appends the "LINE", "FILE", and "FUNC" macros as arguments to the underlying singleton object that handles your error reporting.
For the object itself I'll defer to you. However, you want public APIs that configure your "drains"- e.g. log to stderr, log to logfile, log to MS Services log, etc. You also want the underlying singleton to be thread safe, re-entrant where possible, and very robust.
With this system, you can make "exception reporting" ONE MORE DRAIN TYPE. Just add an internal API to that error manager object that packages your logged message as an exception, then throws it. Users can then enable AND DISABLE exceptions-on-error behavior in your code with ONE LINE in their apps. You can put try-catches around 3rd party or system code in you libraries that then calls a "Log..." macro in the catch clauses to enable clean re-throw behavior (with certain platforms and compiler options you can even catch things like segfaults using this).
I am writing a custom C++ exception class (so I can pass exceptions occuring in C++ to another language via a C API).
My initial plan of attack was to proceed as follows:
//C++
myClass
{
public:
myClass();
~myClass();
void foo() // throws myException
int foo(const int i, const bool b) // throws myException
} * myClassPtr;
// C API
#ifdef __cplusplus
extern "C" {
#endif
myClassPtr MyClass_New();
void MyClass_Destroy(myClassPtr p);
void MyClass_Foo(myClassPtr p);
int MyClass_FooBar(myClassPtr p, int i, bool b);
#ifdef __cplusplus
};
#endif
I need a way to be able to pass exceptions thrown in the C++ code to the C side. The information I want to pass to the C side is the following:
(a). What
(b). Where
(c). Simple Stack Trace (just the sequence of error messages in order they occured, no debugging info etc)
I want to modify my C API, so that the API functions take a pointer to a struct ExceptionInfo, which will contain any exception info (if an exception occured) before consuming the results of the invocation.
So an C++ method exposed in the C API would be implemented something like this:
// I must admit there is something freakishly 'hackish' about this sort of implementation (maintenance nightmare aside) - ther must be a better way?
// ExceptionInfo default ctor initialized to no error
void MyClass::foobarTrappedExceptionWrapper(ExceptionInfo& ei)
{
try {
// call foobar ...
foobar();
}
catch(fast_exception& fe)
{
switch (fe.errcode())
{
//package exception info into ei for C consumption
}
}
catch(std::exception &e)
{
//package exception info into ei for C consumption
}
}
Implementation of each of the C++ methods exposed in the C API needs to be enclosed in a try/catch statement (see above snippet). The performance implications for this seem quite serious (according to this article):
"It is a mistake (with high runtime
cost) to use C++ exception handling
for events that occur frequently, or
for events that are handled near the
point of detection."
At the same time, I remember reading somewhere in my C++ days, that all though exception handling is expensive, it only becmes expensive when an exception actually occurs. So, which is correct?. what to do?. Is there an alternative way that I can trap errors safely and pass the resulting error info to the C API?. Or is this a minor consideration (the article after all, is quite old, and hardware have improved a bit since then).
[Edit]
Removed question2, since I found a way to create a simple stack trace here.
Answers
Both are correct; you are misinterpreting the first source. The try{} block incurs basically no cost; it is the throw....catch (i.e. propagation) that is expensive. So, it's cheap if the exception doesn't occur, which is why they should not be used if they are occuring frequenty (because then you do throw...catch often).
Using a very long but fixed-size buffer isn't lighter weight than using an std::string. Using an std::string is a bad idea only if you are worried that you might throw an exception due to being out of memory (in which case the std::string constructor would throw because it cannot allocate space). However, why reinvent the wheel? I suggest you take a look at boost::exception, which provides a boost::exception class that allows arbitrary metadata to be attached to the exception object, and it can be as light weight (no metadata attached) or as heavy weight as you want, depending on what metadata you use.
Comment on Passing C++ Exceptions Around in C
The choice to inherit or not to inherit from std::exception has no implication on performance; the article was mentioning the fact that propagating an exception and performing stack unwinding is expensive (regardless of the exception's type). In terms of passing an exception around in the C world.... error handing in C is typically done using integer return types and error codes, although if you want to pass a C++ object around, as well, then if you can polymorphically copy the exception type, then you can simply construct a copy on the heap, and then pass around the exception object as type void*, and if you need to extract information from that object, then you can create C interfaces that are implemented in C++ and which cast the void* object back to a pointer to your exception type and extract the appropriate fields. An alternative, if all you want is the what() message, is to pass around the strdup() of the message. Bear in mind, though, that anything that requires allocation / copying is not going to work if std::bad_alloc is one of the exceptions your handling (in other words, if you can fail due to being out of memory, allocating more memory is not a good idea).
As the saying goes, "When in Rome, do as the Romans do".... my own advice would be to use return status codes and error codes when in C and to use exception handling in C++. I really would not bother trying to bring a C++ exception object into the C world. Also bear in mind, that it is more common for C++ code to call into C code than for C code to call into C++ code. If you are creating C interaces for your C++ code, then they should probably behave like C interfaces, namely returning error status codes. If your C++ code calls a C function that returns an error status code, then it is reasonable to throw an exception rather than returning an error status code where the underlying C code failed. In that case, though, you are in C++ space and don't really have to worry about the exception crossing back into C code. But, in the case where you do cross back into the C world, I would advise logging whatever information you have and using error status codes.
Also, given that std::exception::what() returns a char* object describing what happened, whereas your API returns an integer code indicating the type of exception... well, this will be really confusing to other people using your code. When I saw you use waht() in a switch statement, I was about to comment that you cannot switch on a string, and then had to make a double-take and realize it means something different than usual.