-
GILLES Sebastien authored
INstall Jupytext in the Conda environment, and add configuration file so that Markdown files are loaded as notebooks.
GILLES Sebastien authoredINstall Jupytext in the Conda environment, and add configuration file so that Markdown files are loaded as notebooks.
- Getting started in C++ - C++ in a real environment - Namespaces
- Introduction
- Defining a namespace and using its content
- Look-up rules
- Layers of namespaces
- Good practice: use at least one namespace for your projects
- The std namespace
- Good practice: free functions closely associated to a class should be put in the same namespace
- The using directive
- Good practice: never use using namespace directive in header files
- The softer using directive
- Namespace shortcut
- Unnamed namespace
- Side note: how to include C header
jupyter:
jupytext:
text_representation:
extension: .md
format_name: markdown
format_version: '1.3'
jupytext_version: 1.16.1
kernelspec:
display_name: C++17
language: C++17
name: xcpp17
Getting started in C++ - C++ in a real environment - Namespaces
Introduction
So far, we've dealt with snippets of code, and it was trivial not to make the mistake of naming two functions or classes with the same name. With a more sizable codebase, you might feel stuck at some point if you want to use the same name twice for different purposes... and with third party libraries it gets even worse: if this library already defines a function with the prototype you intended to define yourself, you're screwed.
Even worse, if you use two libraries that both provide for instance their own spin over Vector
, you are stuck as there will be ambiguity if these two classes are to be used in the same file.
Strangely enough, with C for which the issue may arise as well, no solution was provided to eschew this potential name clashing.
Fortunately, C++ introduced the namespaces for this: a namespace is an entity which groups together classes, functions and so on... So far, as we didn't define explicitly one we were actually in the global namespace.
Defining a namespace and using its content
A namespace is just defined as a block prefixed by namespace XXX
where XXX is the name of the namespace. Contrary to a class, a namespace block with the same name can be used in as many files as you wish; declaration and definition may therefore be defined in separate files as usual.
// hpp file
#include <iostream>
namespace MyProject
{
void Hello();
} // namespace MyProject
// cpp file
#include <iostream>
namespace MyProject
{
void Hello()
{
std::cout << "Hello from MyProject namespace!" << std::endl;
}
} // namespace MyProject
To use the Hello
function, you need to specify the namespace explicitly with the exact same syntax already used for classes:
Hello(); // COMPILATION ERROR
MyProject::Hello(); // OK
Look-up rules
Now what happens if two functions of the same name do exist? There are actually look-up rules: the compiler first looks in the current namespace, and if it doesn't find any, it moves one step up toward the global namespace and retries.
This is the behaviour by default; you may choose explicitly to call another one with the ::
syntax: namespace::fun
. Using ::
with no prefix means you explicitly tell to go to the global namespace.
void Hello()
{
std::cout << "Hello from the global namespace!" << std::endl;
}
Hello();
MyProject::Hello();
namespace MyProject
{
void PrintHello()
{
Hello(); // Calls Hello() from MyProject namespace
::Hello(); // Calls Hello() from global namespace
}
} // namespace MyProject
MyProject::PrintHello();
Layers of namespaces
You may in fact pile up several namespaces if you wish; the rules explained above still apply the same:
// hpp file
namespace MyProject
{
namespace Internal
{
void DoInternalStuff();
} // namespace Internal
} // namespace MyProject
// cpp file
namespace MyProject
{
namespace Internal
{
void DoInternalStuff()
{
std::cout << "Do internal stuff!" << std::endl;
}
} // namespace Internal
} // namespace MyProject
namespace MyProject
{
void FunctionWithUsesUpInternalStuff();
} // namespace MyProject
namespace MyProject
{
void FunctionWithUsesUpInternalStuff()
{
Internal::DoInternalStuff();
}
} // namespace MyProject
MyProject::FunctionWithUsesUpInternalStuff();
Since C++ 17, you may in fact define several layers of namespasces in a same line:
namespace MyProject::Internal // should work only if notebook kernel set to C++ 17 and above!
{
void OtherInternalStuff();
} // namespace MyProject::Internal
Good practice: use at least one namespace for your projects
To my mind namespaces are a big asset, and in my code I often use several layers of namespaces. This enables proper ordering of the code, and also helps you in the naming of your functions and classes: sometimes it's handy to use the same name at different places in your code.
Your mileage may vary on this, but I really advise you to use at least one that would encompass all the content of your project. This way, you prevent the drawback I mentioned in the introduction about possible name conflict with a third-party library namesake function.
The Internal
I used to illustrate layers is not innocent: it is also a common way to indicate end-users they shouldn't bother with some utility and/or internal functionalities. For instance in Boost library there is a namespace detail
: any stuff defined in boost::detail
namespace is clearly an indication you shouldn't bother except if you are implementing yourself a Boost library (I gather you would not be reading this tutorial if that is the case!)
std
namespace
The We've actually used one namespace extensively so far even if I didn't say what it was: the std::
prefix was just to indicate the namespace in which the STL constructs are defined.
There is a specific rule for that namespace: it is forbidden to try to add content in that namespace except for very specific case such as std::hash
specialization (see the cppreference page for more details). Unfortunately, compilers will not tell you something is amiss; the standard says that doing so leads to undefined behaviour.
Good practice: free functions closely associated to a class should be put in the same namespace
We saw previously that operators may be overloaded for your own types outside of the class (it is even mandatory to do so for some operators such stream ones).
The rule in this case is to define them in the same namespace as the one in which the class itself is defined. This way, you ensure the overload may be found even if the calling site is in an entirely different namespace.
namespace MyNamespace
{
class HoldAnInt
{
public:
HoldAnInt(int a);
void Print(std::ostream& out) const;
private:
int a_;
};
} // namespace MyNamespace
namespace MyNamespace
{
HoldAnInt::HoldAnInt(int a)
: a_{a}
{ }
} // namespace MyNamespace
namespace MyNamespace
{
void HoldAnInt::Print(std::ostream& out) const
{
out << a_ << std::endl;
}
} // namespace MyNamespace
namespace MyNamespace
{
std::ostream& operator<<(std::ostream& out, const HoldAnInt& obj)
{
obj.Print(out);
return out;
}
} // namespace MyNamespace
#include <iostream>
{
MyNamespace::HoldAnInt object(42);
std::cout << "The answer to life, the universe and everything is " << object << std::endl;
}
As we see, the overload is properly called even outside of MyNamespace
.
As a rule, it is a good idea to put in the same namespace (and often even in the same file) the free functions that are closely related to a class.
using
directive
The Some people find it annoying to have to put explicitly the namespace scope in the call: they would prefer to use cout
rather than std::cout
.
There is a way to do so... with some caveats you absolutely need to know.
The way is to use the using namespace
directive at the top of a file, at the same level as includes, which puts in the namespace from which it is called the content of the namespace specified in the command. For those of you familiar with Python, it is the same as from MODULE import *
.
We'll illustrate this with std
, which is a namespace like any other for stuff defined in the standard library.
#include <iostream>
cout << "Without std:: prefix!"; // Won't work before following cell is activated
using namespace std;
cout << "Without std:: prefix!"; // Works! - or at least should work: Xeus Cling seems lost here...
The danger of doing so is that if all namespaces are used this way, we negate completely the reason for which they were introduced in the first place!
using namespace
directive in header files
Good practice: never use Naïvely, it might be tempting if you're allergic to typing std::
everywhere to put in a header file once and for all using namespace std
; this way, it is written only once and you only have to include this file to ensure it works. It respects the Don't Repeat Yourself mantra... Right?
Unfortunately, the big caveat in doing so is that you impose your choice on any end-user of the library that will include one of your header file in its project... rendering the namespace ineffective: its content is in this case everywhere in the global namespace.
So you should really use this only in source files.
using
directive
The softer The using namespace
we saw imports all the symbols into the current namespace. It is possible however to just cherry-pick the command you wish with using
:
namespace Example
{
void Foo() {}
void Bar() {}
} // namespace Example
using Example::Foo;
Foo(); // OK
Bar(); // COMPILATION ERROR
The caveats identified for using namespace
remain true.
Namespace shortcut
You may also in a file use a shortcut if you wish; you thus avoid the caveats we mentioned (at a possible cost in readability if you abuse the feature). To do so, you use an assignment-like syntax with namespaces:
{
namespace Ex = Example;
Ex::Bar(); // Ok
}
Unnamed namespace
Unnamed namespace (the official name in the standard, but anonymous namespace is also commonly used) is just a namespace... without name (quite underwhelming a reveal I surmise!)
They may be used in source files to define helper functions or classes that are used only in the scope of this source file.
// AnySourceFile.cpp
namespace // anonymous
{
void InternalStuff(); // This function exists in the cpp file but its symbol is not
// exported - exactly as C static function we saw earlier.
} // namespace anonymous
Doing so prevents issues if you define helper functions with the exact same name in different source files, which may cause linking error if not in the unnamed namespace (see the first two replies of this StackOverflow thread if you want to learn more about them).
If you have a C background, you may have used static functions
in this case; in C++ the unnamed namespace is recommended instead.
Unnamed namespace should be used in compiled files; in header files their purpose would be defeated...
Side note: how to include C header
Namespaces are in fact the reason the C header are included with a name like #include <cmath>
rather than #include <math.h>
:
- With
#include <cmath>
, symbols are deemed to be instd
namespace and possibly in the global namespace as well. - With
#include <math.h>
, symbols are in global namespace and possibly instd
namespace as well.
More on this in this StackOverflow thread; the bottom line is that you should really use the first form intended for C++.