diff --git a/.gitignore b/.gitignore index 8f51131..d25c9e2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ exercises/3-containers/part1/test exercises/3-containers/part2/test exercises/4-pointers/pointers exercises/5-templates/part1/sum -exercises/5-templates/part2/test +exercises/5-templates/part2/main exercises/6.1-my-array/part1 exercises/6.1-my-array/part2 exercises/6.1-my-array/part3 diff --git a/exercises/5-templates/README.md b/exercises/5-templates/README.md index a4744e8..5ebad38 100644 --- a/exercises/5-templates/README.md +++ b/exercises/5-templates/README.md @@ -30,7 +30,9 @@ List the files in `part2`: ```bash $ cd archer2-cpp/exercises/5-templates/part2 $ ls -Makefile complex.cpp complex.hpp test.cpp +Makefile array.hpp main.cpp ``` -`complex.cpp` contains a working version of the complex number class. Change the class declaration and definitions to use type templating. +`array.hpp` contains the beginnings of a `FixedSizeArray` class. Update the class declaration and definitions to use type and non-type template parameters. Additionally, add new class member functions as directed by the TODO comments. `main.cpp` provides instructions for trying out and testing your implementation. + +As before, you can compile with `make`. diff --git a/exercises/5-templates/part2/Makefile b/exercises/5-templates/part2/Makefile index ca3fc0c..7931774 100644 --- a/exercises/5-templates/part2/Makefile +++ b/exercises/5-templates/part2/Makefile @@ -1,10 +1,10 @@ -CXXFLAGS = --std=c++17 -I../../include +CXXFLAGS = --std=c++20 -test : complex.o test.o +main : main.o $(CXX) $^ -o $@ -run : test - ./test +run : main + ./main clean : - rm -rf *.o test + rm -rf *.o main diff --git a/exercises/5-templates/part2/array.hpp b/exercises/5-templates/part2/array.hpp new file mode 100644 index 0000000..06c7e90 --- /dev/null +++ b/exercises/5-templates/part2/array.hpp @@ -0,0 +1,27 @@ +#ifndef CPPEX_ARRAY_HPP +#define CPPEX_ARRAY_HPP + +// TODO: Add template parameters to set the data type and array size at compile time +class FixedSizeArray { + +private: + int data[10]; + +public: + + FixedSizeArray() { + for (int i = 0; i < 10; ++i) { + data[i] = 0; // Does this work for all data types? + } + } + + // TODO: Overload the [] operator to provide access to the array elements + + // TODO: Create function size() that returns the size of the array + + // TODO: Create function reduce() that sums all elements of the array + + // TODO: Create function doubleInPlace() that doubles each element of the array +}; + +#endif diff --git a/exercises/5-templates/part2/complex.hpp b/exercises/5-templates/part2/complex.hpp deleted file mode 100644 index 70a7e50..0000000 --- a/exercises/5-templates/part2/complex.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef CPPEX_COMPLEX_COMPLEX_HPP -#define CPPEX_COMPLEX_COMPLEX_HPP - -// Simple complex number class -/* Add template typename definition here */ -class Complex { -public: - // Default value is zero - Complex() = default; - Complex(double real); - Complex(double real, double imag); - - // Access components - double real() const; - double imag() const; - - // Compute the complex conjugate - Complex conj() const; - - // Compute the magnitude and squared magnitude - double norm() const; - double norm2() const; - - // Declare comparisons - friend bool operator==(Complex const& a, Complex const& b); - friend bool operator!=(Complex const& a, Complex const& b); - - // Declare binary arithmetic operators - Assume both complex numbers have the same template type. - friend Complex operator+(Complex const& a, Complex const& b); - friend Complex operator-(Complex const& a, Complex const& b); - friend Complex operator*(Complex const& a, Complex const& b); - - /* - Extension: What if I want to add a Complex to a Complex? - What would be returned in this case? What should the return type - be to make this flexible? - Hint: Look up std::common_type from the type_traits header. - - If you complete this extension, uncomment the final test case in - test.cpp to test your implementation. - */ - - // Unary negation - friend Complex operator-(Complex const& a); - -private: - double re = 0.0; - double im = 0.0; -}; - - -#endif diff --git a/exercises/5-templates/part2/main.cpp b/exercises/5-templates/part2/main.cpp new file mode 100644 index 0000000..4835e0d --- /dev/null +++ b/exercises/5-templates/part2/main.cpp @@ -0,0 +1,44 @@ +#include "array.hpp" +#include + +void print(auto&& arr) { + /* Helper function to print the contents of a FixedSizeArray */ + std::cout << "[ " << arr[0]; + std::cout << "[ " << arr[0]; + for (int i = 1; i < arr.size(); ++i) { + std::cout << ", " << arr[i]; + } + std::cout << " ]" << std::endl; +} + +int main() { + // TODO: Initialise an empty fixed size array of 5 integers + std::cout << "Array size: " << arr.size() << std::endl; + print(arr); + + // TODO: Use a for loop to set the array values. E.g. 1-5 + print(arr); + + // TODO: Call doubleInPlace() and print the new array + arr.doubleInPlace(); + print(arr); + + // TODO: Initialise an empty fixed size array of 5 integers with the const specifier + print(constArr); + + // TODO: Print the output of calling reduce() + + // TODO: Check you get a compile time error if you try to set an element value in the + // const array, or if you call doubleInPlace() + + // TODO: Initialise a fixed size array of strings with element values "Hello" and "World!" + print(strArr); + + // TODO: Print the output of calling reduce() on strArr + + // TODO: Call doubleInPlace(). What happens? Can you explain this? + // EXTENSION: Can you change the behaviour of doubleInPlace for non numeric data types? + // E.g. Just print an error and leave the array unchanged. + // Hint: You may want to search for useful type traits, and look up 'if constexpr'. + print(strArr); +} diff --git a/exercises/5-templates/part2/test.cpp b/exercises/5-templates/part2/test.cpp deleted file mode 100644 index 27aa8a8..0000000 --- a/exercises/5-templates/part2/test.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// Catch2 is a unit testing library -// Here we let it create a main() function for us -#define CATCH_CONFIG_MAIN -#include "catch.hpp" - -#include "complex.hpp" - -TEST_CASE("Complex numbers are constructed real/imag parts readable") { - const Complex zero; - REQUIRE(zero.real() == 0.0); - REQUIRE(zero.imag() == 0.0); - - const Complex one{1}; - REQUIRE(one.real() == 1.0); - REQUIRE(one.imag() == 0.0); - - const Complex i{0, 1}; - REQUIRE(i.real() == 0.0); - REQUIRE(i.imag() == 1.0); - - const Complex z1{1, -83}; - const Complex z2 = z1; - REQUIRE(z1.real() == z2.real()); - REQUIRE(z1.imag() == z2.imag()); - REQUIRE(z2.real() == 1.0); - REQUIRE(z2.imag() == -83.0); -} - -TEST_CASE("Complex numbers can be compared") { - const Complex zero; - const Complex one{1}; - const Complex i{0, 1}; - REQUIRE(zero == zero); - REQUIRE(one == one); - REQUIRE(i == i); - REQUIRE(zero != one); - REQUIRE(zero != i); - REQUIRE(one != i); -} - -TEST_CASE("Complex numbers can have magnitude computed") { - REQUIRE(Complex{}.norm2() == 0.0); - REQUIRE(Complex{3,4}.norm2() == 25.0); -} - -// Pure real => z == z* -void CheckConjReal(double x) { - Complex z{x}; - REQUIRE(z == z.conj()); -} -// Pure imaginary => z* == -z -void CheckConjImag(double y) { - Complex z{0.0, y}; - - REQUIRE(z == -z.conj()); -} - -TEST_CASE("Complex numbers be conjugated") { - CheckConjReal(0); - CheckConjReal(1); - CheckConjReal(-3.14); - CheckConjReal(1.876e6); - - CheckConjImag(0); - CheckConjImag(1); - CheckConjImag(-3.14); - CheckConjImag(1.876e6); -} - -void CheckZplusZeq2Z(const Complex& z) { - REQUIRE(z + z == Complex{2*z.real(), 2*z.imag()}); -} -void CheckZminusZeq0(const Complex& z) { - REQUIRE(z - z == Complex{}); -} - -TEST_CASE("Complex number can be added and subtracted") { - CheckZplusZeq2Z(1); - CheckZplusZeq2Z(0); - CheckZplusZeq2Z(-1); - - CheckZminusZeq0(1); - CheckZminusZeq0(0); - CheckZminusZeq0(-1); - CheckZminusZeq0(Complex{1,2}); - CheckZminusZeq0(Complex{-42, 1e-3}); -} - -TEST_CASE("Complex numbers can be multiplied") { - const Complex i{0, 1}; - Complex z{1}; - z = z*i; - REQUIRE(z == i); - z = z*i; - REQUIRE(z == Complex{-1}); - z = z*i; - REQUIRE(z == -i); -} - -TEST_CASE("Complex numbers can be templated") { - const Complex z1{static_cast(3.4), static_cast(5.1)}; - const Complex z2{3.4, 5.1}; - REQUIRE(z1 != z2); - REQUIRE(z1.real() == 3); - REQUIRE(z1.imag() == 5); - REQUIRE(z1.norm2() == 34); -} - -/* -Uncomment the code below to test the binary operators extension exercise - -TEST_CASE("Complex number binary operators can use two different template types") { - const Complex z1{3, 5}; - const Complex z2{3.4, 5.1}; - REQUIRE(z1 + z2 == Complex{6.4, 10.1}); - REQUIRE(std::is_same>::value); - REQUIRE(std::is_same>::value); - REQUIRE(std::is_same>::value); -} -*/ diff --git a/lectures/5-templates/README.md b/lectures/5-templates/README.md index 9ad1abb..d1bdb09 100644 --- a/lectures/5-templates/README.md +++ b/lectures/5-templates/README.md @@ -507,8 +507,5 @@ In your clone of this repository, find the `5-templates` exercise. It contains t 4. Change the `sum()` function to use type templating. How does this change the output? -**Part 2** +**Part 2** - `array.hpp` contains the beginnings of a `FixedSizeArray` class. Add template parameters and complete the tasks in the TODO comments. -`complex.cpp` contains a working version of the complex number class. Change the class declaration and definitions to use type templating. - -As before, `test.cpp` holds some basic unit tests and you can compile with `make`.