# Lab Exercise 2: Arrays

We will build a unit test suite that shows that C-style raw arrays may be replaced without any loss of function or behaviour using higher level constructs made available by the C++ Standard Template Library

## Instructions

These instructions cover creating a new project in Microsoft Visual Studio. The source files used in this exercise may be configured in any other IDE/platform of choice to achieve the same results.
Using std::array

We will use a `std::array` to replace a raw C-style array.

• Create a new empty Visual Studio C++ project named array.
• Right-click the array solution on the left navigation pane and choose Properties
• Expand the Linker node and select the System item
• Select `“Console (/SUBSYSTEM:CONSOLE)`” from the SubSystem drop-down menu. This will allow us to view the results of running the application in the DOS Command window.
• Add a new header file named `Point` to the project. We will declare a simple structure named `Point`, and add some useful functions to support our structure. We will use the Point structure to exercise array of objects and their equivalents.
``````#pragma once

#include <iostream>
#include <ostream>

#define VALUES { csc240::Point{0.0, 0.0}, csc240::Point{1.0, 1.0}, csc240::Point{2.0, 2.0} }

namespace csc240
{
template <typename T, unsigned S>
inline unsigned arraysize( const T( &v )[S] ) { return S; }

struct Point { double x; double y; };

inline std::ostream& operator << ( std::ostream& stream, const Point& point )
{
stream << "Point - x: " << point.x << ", y: " << point.y << std::endl;
return stream;
}

inline bool operator == ( const Point& left, const Point& right )
{
return ( left.x == right.x ) && ( left.y == right.y );
}
}
``````
• Add a new C++ source file named `array` to the project. We will build a BDD style test that illustrates that we can use a `std::array` in exactly the same way as a C-array. The scenario for the test will be that we can replace any C-style array with a `std::array` instance. We will then test for a few common use cases such as initialising the array, iterating over values in the array, updating values in the array etc. Note: We chose to use the modern `using` syntax for defining an alias over the old C `typedef`.
``````#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "Point.h"

#include <array>
#include <iterator>
#include <sstream>
#include <type_traits>

using RawArray = csc240::Point[];
template <unsigned S> using ModernArray = std::array<csc240::Point, S>;

SCENARIO( "Raw C-style arrays may be replaced with std::array" )
{
GIVEN( "A raw C array of Point instances" )
{
const RawArray rawArray = VALUES;

const auto n = std::extent< decltype( rawArray ) >::value;
REQUIRE( n == csc240::arraysize( rawArray ) );

WHEN( "Represented as a std::array" )
{
const ModernArray<3> stdArray VALUES;

THEN( "Size should be same" )
{
REQUIRE( n == stdArray.size() );
}

THEN( "Sub-script operator should yield similar results" )
{
for ( int i = 0; i < n; ++i )
{
REQUIRE( rawArray[i] == stdArray[i] );
}
}

THEN( "Pointer arithmetic works identically" )
{
for ( int i = 0; i < n; ++i )
{
REQUIRE( *( rawArray + i ) == *( stdArray.data() + i ) );
REQUIRE( *( rawArray + i ) == *( &stdArray[0] + i ) );
}
}
}
}

GIVEN( "Empty C-array and std::array" )
{
const csc240::Point p{ 5.0, 5.0 };
const int index = 0;

RawArray rawArray { 1 };
std::array<csc240::Point,1> stdArray;

WHEN( "Setting values using sub-script operator" )
{
rawArray[index] = p;
stdArray[index] = p;

THEN( "Values are same" )
{
REQUIRE( rawArray[index] == stdArray[index] );
}
}
}

GIVEN( "std::array instance" )
{
ModernArray<0> stdArray;

WHEN( "Attempting to access value out of bounds" )
{
THEN( "Throws exception" )
{
REQUIRE_THROWS( stdArray.at( 0 ) );
}
}
}

GIVEN( "An input stream of numbers" )
{
std::stringstream ss;
ss << 1 << std::endl << 2 << std::endl << 3;

WHEN( "Initialising C-array or std::array" )
{
int ra[3];
std::array<int,3> sa;

THEN( "Work similarly" )
{
std::copy( std::istream_iterator<int>( ss ),
std::istream_iterator<int>(), ra );

ss.clear();
ss.seekg( 0, std::ios::beg );

std::copy( std::istream_iterator<int>( ss ),
std::istream_iterator<int>(), std::begin( sa ) );

REQUIRE( sa.size() == csc240::arraysize( ra ) );
for ( uint8_t i = 0; i < sa.size(); ++i )
{
REQUIRE( ra[i] == sa[i] );
}
}
}
}
}
``````
• Compile the project using `F7`
• Run the executable generated by the project using `F5`. Note:
• You can use `F5` to compile and run the project in one step.
• Use `CTRL+F5` to run the project without closing the DOS Command window. This is useful to see the output from the unit test suite.
Using std::vector

We will use a `std::vector` to replace a raw C-style array.

• Create a C++ source file named `vector` to the project. We will build a BDD style test that illustrates that we can use a `std::vector` in exactly the same way as a C-array. The scenario for the test will be that we can replace any C-style array with a `std::vector` instance. We will then test for a few common use cases such as initialising the array/vector, iterating over values in the array/vector, updating values in the array/vector etc. Note: We use `emplace_back` instead of `push_back` to add values to the vector. This avoids unnecessary copies of values being added to the vector.
``````#include "catch.hpp"
#include "Point.h"

#include <vector>

using PointArray = csc240::Point[];
using PointVector = std::vector<csc240::Point>;

SCENARIO( "Raw C-style arrays may be replaced with std::vector" )
{
GIVEN( "A raw C array of Point instances" )
{
const PointArray rawArray = VALUES;

const auto n = std::extent< decltype( rawArray ) >::value;
REQUIRE( n == csc240::arraysize( rawArray ) );

WHEN( "Represented as a std::vector" )
{
const PointVector stdVector VALUES;

THEN( "Size should be same" )
{
REQUIRE( n == stdVector.size() );
}

THEN( "Sub-script operator should yield similar results" )
{
for ( int i = 0; i < n; ++i )
{
REQUIRE( rawArray[i] == stdVector[i] );
}
}

THEN( "Pointer arithmetic works identically" )
{
for ( int i = 0; i < n; ++i )
{
REQUIRE( *( rawArray + i ) == *( stdVector.data() + i ) );
REQUIRE( *( rawArray + i ) == *( &stdVector[0] + i ) );
}
}
}
}

GIVEN( "Empty C-array and std::vector" )
{
const int index = 0;

PointArray rawArray{ 1 };
PointVector stdVector { 1 };
const csc240::Point p{ 5.0, 5.0 };

WHEN( "Setting values using sub-script operator" )
{
rawArray[index] = p;
stdVector[index] = p;

THEN( "Values are same" )
{
REQUIRE( rawArray[index] == stdVector[index] );
}
}
}

GIVEN( "std::vector instance" )
{
PointVector stdVector;

WHEN( "Attempting to access value out of bounds" )
{
THEN( "Throws exception" )
{
REQUIRE_THROWS( stdVector.at( 0 ) );
}
}

WHEN( "Adding items to end" )
{
THEN( "Vector grows" )
{
stdVector.emplace_back( csc240::Point() );
REQUIRE_FALSE( stdVector.empty() );
}
}
}

GIVEN( "An input stream of numbers" )
{
std::stringstream ss;
ss << 1 << std::endl << 2 << std::endl << 3;

WHEN( "Initialising C-array or std::vector" )
{
int ra[3];
std::vector<int> sa;
sa.reserve( 3 );

THEN( "Work similarly" )
{
std::copy( std::istream_iterator<int>( ss ),
std::istream_iterator<int>(), ra );

ss.clear();
ss.seekg( 0, std::ios::beg );

std::copy( std::istream_iterator<int>( ss ),
std::istream_iterator<int>(), std::back_inserter( sa ) );

REQUIRE( sa.size() == csc240::arraysize( ra ) );
for ( uint8_t i = 0; i < sa.size(); ++i )
{
REQUIRE( ra[i] == sa[i] );
}
}
}
}
}
``````
• Compile and run the project using `CTRL+F5`
Parallel Arrays

We will use a few standard library containers to replace parallel arrays.

• Add a new header file named `Month` to the project. We will define a simple structure that represents a month of year. We will use that while exercising parallel arrays and their higher level replacements.
``````#pragma once

#include <string>
#include <ostream>

namespace csc240
{
struct Month { uint8_t days; std::string name; };

inline bool operator == ( const Month& left, const Month& right )
{
return ( left.name == right.name ) && ( left.days == right.days );
}

inline std::ostream& operator << ( std::ostream& stream, const Month& month )
{
stream << "Month - name: " << month.name << ", days: " << month.days << std::endl;
}
}
``````
• Create a C++ source file named `parallel` to the project. We will build a BDD style test that illustrates that we can use a couple of different data structures to replace parallel arrays. We will use the same name of month and number of days in a month example used in the text book. We will use a `std::array<std::tuple,12>` data structure to replace the parallel array in the first test scenario. In the second scenario, we will use a `std::map<uint8_t,csc240::Month>` data structure to replace the parallel array. You will notice that in both options, we have unified access to a single index, rather than assuming that two different data structures share the same index.
``````#include "catch.hpp"
#include "Month.h"
#include <array>
#include <map>
#include <tuple>

namespace csc240
{
const uint8_t MONTHS = 12;

const std::string names[] =
{
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};

const uint8_t days[] =
{
31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31
};
}

SCENARIO( "A parallel array may be represented using array of std::tuple" )
{
GIVEN( "Given a parallel array for month name and days" )
{
WHEN( "Represented as an array of tuples" )
{
using csc240::MONTHS;
using csc240::names;
using csc240::days;

using Tuple = std::tuple<std::string, uint8_t>;
using Array = std::array<Tuple, MONTHS>;

Array array;

for ( uint8_t i = 0; i < MONTHS; ++i )
{
array[i] = std::make_tuple( names[i], days[i] );
}

THEN( "Access by month number is equivalent" )
{
for ( uint8_t i = 0; i < MONTHS; ++i )
{
REQUIRE( names[i] == std::get<0>( array[i] ) );
REQUIRE( days[i] == std::get<1>( array[i] ) );
}
}
}
}
}

SCENARIO( "A parallel array may be represented using std::map of csc240::Month" )
{
GIVEN( "Given a parallel array for month name and days" )
{
WHEN( "Represented as a map of Month objects" )
{
using csc240::MONTHS;
using csc240::Month;
using csc240::names;
using csc240::days;

using Map = std::map<uint8_t, Month>;
Map map;

for ( uint8_t i = 0; i < MONTHS; ++i )
{
map.emplace( i, Month{ days[i], names[i] } );
}

THEN( "Access by month number is equivalent" )
{
for ( uint8_t i = 0; i < MONTHS; ++i )
{
REQUIRE( names[i] == map[i].name );
REQUIRE( days[i] == map[i].days );
}
}
}
}
}
``````
• Compile and run the project using `CTRL+F5`