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:
inline static const std::unordered_set<char> CHAR_SET {'/', '\\', '|', '-'};
friend class Invalid_C_Exception;class
};
} // 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( )
{
// if implementation details are irrelevant, remove them
std::stringstream ss;
ss << "Invalid_C_Exception: { ";
// compiler gives an error here because of uses 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 separate header and source files but under the same namespace? I was undecided so I put the declaration and implementation in the source file Foo.cpp cause the exception class is designed to work for the class Foo so they're related to each other.
CodePudding user response:
There is a big difference between
friend class Invalid_C_Exception;
and
friend Invalid_C_Exception;
The latter expects that Invalid_C_Exception
already exists somewhere accessible and makes that symbol a friend of Foo
. The former declares a new symbol Invalid_C_Exception
in the enclosing scope and makes that the new friend.
Meaning you have made Bar::Invalid_C_Exception
a friend of Foo
, not the global ::Invalid_C_Exception
you have defined later which is still not a friend. The fact that Bar::Invalid_C_Exception
will never exist is irrelevant.
Regarding your first question, Invalid_C_Exc
is a global variable, by default it has external linkage. If you use it only in this translation unit(.cpp file), make it static
or put it into an anonymous namespace.
I would recommend not being lazy and write its definition on a separate line, it's much more clearer that way. Other than that, you can use it just fine, it is initialized before main
begins and lives until the end of the program.
Regarding your third question. Wherever you want, I would generally advise for one .hpp
per one exception type or tightly-grouped pack of them. Putting it into .cpp
makes catching it hard.
CodePudding user response:
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.