Commit adf1a1c7 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

Fix a plain mistake: overloads of operator<< should not be in namespace std!...

Fix a plain mistake: overloads of operator<< should not be in namespace std! Rewrite the streams notebook appropriately; unfortunately doing so render the example code not runnable on Xeus-cling. I have added explicit instantiation of operator>>. In the namespace notebook, I have added a part about namespace std and another one to say where overloaded operators should be added.
parent 59b58677
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [Operators](/notebooks/3-Operators/0-main.ipynb) - [Stream operators](/notebooks/3-Operators/3-Stream.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Operators-<<" data-toc-modified-id="Operators-<<-1">Operators &lt;&lt;</a></span></li></ul></div>
<div class="toc"><ul class="toc-item"><li><span><a href="#Operators-<<" data-toc-modified-id="Operators-<<-1">Operators &lt;&lt;</a></span></li><li><span><a href="#Operator>>" data-toc-modified-id="Operator>>-2">Operator&gt;&gt;</a></span></li></ul></div>
%% Cell type:markdown id: tags:
## Operators <<
So far, we have very often defined methods and/or functions named `Print()` to provide a visual printing of the content of a class.
However, it's even better if we could use the `<<` syntax directly, and the way to do so is to provide an overload of `operator<<`.
There is however an additional subtlety: if we want it to work at all scopes we need to put that in the `namespace std`; it is the only occurrence in which we are entitled to add new content in that namespace (for more on namespaces wait until [further notebook](/notebooks/6-InRealEnvironment/5-Namespace.ipynb)...)
The recommanded way to implement it is to use under the hood a `Print(std::ostream&)` (or whatever you want to call it) under the hood:
%% Cell type:code id: tags:
``` C++17
// Doesn't run on Xeus-cling due to definition of an operator outside of a class
// A ticket has been opened for this: https://github.com/QuantStack/xeus-cling/issues/214
#include <iostream>
class Rational
{
public :
explicit Rational(int numerator, int denominator);
// Might even be private; in which case friendship needs to be declared
void Print(std::ostream& out) const;
private :
int numerator_ = 0;
int denominator_ = 0;
};
```
%% Cell type:code id: tags:
``` C++17
Rational::Rational(int numerator, int denominator)
: numerator_(numerator),
denominator_(denominator)
{ }
```
%% Cell type:code id: tags:
``` C++17
void Rational::Print(std::ostream& out) const
{
out << numerator_ << " / " << denominator_;
}
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
std::ostream& operator<<(std::ostream& out, const Rational& r)
{
r.Print(out); // see how the buk of the work is done by the 'Print()' method!
return out;
}
namespace std
int main()
{
Rational r1(22, 7);
Rational r2(77, 17);
std::cout << "Rational = " << r1 << std::endl;
std::ostream& operator<<(std::ostream& out, const Rational& r)
{
r.Print(out);
return out;
}
std::cout << "Call may also be chained due to the signature of the function: " << r1
<< " and " << r2 << std::endl;
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
Please notice the slightly weird expected syntax for the `operator<<`: the `std::ostream` appears actually twice; the reason for that is to enable chained calls:
%% Cell type:code id: tags:
``` C++17
// Doesn't run on Xeus-cling!
int main()
{
Rational r(22, 7);
std::cout << "Rational = " << r << std::endl;
Rational r1(22, 7);
Rational r2(84, 9);
std::cout << "Rationals = " << r1 << " and " << r2 << std::endl;
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
Please notice the slightly weird expected syntax for the `operator<<`: the `std::ostream` appears actually twice; the reason for that is to enable chained calls:
## Operator>>
Operator>> might be overloaded in a similar way; it might be used for instance to create an object from data readin a file. As usual with `operator>>`, you should be cautious and handle well the case in which the flux becomes invalid:
%% Cell type:code id: tags:
``` C++17
// Doesn't run on Xeus-cling due to definition of an operator outside of a class
// A ticket has been opened for this: https://github.com/QuantStack/xeus-cling/issues/214
#include <iostream>
class Rational
{
public :
explicit Rational(int numerator, int denominator);
void Print(std::ostream& out) const;
friend std::istream& operator>>(std::istream& in, Rational& r);
private:
//! Set method to modify numerator and denominator. I made the arbitrary choice to make it private
//! to illustrate friendship.
void Set(int numerator, int denominator);
private :
int numerator_ = 0;
int denominator_ = 0;
};
Rational::Rational(int numerator, int denominator)
: numerator_(numerator),
denominator_(denominator)
{ }
void Rational::Print(std::ostream& out) const
{
out << numerator_ << " / " << denominator_;
}
std::ostream& operator<<(std::ostream& out, const Rational& r)
{
r.Print(out);
return out;
}
void Rational::Set(int numerator, int denominator)
{
numerator_ = numerator;
denominator_ = denominator;
}
std::istream& operator>>(std::istream& in, Rational& r)
{
int numerator {}, denominator {};
in >> numerator;
in >> denominator;
if (!in)
{
// If istream is bad; do not modify 'r'
return in;
}
r.Set(numerator, denominator); // ok due to friendship!
return in;
}
int main()
{
Rational r1(22, 7);
Rational r2(84, 9);
std::cout << "Rationals = " << r1 << " and " << r2 << std::endl;
Rational r2(77, 17);
std::cout << "Rational = " << r1 << std::endl;
std::cout << "Call may also be chained due to the signature of the function: " << r1 << " and " << r2 << std::endl;
std::cout << "Enter a rational by separating calls by a space" << std::endl;
std::cin >> r1;
if (std::cin)
std::cout << "Value read is " << r1 << std::endl;
else
std::cout << "Invalid input!" << std::endl;
return EXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
I won't demonstrate it here (partly for time, partly due to Xeus-Cling constraint) but the operator>> might be overloaded in a similar way; it might be used for instance to create an object from data read in a file.
This code doesn't run neither in Xeus-cling (due to the operator bug) nor in [Coliru](https://coliru.stacked-crooked.com/) or [Wandbox](https://wandbox.org/) (due to limited `std::cin` support) but you may check it on a local installation.
Even then, it is not perfect: it handles correctly the case gibberish is given through std::cin, but if you put more than two blocks separated by spaces the first two ones are used and the others aren't dealt with... As I said, proper and complete handling of input stream is though!
%% Cell type:markdown id: tags:
© _CNRS 2016_ - _Inria 2018-2019_
_This notebook is an adaptation of a lecture prepared by David Chamont (CNRS) under the terms of the licence [Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/)_
_The present version has been written by Sébastien Gilles and Vincent Rouvreau (Inria)_
......
%% Cell type:markdown id: tags:
# [Getting started in C++](/) - [C++ in a real environment](/notebooks/6-InRealEnvironment/0-main.ipynb) - [Namespaces](/notebooks/6-InRealEnvironment/5-Namespace.ipynb)
%% Cell type:markdown id: tags:
<h1>Table of contents<span class="tocSkip"></span></h1>
<div class="toc"><ul class="toc-item"><li><span><a href="#Introduction" data-toc-modified-id="Introduction-1">Introduction</a></span></li><li><span><a href="#Defining-a-namespace-and-using-its-content" data-toc-modified-id="Defining-a-namespace-and-using-its-content-2">Defining a namespace and using its content</a></span></li><li><span><a href="#Look-up-rules" data-toc-modified-id="Look-up-rules-3">Look-up rules</a></span></li><li><span><a href="#Layers-of-namespaces" data-toc-modified-id="Layers-of-namespaces-4">Layers of namespaces</a></span></li><li><span><a href="#Good-practice:-use-at-least-one-namespace-for-your-projects" data-toc-modified-id="Good-practice:-use-at-least-one-namespace-for-your-projects-5">Good practice: use at least one namespace for your projects</a></span></li><li><span><a href="#The-using-directive" data-toc-modified-id="The-using-directive-6">The <code>using</code> directive</a></span><ul class="toc-item"><li><span><a href="#Good-practice:-never-use-using-namespace-directive-in-header-files" data-toc-modified-id="Good-practice:-never-use-using-namespace-directive-in-header-files-6.1">Good practice: never use <code>using namespace</code> directive in header files</a></span></li><li><span><a href="#The-softer-using-directive" data-toc-modified-id="The-softer-using-directive-6.2">The softer <code>using</code> directive</a></span></li><li><span><a href="#Namespace-shortcut" data-toc-modified-id="Namespace-shortcut-6.3">Namespace shortcut</a></span></li></ul></li><li><span><a href="#Unnamed-namespace" data-toc-modified-id="Unnamed-namespace-7">Unnamed namespace</a></span></li><li><span><a href="#Side-note:-how-to-include-C-header" data-toc-modified-id="Side-note:-how-to-include-C-header-8">Side note: how to include C header</a></span></li></ul></div>
<div class="toc"><ul class="toc-item"><li><span><a href="#Introduction" data-toc-modified-id="Introduction-1">Introduction</a></span></li><li><span><a href="#Defining-a-namespace-and-using-its-content" data-toc-modified-id="Defining-a-namespace-and-using-its-content-2">Defining a namespace and using its content</a></span></li><li><span><a href="#Look-up-rules" data-toc-modified-id="Look-up-rules-3">Look-up rules</a></span></li><li><span><a href="#Layers-of-namespaces" data-toc-modified-id="Layers-of-namespaces-4">Layers of namespaces</a></span></li><li><span><a href="#Good-practice:-use-at-least-one-namespace-for-your-projects" data-toc-modified-id="Good-practice:-use-at-least-one-namespace-for-your-projects-5">Good practice: use at least one namespace for your projects</a></span></li><li><span><a href="#The-std-namespace" data-toc-modified-id="The-std-namespace-6">The <code>std</code> namespace</a></span></li><li><span><a href="#Good-practice:-free-functions-closely-associated-to-a-class-should-be-put-in-the-same-namespace" data-toc-modified-id="Good-practice:-free-functions-closely-associated-to-a-class-should-be-put-in-the-same-namespace-7">Good practice: free functions closely associated to a class should be put in the same namespace</a></span></li><li><span><a href="#The-using-directive" data-toc-modified-id="The-using-directive-8">The <code>using</code> directive</a></span><ul class="toc-item"><li><span><a href="#Good-practice:-never-use-using-namespace-directive-in-header-files" data-toc-modified-id="Good-practice:-never-use-using-namespace-directive-in-header-files-8.1">Good practice: never use <code>using namespace</code> directive in header files</a></span></li><li><span><a href="#The-softer-using-directive" data-toc-modified-id="The-softer-using-directive-8.2">The softer <code>using</code> directive</a></span></li><li><span><a href="#Namespace-shortcut" data-toc-modified-id="Namespace-shortcut-8.3">Namespace shortcut</a></span></li></ul></li><li><span><a href="#Unnamed-namespace" data-toc-modified-id="Unnamed-namespace-9">Unnamed namespace</a></span></li><li><span><a href="#Side-note:-how-to-include-C-header" data-toc-modified-id="Side-note:-how-to-include-C-header-10">Side note: how to include C header</a></span></li></ul></div>
%% Cell type:markdown id: tags:
## 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 code, 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 preffixed by `namespace XXX` where XXX is the name of the namespace. Contrary to a class, a same namespace block may be used in as many file as you wish; declaration and definition may therefore be defined in separate files as usual.
%% Cell type:code id: tags:
``` C++17
// hpp file
#include <iostream>
namespace MyProject
{
void Hello();
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
// cpp file
#include <iostream>
namespace MyProject
{
void Hello()
{
std::cout << "Hello from MyProject namespace!" << std::endl;
}
} // namespace MyProject
```
%% Cell type:markdown id: tags:
To use the `Hello` function, you need to specify the namespace explicitly with the exact same syntax already used for classes:
%% Cell type:code id: tags:
``` C++17
Hello(); // COMPILATION ERROR
```
%% Cell type:code id: tags:
``` C++17
MyProject::Hello(); // OK
```
%% Cell type:markdown id: tags:
## Look-up rules
%% Cell type:markdown id: tags:
Now what's happen if two functions of the same name do exist? There are actually look-up rules: the compiler first look in the same namespace, and if it doesn't find any try one step toward the global namespace.
This is the behaviour by default; you may choose explicitly to call another one with the `::` syntax (of the function begins by `::` this means you explicitly tell to go to the global namespace).
%% Cell type:code id: tags:
``` C++17
void Hello()
{
std::cout << "Hello from the global namespace!" << std::endl;
}
```
%% Cell type:code id: tags:
``` C++17
Hello();
```
%% Cell type:code id: tags:
``` C++17
MyProject::Hello();
```
%% Cell type:code id: tags:
``` C++17
namespace MyProject
{
void PrintHello()
{
Hello(); // Calls Hello() from MyProject namespace
::Hello(); // Calls Hello() from global namespace
}
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
MyProject::PrintHello();
```
%% Cell type:markdown id: tags:
## Layers of namespaces
You may in fact pile up several namespaces if you wish; the rules explained above do apply the same:
%% Cell type:code id: tags:
``` C++17
namespace MyProject
{
namespace Internal
{
void DoInternalStuff();
} // namespace Internal
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
namespace MyProject
{
namespace Internal
{
void DoInternalStuff()
{
std::cout << "Do internal stuff!" << std::endl;
}
} // namespace Internal
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
namespace MyProject
{
void FunctionWithUsesUpInternalStuff();
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
namespace MyProject
{
void FunctionWithUsesUpInternalStuff()
{
Internal::DoInternalStuff();
}
} // namespace MyProject
```
%% Cell type:code id: tags:
``` C++17
MyProject::FunctionWithUsesUpInternalStuff();
```
%% Cell type:markdown id: tags:
Since C++ 17, you may in fact define several layers of namespasces in a same line:
%% Cell type:code id: tags:
``` C++17
namespace MyProject::Internal // should work only if notebook kernel set to C++ 17 and above!
{
void OtherInternalStuff();
} // namespace MyProject::Internal
```
%% Cell type:markdown id: tags:
## 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 up 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 they have 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!)
%% Cell type:markdown id: tags:
## The `std` namespace
We've actually used one namespace extensively so far even if I didn't tell what it was: the `std::` preffix 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](https://en.cppreference.com/w/cpp/language/extending_std) for more details). Unfortunately, compilers will not tell you something is amiss; the standard says that doing so leads to undefined behaviour.
%% Cell type:markdown id: tags:
## Good practice: free functions closely associated to a class should be put in the same namespace
We saw [previously](/notebooks/3-Operators/1-Intro.ipynb) 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](/notebooks/3-Operators/3-Stream.ipynb)).
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.
%% Cell type:code id: tags:
``` C++17
namespace MyNamespace
{
class HoldAnInt
{
public:
HoldAnInt(int a);
void Print(std::ostream& out) const;
private:
int a_;
};
} // namespace MyNamespace
```
%% Cell type:code id: tags:
``` C++17
namespace MyNamespace
{
HoldAnInt::HoldAnInt(int a)
: a_{a}
{ }
} // namespace MyNamespace
```
%% Cell type:code id: tags:
``` C++17
namespace MyNamespace
{
void HoldAnInt::Print(std::ostream& out) const
{
out << a_ << std::endl;
}
} // namespace MyNamespace
```
%% Cell type:code id: tags:
``` C++17
namespace MyNamespace
{
std::ostream& operator<<(std::ostream& out, const HoldAnInt& obj)
{
obj.Print(out);
return out;
}
} // namespace MyNamespace
```
%% Cell type:code id: tags:
``` C++17
#include <iostream>
{
MyNamespace::HoldAnInt object(42);
std::cout << "The answer to life, the universe and everything is " << object << std::endl;
}
```
%% Cell type:markdown id: tags:
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.
%% Cell type:markdown id: tags:
## The `using` directive
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.
%% Cell type:code id: tags:
``` C++17
#include <iostream>
```
%% Cell type:code id: tags:
``` C++17
cout << "Without std:: preffix!"; // Won't work before following cell is activated
```
%% Cell type:code id: tags:
``` C++17
using namespace std;
```
%% Cell type:code id: tags:
``` C++17
cout << "Without std:: preffix!"; // Works!
```
%% Cell type:markdown id: tags:
The danger of doing so is that if all namespaces are used this way, we hinder completely the reason for which they were introduced in the first place!
### Good practice: never use `using namespace` directive in header files
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.
%% Cell type:markdown id: tags:
### The softer `using` directive
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`:
%% Cell type:code id: tags:
``` C++17
namespace Example
{
void Foo() {}
void Bar() {}
} // namespace Example
```
%% Cell type:code id: tags:
``` C++17
using Example::Foo;
```
%% Cell type:code id: tags:
``` C++17
Foo(); // OK
```