How should I write an efficient exception class to show the error that can be prevented by fixing the source code mistakes before run-time?
This is the reason I chose std::invalid_argument
.
My exception class(not working, obviously):
class Foo_Exception : public std::invalid_argument
{
private:
std::string Exception_Msg;
public:
explicit Foo_Exception( const std::string& what_arg );
virtual const char* what( ) const throw( );
};
explicit Foo_Exception::Foo_Exception( const std::string& what_arg ) // how should I write this function???
{
Exception_Msg.reserve( 130000 );
Exception_Msg = "A string literal to be appended, ";
Exception_Msg += std::to_string( /* a constexpr int */ );
Exception_Msg += /* a char from a const unordered_set<char> */;
}
const char* Foo_Exception::what( ) const throw( )
{
return Exception_Msg.c_str( );
}
An if
block that throws Foo_Exception
:
void setCharacter( const char& c )
{
if ( /* an invalid character (c) is passed to setCharacter */ )
{
try
{
const Foo_Exception foo_exc;
throw foo_exc;
}
catch( const Foo_Exception& e )
{
std::cerr << e.what( );
return;
}
}
/*
rest of the code
*/
}
int main( )
{
setCharacter( '-' ); // dash is not acceptable!
return 0;
}
As you can see, I need to concatenate Exception_Msg
with a few substrings in order to form the completed message. These substrings are not only string literals but also constexpr int
s and char
s from a static std::unordered_set<char>
. That's why I used std::string because it has the string::operator+=
which is easy to work with. And needless to say, my goal is to reduce heap allocations down to only 1.
Another very important question is that where should I place the handler( the try-catch )? Inside the main()
wrapping the setCharacter()
or keep it inside setCharacter
?
Please make a good and standard custom exception class similar to the above. Or write your own. Thanks in advance.
Custom exceptions provide relevant information about an error to the exception handling mechanism. They can be generated by creating a new class containing the attributes needed and throwing an instance of such a class, or by inheriting from std::exception and overriding the what() function.
std::invalid_argument Defines a type of object to be thrown as exception. It reports errors that arise because an argument value has not been accepted. This exception is thrown by std::bitset::bitset, and the std::stoi and std::stof families of functions. Inheritance diagram.
If you want to make use of the string constructor, you should inherit from std::runtime_error or std::logic_error which implements a string constructor and implements the std::exception::what method.
start a debugger and place a breakpoint in the exceptions constructor, and see from where it is being called. the caling function is probably something like __throw(). afterwards, start the debugger again with the program you want to investigate as debuggee.
Rather than new char[]
every time, why not have a std::string
data member?
class Foo_Exception : public std::exception /* ? */
{
std::string msg;
public:
Foo_Exception() {
msg.reserve( 130000 );
msg = "A string literal to be appended, ";
msg += "another string, ";
msg += "yet another one";
}
const char * what() const override { return msg.data(); }
/* other members ? */
}
Or you could derive from one of the std
exception types that takes a string as a parameter on construction
class Foo_Exception : public std::runtime_error /* or logic_error */
{
public:
Foo_Exception() : runtime_error("A string literal to be appended, " "another string, " "yet another one") {}
/* other members ? */
}
However if you just have string literals, you can split them on multiple source lines, and have zero allocations.
const char * what() const override {
return "A string literal to be appended, "
"another string, "
"yet another one";
}
I solved this problem based on the feedback that I received from others through comments and answers. So I decided to leave my own answer/solution here for future readers.
Below can be seen what I came up with after much thought and research. This solution is fairly simple and readable.
Here is the exception class interface:
Foo_Exception.h
#include <exception>
class Foo_Exception : public std::invalid_argument
{
public:
explicit Foo_Exception( const std::string& what_arg );
};
And here is its implementation:
Foo_Exception.cpp
#include "Foo_Exception.h"
Foo_Exception::Foo_Exception( const std::string& what_arg )
: std::invalid_argument( what_arg )
{
}
A function that throws Foo_Exception
:
Bar.cpp
#include "Bar.h"
#include "Foo_Exception.h"
void setCharacter( const char& c )
{
if ( /* an invalid character (c) is passed to setCharacter */ )
{
std::string exceptionMsg;
exceptionMsg.reserve( 130000 );
exceptionMsg = "A string literal to be appended, ";
exceptionMsg += std::to_string( /* a constexpr int */ );
exceptionMsg += /* a char from a const unordered_set<char> */;
throw Foo_Exception( exceptionMsg );
}
/*
rest of the code
*/
}
How to handle the exception:
main.cpp
#include <iostream>
#include "Bar.h"
#include "Foo_Exception.h"
int main( )
{
try
{
setCharacter( '-' ); // The dash character is not valid! Throws Foo_Exception.
}
catch ( const Foo_Exception& e )
{
std::cerr << e.what( ) << '\n';
}
return 0;
}
Summary of the Changes:
Notice how there's no need for a what()
function since the compiler generates it implicitly because Foo_Exception
inherits from std::invalid_argument
.
Also, the process of creating the exception message was moved from the Foo_Exception
's ctor to the body of the function setCharacter
which actually throws the aforementioned exception. This way, the Foo_Exception
's ctor is not responsible for creating the message. Instead, it is created in the body of the function that throws and is then passed to the ctor of Foo_Exception
to initialize the new exception object.
The data member std::string Exception_Msg
was also removed from Foo_Exception
as it wasn't needed anymore.
Finally, the try-catch block was moved to main()
so that it now wraps around setCharacter()
and catches a Foo_Exception
object that it might throw.
Final word: Any suggestions to further improve my answer is highly appreciated. Thanks a ton for all the feedback.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With