Namespaces

Programming Workshop 2 (CSCI 1061U)

Faisal Qureshi

Faculty of Science, Ontario Tech University

http://vclab.science.uoit.ca


Separate compilation

Class separation

Encapsulation

Consider class Vec definition below

class Vec {
  protected:
  float d[3];
  
  Vec(float x, float y, float z) {
    d[0] = x; 
    d[1] = y; 
    d[2] = z; 
  }
   
  float length2() const;
};

Now consider two possible implementations of the length() method.

Implementation 1

float Vec::length() const
{
  float squared_sum = 0.0;
  
  for (int i=0; i<3; ++i) {
    squared_sum += (d[i]*d[i]);
  }
  
  return squared_sum;
}

Implementation 2

float Vec::length() const
{
  return d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
}

Notice the actual implementation of method length() doesn’t change how this class may be used as seen below

Vec v1(1,2,3);
cout << "length of v1 is " << v1.length() << endl;

Guidelines

See example below. We define Vec class in Vec.h file.

// Vec.h

class Vec {
  protected:
  float d[3];
  
  Vec(float x, float y, float z);
   
  float length2() const;
};

And we implement Vec in Vec.cpp file.

// Vec.cpp

#include "Vec.h"

Vec::Vec(float x, float y, float z)
{
  d[0] = x;
  d[1] = y;
  d[2] = z;
}

float Vec::length2() const
{
  return d[0]*d[0] + d[1]*d[1] + d[2]*d[2];
}

Multiple compiles of header files

Consider class A defined in header A.h. This class is used in various places in the program.

// A.h

class A { ... };

The class implementation resides in A.cpp

// A.cpp

...

Say this class is used in the files B.cpp, C.cpp and D.cpp.

B.cpp

// B.cpp

#include "A.h"

...

C.cpp

// C.cpp

#include "A.h"

...

D.cpp

// D.cpp

#include "A.h"

...

Contents in A.h will be compiled everytime files B.cpp, C.cpp and D.cpp are compiled. We can avoid this by using preprocessor directives, telling the compiler to compile the contents of A.h only once, as follows:

// A.h

#ifndef FNAME_H
#define FNAME_H

class A { ... };

#endif

#ifndef, #define and #endif are preprocessors that can be used to exclude portions of code from compilation. Label FNAME is typically filename for consistency and readability. E.g., FNAME_H is often chosen as __A_H__.

This prevents the contents of A.h to be compiled multiple times.

Namespaces

Example 1

Given namespace NS1 and NS2

{
    using namespace NS1;
    func();
}
{
    using namespace NS2;
    func();
}

Or

    NS1::func();
    NS2::func();

Defining a namespace

namespace NS
{
    // Code goes here
}

Example 1

namespace csci1061
{
  class Vec {
  };
}

Then we will use the file as follows

using namespace csci1061;

Vec v;

or

csci1061::Vec v;

or

using csci1061::Vec;

Vec v;

Example 2

namespace NS1
{
  void greeting();
}
namespace NS1
{
  void greeting() { cout << "Hello world." << endl; }
}

Declaration vs. directive

Declarations

Makes only one (the specified) name available, as seen below. This prevents any other uses of the name.

using NS1::getting;

Directive

Makes all names in the namespace available.

using namespace NS1;

Naming namespaces

Unnamed namespaces

Global vs. unnamed namespaces

Nested namespaces

It is possible to have nested namespaces

namespace NS1 {
  namepace NS2 {
    namespace NS3 {
      void func() { ... }
    }
  }
}

We cam use func() as follows

NS1::NS2::NS3::func();

Function hiding

Recall that we want to keep class implementation hidden from the user of that class. Similarly, we may want to hide helping functions (local level utilities used within other methods within a file, but not available to methods in other files) as well.

We can hide helping functions by making these private, or we can place these functions in class implementations unnamed space.

Example

Consider the following two files: a.cpp and b.cpp. Here anonymous namespace is used to hide method product in b.cpp from methods in file a.cpp.

a.cpp

// Faisal Qureshi
// faisal.qureshi@ontariotechu.ca
// March 29, 2021
//
// File a.cpp

#include <iostream>

using namespace std;

int sum(int a, int b);
int product(int a, int b);
int product_for_others(int a, int b);

int main()
{
    cout << sum(10,20) << endl;

//    cout << product(10,20) << endl;  // This line gives an error since
                                     // function int product(int a, int b)
                                     // is within an anonymous namespace
                                     // in b.cpp
                                     // So this function is only visible to
                                     // other methods in b.cpp
                                     // This behavior is similar to 
                                     // using a method with a static keyword
                                     // before 

    cout << product_for_others(10,20) << endl;  // This works.  Note that
                                              // product_for_others actually
                                              // uses the product method defined
                                              // in b.cpp

    return 0;
}

b.cpp

// Faisal Qureshi
// faisal.qureshi@ontariotechu.ca
// March 29, 2021
//
// File b.cpp

// This function is available outside of this file
int sum(int a, int b) {
    return a+b;
}

namespace 
{
    // This function is only available to other functions 
    // in this file
    int product (int a, int b) {
        return a*b;
    }
}

// This function is available outside of this file
int product_for_others(int a, int b){
    return a*b;
}

References