Show Lecture.NamespacePollution as a slide show.
CS253 Namespace Pollution
Guiding principles
- A header file should only define the symbols that it’s supposed
to define. No more.
- For example, if
Zippy.h
is documented to define the Zippy
class,
then that’s the only symbol that it should drop into the global namespace.
- not another
class ZippyHelper
- not a bunch of constants
- not some random function declarations
- If the
class Zippy
needs constants, or a helper class, or functions,
then put them inside the class Zippy
.
- If you have a whole giant subsystem, then consider defining your own
namespace Zippy
, and putting your classes & functions in that namespace.
An example
Consider the files in ~cs253/pub/Example/Pollution:
main.cc
#include "Graphics.h"
#include <string>
std::string fill = "gray";
std::string color = "red";
int main() {
Graphics g(fill, color);
}
Fine—it defines a couple of strings, and initializes a Graphics
object.
Graphics.h
#ifndef GRAPHICS_H_INCLUDED
#define GRAPHICS_H_INCLUDED
#include <string>
// 🦡 all over
using namespace std;
class Graphics {
public:
Graphics(const string &f, const string &p) : fill_color(f), pen(p) { }
private:
string fill_color, pen;
};
// Internal function to translate color name to int
int color(const string &name);
#endif /* GRAPHICS_H_INCLUDED */
It declares the class Graphics
, and an auxillary function.
Try it out
% g++ -Wall ~cs253/Example/Pollution/main.cc .
/s/bach/a/class/cs253/Example/Pollution/main.cc:5:13: error: ‘std::__cxx11::string color’ redeclared as different kind of symbol
std::string color = "red";
^~~~~
In file included from /s/bach/a/class/cs253/Example/Pollution/main.cc:1:
/s/bach/a/class/cs253/Example/Pollution/Graphics.h:16:5: note: previous declaration ‘int color(const string&)’
int color(const string &name);
^~~~~
/s/bach/a/class/cs253/Example/Pollution/main.cc: In function ‘int main()’:
/s/bach/a/class/cs253/Example/Pollution/main.cc:8:16: error: reference to ‘fill’ is ambiguous
Graphics g(fill, color);
^~~~
In file included from /usr/include/c++/8/bits/char_traits.h:39,
from /usr/include/c++/8/string:40,
from /s/bach/a/class/cs253/Example/Pollution/Graphics.h:4,
from /s/bach/a/class/cs253/Example/Pollution/main.cc:1:
/usr/include/c++/8/bits/stl_algobase.h:724:5: note: candidates are: ‘template<class _ForwardIterator, class _Tp> void std::fill(_ForwardIterator, _ForwardIterator, const _Tp&)’
fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
^~~~
/s/bach/a/class/cs253/Example/Pollution/main.cc:4:13: note: ‘std::__cxx11::string fill’
std::string fill = "gray";
^~~~
That didn’t go well
- The problems:
main.cc
tried to define a variable named color
, but
Graphics.h
already declared a function named color
.
main.cc
tried to use a variable named fill
, but that failed,
because there’s already a fill
somewhere in the C++ standard library.
- The solutions:
Graphics.h
has no business declaring the function color()
,
since its only job is to declare class Graphics
.
Graphics.h
has no business doing using namespace std;
,
which introduces potential conflicts with
every symbol in the standard library.
- Instead, references to string in
Graphics.h
should
be std::string
.
Alas, not having using namespace std;
in a header file
makes it more verbose:
class Graphics {
public:
Graphics(const std::string &fill, const std::string &color);
};
so do this, instead:
class Graphics {
using string = std::string;
public:
Graphics(const string &fill, const string &color);
};
The alias is inside the class, so it doesn’t pollute the global namespace.
The alias is in the private section of the class, so it’s
not visible to users.
Don’t misunderstand
- The command "don’t put
using namespace std;
in a header file” should
not be misunderstood as to forbid it in other places.
- It’s fine in a
*.cc
file. Nobody else is going to #include
your *.cc
file, so do what you like there.
- A
*.h
file, on the other hand, is designed to be #included
into other files. Therefore, *.h
files have to be well-behaved.