Home > front end >  How to write a custom exception class derived from std::invalid_argument?
How to write a custom exception class derived from std::invalid_argument?

Time:11-14

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 ints and chars 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.

CodePudding user response:

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";
}

CodePudding user response:

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:

  1. Notice how there's no need for a what() function since the compiler generates it implicitly because Foo_Exception inherits from std::invalid_argument.

  2. 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.

  3. The data member std::string Exception_Msg was also removed from Foo_Exception as it wasn't needed anymore.

  4. 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.

  • Related