Header files

On Friday we had a short debate in the room with the guys about the role of header and cpp files. I didn’t support to separate unit tests to header and implementation part, because I guess in this case it’s not necessary. Why?

Data types (i.e. classes) have abstract and concrete aspects. We store the abstract aspects in header files (using c++ this is the .h file) and the concrete aspects in implementation files (cpp file). In the header we define the data type’s signature, export interface (public part of a class), parameters, methods, properties. Using C this kind of approach is shown better because header files can be separate from the implementation well, while in C++ sometimes you must store implementation specific properties in the header (usually in the private space).

The more information we define in the header file in connection with the implementation, the harder to change the implementation later without changing the header file. It can be a problem in a complex and huge software, where changing something in a deeper layer can result more hours compilation time.

In my example I show you an implementation of the stack data type. Stack is a very simple type, we can push elements to it, pop these elements out and get the number of the stored elements. The stack can be implemented in different ways, we can use vector or linked list to make it work.

In this example I’m going to store integer value in it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef STACK__H
#define STACK__H
 
class Stack
{
public:
    Stack(const int size);
    ~Stack();
 
    Stack&  push(const int value);
    int     pop();
    int     size() const;
 
private:
    int   *entries;
    int   stack_size;
};
 
#endif
#ifndef STACK__H
#define STACK__H

class Stack
{
public:
    Stack(const int size);
    ~Stack();

    Stack&  push(const int value);
    int     pop();
    int     size() const;

private:
    int   *entries;
    int   stack_size;
};

#endif

There’s a problem with this header file. Using int *entries assumes we want to use array to store values. However it’s good for shorter stacks, since handling larger stack is not so efficient, because we have to allocate in the memory the whole size of the stack at the beginning. Later, when you want to change it to linked list to it can be more efficient in memory consumption, you must recompile every part of your software that includes this header file.

Using another structure we can change very easy to different implementation without to compile the software again. Considering the next change:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef STACK__H
#define STACK__H
 
struct _stack_impl;
 
class Stack
{
public:
    Stack(const int size);
    ~Stack();
 
    Stack&  push(const int value);
    int     pop();
    int     size() const;
 
private:
    _stack_impl *impl;
    int         stack_size;
};
 
#endif
#ifndef STACK__H
#define STACK__H

struct _stack_impl;

class Stack
{
public:
    Stack(const int size);
    ~Stack();

    Stack&  push(const int value);
    int     pop();
    int     size() const;

private:
    _stack_impl *impl;
    int         stack_size;
};

#endif

stack_impl structure gives opportunity to define properties in the cpp file. Stack_size is a property of the stack, so it can be in the header file.

In the cpp file that’s using linked list, the stack_impl looks like this:

1
2
3
4
5
6
7
8
9
10
11
struct Entry {
    Entry   *prev;
    int      value;
};
 
struct _stack_impl
{
    Entry     *first;
    Entry     *top;
    int       size;
};
struct Entry {
    Entry   *prev;
    int      value;
};

struct _stack_impl
{
    Entry     *first;
    Entry     *top;
    int       size;
};

and with array:

1
2
3
4
5
6
7
8
9
struct Entry {
    int      value;
};
 
struct _stack_impl
{
    Entry *entries;
    int    top;
};
struct Entry {
    int      value;
};

struct _stack_impl
{
    Entry *entries;
    int    top;
};

The implementation with linked list (stack.cpp) is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "stack.h"
 
struct Entry {
    Entry   *prev;
    int      value;
};
 
struct _stack_impl
{
    Entry     *first;
    Entry     *top;
    int       size;
};
 
Stack::Stack(const int size)
    : stack_size(size), impl(new _stack_impl)
{
    impl->first = 0;
    impl->top   = 0;
    impl->size  = 0;
}
 
Stack::~Stack()
{
    delete impl;
}
 
Stack&
Stack::push(const int value)
{
    if (stack_size == impl->size)
        throw "FULL";
 
    Entry *entry = new Entry;
    entry->value = value;
    entry->prev  = impl->top;
    impl->top    = entry;
    impl->size++;
    return *this;
}
 
int
Stack::pop()
{
    if (impl->size == 0)
        throw "EMPTY";
 
    Entry *temp = impl->top;
    impl->top = impl->top->prev;
    int value = temp->value;
    delete temp;
    return value;
}
 
int
Stack::size() const
{
    return impl->size;
}
#include "stack.h"

struct Entry {
    Entry   *prev;
    int      value;
};

struct _stack_impl
{
    Entry     *first;
    Entry     *top;
    int       size;
};

Stack::Stack(const int size)
    : stack_size(size), impl(new _stack_impl)
{
    impl->first = 0;
    impl->top   = 0;
    impl->size  = 0;
}

Stack::~Stack()
{
    delete impl;
}

Stack&
Stack::push(const int value)
{
    if (stack_size == impl->size)
        throw "FULL";

    Entry *entry = new Entry;
    entry->value = value;
    entry->prev  = impl->top;
    impl->top    = entry;
    impl->size++;
    return *this;
}

int
Stack::pop()
{
    if (impl->size == 0)
        throw "EMPTY";

    Entry *temp = impl->top;
    impl->top = impl->top->prev;
    int value = temp->value;
    delete temp;
    return value;
}

int
Stack::size() const
{
    return impl->size;
}

And the implemantation with array (stack2.cpp)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "stack.h"
 
struct Entry {
    int      value;
};
 
struct _stack_impl
{
    Entry *entries;
    int    top;
};
 
Stack::Stack(const int size)
    : stack_size(size), impl(new _stack_impl)
{
    impl->entries = new Entry[size];
    impl->top = -1;
}
 
Stack::~Stack()
{
    delete[] impl->entries;
}
 
Stack&
Stack::push(const int value)
{
    if (impl->top+1 == stack_size)
        throw "FULL";
    impl->top = impl->top+1;
    impl->entries[impl->top].value = value;
    return *this;
}
 
int
Stack::pop()
{
    if (impl->top == -1)
        throw "EMPTY";
    return impl->entries[impl->top--].value;
}
 
int
Stack::size() const
{
    return impl->top+1;
}
#include "stack.h"

struct Entry {
    int      value;
};

struct _stack_impl
{
    Entry *entries;
    int    top;
};

Stack::Stack(const int size)
    : stack_size(size), impl(new _stack_impl)
{
    impl->entries = new Entry[size];
    impl->top = -1;
}

Stack::~Stack()
{
    delete[] impl->entries;
}

Stack&
Stack::push(const int value)
{
    if (impl->top+1 == stack_size)
        throw "FULL";
    impl->top = impl->top+1;
    impl->entries[impl->top].value = value;
    return *this;
}

int
Stack::pop()
{
    if (impl->top == -1)
        throw "EMPTY";
    return impl->entries[impl->top--].value;
}

int
Stack::size() const
{
    return impl->top+1;
}

Using this somewhere (let this be the main.cpp), we can call the stack’s methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include
#include "stack.h"
 
int main()
{
  Stack stack(5);
  stack.push(2);
  stack.push(3);
  stack.push(4);
  printf("pop: %d\n", stack.pop());
  printf("pop: %d\n", stack.pop());
  printf("size: %d\n", stack.size());
  return 0;
}
#include
#include "stack.h"

int main()
{
  Stack stack(5);
  stack.push(2);
  stack.push(3);
  stack.push(4);
  printf("pop: %d\n", stack.pop());
  printf("pop: %d\n", stack.pop());
  printf("size: %d\n", stack.size());
  return 0;
}

Create the object files using g++:

1
2
3
  g++ -c main.cpp
  g++ -c stack.cpp
  g++ -c stack2.cpp
  g++ -c main.cpp
  g++ -c stack.cpp
  g++ -c stack2.cpp

Finally we create two different executable code:

1
2
  g++ stack.o main.o -o linked_list
  g++ stack2.o main.o -o array
  g++ stack.o main.o -o linked_list
  g++ stack2.o main.o -o array

So for unit testing I would create only a cpp file and put the class declaration and definition there inline, because it doesn’t have any implementation code that we want to hide. I guess the unit test be simple, perspicuous and brief, so guys, don’t separate it…