Search code examples
c++classexceptionheader-files

Where to put the custom exception classes?


Suppose that I have a class which you can see below (Please read the comments too):

Foo.h

#pragma once

namespace Bar
{

class Foo
{
public:
    inline Foo( );

private:
    char c;
    inline static const std::unordered_set<char> CHAR_SET {'/', '\\', '|', '-'};

    friend class Invalid_C_Exception;
};

} // end of namespace Bar

And I have:

Foo.cpp

#include "Foo.h"

using namespace Bar;

inline Foo::Foo( )
{
}

class Invalid_C_Exception : public std::exception
{
public:
    virtual const char* what( ) const throw( )
    {
        std::stringstream ss;

        ss << "Invalid_C_Exception: { ";

        // compiler gives an error here because of usages of CHAR_SET
        for ( auto it = Foo::CHAR_SET.cbegin( ); it != Foo::CHAR_SET.cend( ); ++it )

        {
            ss << "'" << *it << "', ";
        }

        return somehow_save_the_C_string(ss.str());
    }

} Invalid_C_Exc;

1st question:

Is the existence of the global Invalid_C_Exc variable ok?

2nd question:

Why does the compiler blame me for CHAR_SET being private while trying to compile Foo.cpp?

The custom exception class, despite being a friend of class Foo, is not able to access CHAR_SET?

3rd question:

Where should I put these custom exception classes? In a separate header and source file, but under the same namespace?

I was undecided, so I put the declaration and implementation in the source file Foo.cpp because the exception class is designed to work for the class Foo, so they're related to each other.


Solution

  • 1st question: Is the existence of this variable ok?

    Globals are not generally a great idea, and since exception objects are copied to dedicated exception storage when thrown, I don't see any use for this particular global.

    second question is that why does the compiler blame me for CHAR_SET being private

    You have defined a class Bar::Foo and declared its friend class Bar::Invalid_C_Exception ... but you haven't touched those at all in the implementation file. You've just declared a new and unrelated Invalid_C_Exception in the global namespace.

    Remove the using namespace Bar and wrap everything below the includes in namespace Bar { ... } same as in the header. Or explicitly write Bar::Foo and Bar::Invalid_C_Exception everywhere if you prefer.

    The using namespace directive makes names from the namespace visible in the enclosing scope. It doesn't suck everything added to the enclosing scope afterwards back into the namespace. How would that work if you have more than one using namespace directive? And why would using namespace std; ever be allowed?

    3rd question: Where should I put these custom exception classes?

    Well, you can't put the definition in your implementation file if you want anyone to be able to use it. Just put the class definition in the header, and leave only the definition of Bar::Invalid_C_Exception::what in the implementation file.

    Whether they go in the same header & implementation file as the class they're friends with is up to you, it doesn't really matter.

    4th question: how many questions should I ask per question?

    One. It's called a question, not a questionnaire.