std::exception details ?

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
binbinhfr
Posts: 78
Joined: May 9th, 2019, 10:57 pm

std::exception details ?

Post by binbinhfr » April 14th, 2020, 8:29 am

Hi guys,

in my code, I wrote my own exception handler and with a macro, I can issue a message containing the file and line of code where the exception was thrown.

Code: Select all

#define THROW_DBT_EXCEPTION( s ) throw DBT_Exception( __FILE__,__LINE__,__FUNCSIG__, s )
BUT concerning exception thrown by the STD lib, I wonder if I can do something in the same "taste".

For example, recently, I had a std::exception issued by the STD lib, just writting "string too long".

Argh.... did not even know where the error was in the code...

At the end of my main, I have this :

Code: Select all

		catch (const DBT_Exception& e)
		{
			MessageBoxA(nullptr, e.what(), "DBT Error", MB_ICONERROR);
		}
		catch (const std::exception& e)
		{
			MessageBoxA(nullptr, e.what(), "STD Error", MB_ICONERROR);
		}
		catch (...)
		{
			MessageBoxA(nullptr, "Unhandled exception...", "Error", MB_ICONERROR);
		}
In order to break at the bad line, I had to comment out the two last exception catch. Then VC++ breaks and I can read the function stack to see where I am.

So, how am I supposed to "smartly" handle STD exception, so that in the final "release" given to the user (non debug), I can still log this kind of error and still record a clue on where it was thrown...

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: std::exception details ?

Post by albinopapa » April 14th, 2020, 3:28 pm

There are of course standard exceptions that you can pass messages to like std::runtime_exception or std::invalid_argument for example, but to create wrapper macros for these and get the extra information I'm guessing you'd need to be the one throwing them.

My best advice is to catch only the exceptions you throw with the macro wrappers while developing. Anything that happens after that will be truly "exceptional". The only other advice I can give is to maybe make your own stack trace logger. Something that logs the function name to a file when entered and removes that line when the function is exited. This way you only end up logging the the functions leading up to the exception. When you examine the file, the last line in the log would be where the exception was thrown.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Slidy
Posts: 80
Joined: September 9th, 2017, 1:19 pm

Re: std::exception details ?

Post by Slidy » April 14th, 2020, 4:54 pm

My usual approach is to just step through in debugger for exceptions I don't throw myself to figure out where they're thrown.

As for exceptions that I throw myself, I usually use a macro that triggers a breakpoint in debug builds.
Example:

Code: Select all

#ifdef _DEBUG
#define ERROR(msg) __debugbreak()
#else
#define ERROR(msg) throw MyException(msg, __FILE__, __LINE__)
#endif

binbinhfr
Posts: 78
Joined: May 9th, 2019, 10:57 pm

Re: std::exception details ?

Post by binbinhfr » April 14th, 2020, 7:13 pm

Well the function stack is an idea, but the overhead will be too heavy.
The break/throw switch is a nice idea that I implement right now.

But I really wish there was something more clever to trace STD errors once the code is compiled and distributed. Imagine there is such an exception "string too long" while a user plays the game. I cannot even automatically log a description of the context where it happened, to help me find the issue afterwards... Very frustrating, no ?

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: std::exception details ?

Post by albinopapa » April 15th, 2020, 5:07 am

Code: Select all

#pragma once

#include <string>
#include <vector>

class AutoLog;
class TraceLogEntry
{
public:
	TraceLogEntry( std::string func_name_ );
	~TraceLogEntry()noexcept;
private:
	std::string func_name;
};

class AutoLog
{
public:

private:
	void EnterFunction( std::string const& func_name )
	{
		entries.push_back( "Entering: " + func_name );
	}
	void ExitFunction( std::string const& func_name )
	{
		entries.push_back( "Exiting: " + func_name );
	}
private:
	friend std::ostream& operator<<( std::ostream& stream, AutoLog const& logger );
	friend class TraceLogEntry;
	std::vector<std::string> entries;
};

inline std::ostream& operator<<( std::ostream& stream, AutoLog const& logger )
{
	for( auto& entry : logger.entries )
	{
		stream << entry << '\n';
	}

	return stream;
}

inline AutoLog logger;

inline TraceLogEntry::TraceLogEntry(std::string func_name_)
	:
	func_name( std::move( func_name_ ) )
{
	logger.EnterFunction( func_name );
}

inline TraceLogEntry::~TraceLogEntry() noexcept
{
	logger.ExitFunction( func_name );
}

#define Trace auto entry = TraceLogEntry(__FUNCSIG__)
Well, you could always give this a shot, but it's not thread safe, so you'd have to modify it to use a mutex or have each thread have their own copy of AutoLog.

There is something on Windows called StackWalk if you can figure out how it all works. The documentation also says that it's not thread safe, so again you'd have to synchronize your threads before calling this function.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply