# Week 4 Solutions Doc
## Section 1: Dependency Injection & Strategy Pattern
In this exercise we'll take a look at two common design patterns in one class: dependency injection and the strategy pattern.
In your terminal, go into the `DependencyInjection` folder.
Dependency injection is a way of separating the construction of an object and the construction of one of its components. We can set the component object as part of the main object by passing it into the constructor, or a setter function.
In `FunctionSampler.h` and `FunctionSampler.cpp` you will find the definition of a `FunctionSampler` class. This class stores a function, and an object which can generate a set of points. The `FunctionSampler` has a method called `generateSamples` which uses the points generator object to generate a set of points, and then samples the function at these points and returns the result as a vector of doubles.
Notice that the `FunctionSampler` creates the `LinearPointsGenerator` inside its constructor code. This means that the way that the `LinearPointsGenerator` is created is hard-coded inside the `FunctionSampler` class, and if the constructor for `LinearPointsGenerator` changes signature, then the `FunctionSampler` class will have to be changed as well. The definitions of these two classes are therefore coupled.
### Exercise 1: Constructor Injection
Change the `FunctionSampler` class definition to make use of the _Dependency Injection_ pattern.
1. Re-write the constructor to allow the points generator to be passed in to the constructor instead.
2. In `DependencyInjection.cpp` rewrite the code in `main` to make use of your new constructor.
### Solution:
In `FunctionSampler.h` we need to change the constructor to take a unique pointer to the `LinearPointsGenerator` type.
```cpp=
class FunctionSampler
{
public:
FunctionSampler(std::function<double(double)> f,
std::unique_ptr<LinearPointsGenerator> g);
std::vector<double> generateSamples(double, double, int n);
private:
std::unique_ptr<LinearPointsGenerator> generator;
std::function<double(double)> function;
};
```
In `FunctionSampler.cpp` we need to change the implementation of the constructor so that the `generator` variable is set using `std::move` and the passed in pointer.
```cpp=
FunctionSampler::FunctionSampler(std::function<double(double)> f,
std::unique_ptr<LinearPointsGenerator> g)
: function(f)
{
generator = std::move(g);
}
```
In `DependencyInjection.cpp` can create the `LinearPointsGenerator` separately or in the arguments of the `FunctionSampler` constructor (which saves you having an empty pointer lying around in your code).
```cpp=
int main()
{
FunctionSampler FS([](double x){return x*x;},
std::make_unique<LinearPointsGenerator>());
auto samples = FS.generateSamples(0, 2, 10);
for(auto x : samples)
{
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
```
### Exercise 2: Setter Injection
1. Add a setter function for the `generator` variable that allows you to change the generator being used.
2. Add a default constructor which does not take a generator and does not set the `generator` variable.
3. Rewrite your main code to create a default `FunctionSampler` and then set the generator using your setter function.
### Solution:
In `FunctionSampler.hpp` declare the two new methods (constructor and setter).
```cpp=
class FunctionSampler
{
public:
FunctionSampler(std::function<double(double)> f,
std::unique_ptr<LinearPointsGenerator> g);
FunctionSampler(std::function<double(double)> f);
void setGenerator(std::unique_ptr<LinearPointsGenerator> g);
std::vector<double> generateSamples(double, double, int n);
private:
std::unique_ptr<LinearPointsGenerator> generator;
std::function<double(double)> function;
};
```
In `FunctionSampler.cpp` add the definitions:
```cpp=
FunctionSampler::FunctionSampler(std::function<double(double)> f) : function(f) {}
void FunctionSampler::setGenerator(std::unique_ptr<LinearPointsGenerator> g)
{
generator = std::move(g);
}
```
and in `DependencyInjection.cpp`:
```cpp=
int main()
{
FunctionSampler FS([](double x){return x*x;});
FS.setGenerator(std::make_unique<LinearPointsGenerator>());
auto samples = FS.generateSamples(0, 2, 10);
for(auto x : samples)
{
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
```
### Exercise 3: Strategy Pattern
The strategy pattern exploits run-time polymorphism to allow us to change the "strategy" used by a class during the program's execution. The "strategy" could be the implementation of anything handled by some helper class: in this case we will look at the points generator used by our `FunctionSampler` class.
We have defined two classes, `LinearPointsGenerator` (which we have used so far and which generates linearly spaced points between the min and max) and `RandomPointsGenerator` (which generates uniform random points between min and max). We will use the strategy pattern to allow our class to change approach at runtime.
1. Define an abstract base class `SamplePointsGenerator` which contains `generateSamples` as a pure, virtual method.
2. Make `LinearPointsGenerator` and `RandomPointsGenerator` publicly inherit from this base class.
3. Modify your `FunctionSampler` class to use a pointer to the base class.
4. Go into your `main` function and write a program where you first generate a linearly spaced sample of your function, and then a random sample of your function, by changing the generator using your setter method.
5. Print both samples to the screen so that you can see that it is working.
### Solution:
In `FunctionSampler.hpp` add a base class and modify the `LinearPointsGenerator` and `RandomPointsGenerator` classes to inherit from it:
```cpp=
class SamplePointsGenerator
{
public:
SamplePointsGenerator();
virtual std::vector<double> generatePoints(double, double, int) = 0;
};
class LinearPointsGenerator : public SamplePointsGenerator
{
public:
LinearPointsGenerator();
std::vector<double> generatePoints(double, double, int);
};
class RandomPointsGenerator : public SamplePointsGenerator
{
public:
RandomPointsGenerator();
std::vector<double> generatePoints(double, double, int);
};
```
and then modify your `FunctionSampler` class to change the pointers to `LinearPointsGenerator` to the base class `SamplePointsGenerator`.
```cpp=
class FunctionSampler
{
public:
FunctionSampler(std::function<double(double)> f,
std::unique_ptr<SamplePointsGenerator>);
FunctionSampler(std::function<double(double)> f);
void setGenerator(std::unique_ptr<SamplePointsGenerator> g);
std::vector<double> generateSamples(double, double, int n);
private:
std::unique_ptr<SamplePointsGenerator> generator;
std::function<double(double)> function;
};
```
And the implementation for the injector functions in `FunctionSampler.cpp` needs to be changed likewise:
```cpp=
FunctionSampler::FunctionSampler(std::function<double(double)> f,
std::unique_ptr<SamplePointsGenerator> g)
: function(f)
{
generator = std::move(g);
}
void FunctionSampler::setGenerator(std::unique_ptr<SamplePointsGenerator> g)
{
generator = std::move(g);
}
```
Now that we've implemented a class capable of run-time polymorphism (due to the pure virtual function in the base which will be overridden by the derived classes) and made our `FunctionSampler` class point to the base class, our injection method should work with pointers to either derived class. This means in `main` we can switch from linear to random sampling at will without having to create a new `FunctionSampler` object!
```cpp=
int main()
{
FunctionSampler FS([](double x){return x*x;},
std::make_unique<LinearPointsGenerator>());
auto samples = FS.generateSamples(0, 2, 10);
for(auto x : samples)
{
std::cout << x << " ";
}
std::cout << std::endl;
FS.setGenerator(std::make_unique<RandomPointsGenerator>());
samples = FS.generateSamples(0, 2, 10);
for(auto x : samples)
{
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
```
### Exercise 4:
Allowing someone to set the generator using an arbitrary pointer introduces some risk into your class: the pointer being used could be empty / invalid, and accesses to invalid memory would result in an error such as a segmentation fault. Ensure your class is protected against this possibility.
### Solution:
To minimise the risk associated with having an object which can be incomplete, we need to at minimum prevent usage of the generator inside `generateSamples` if the `generator` pointer is not set:
```cpp=
std::vector<double> FunctionSampler::generateSamples(double a, double b, int n)
{
if (!generator)
{
throw std::runtime_error("Function sampler has no set generator.");
}
std::vector<double> vec = generator->generatePoints(a, b, n);
for (auto &v : vec)
{
v = function(v);
}
return vec;
}
```
`std::runtime_error` is an appropriate exception to throw since this is an exception that cannot be foreseen and is not related to logical issues such as the domain of a function.
Furthermore, if you haven't already, the `generator` member should be `private`, so that it cannot be accessed directly from outside the class. This will help to protect it from being changed to something invalid, reset, or having its memory tampered with. Having a `unique_ptr` also helps to discourage unwanted tampering with the object since there won't be other smart pointers to it. (Technically there are ways around this by extracting the raw pointer before passing the unique pointer to the constructor / setter, but this would have to be a very deliberate attempt to undermine the safety of the class/program!)
You may wish to prevent the `setGenerator` and constructor methods from setting the `generator` pointer to an empty pointer like this:
```cpp=
FunctionSampler::FunctionSampler(std::function<double(double)> f,
std::unique_ptr<SamplePointsGenerator> g)
: function(f)
{
if(!g)
{
throw std::runtime_error("Function sampler generator cannot be set to empty pointer.");
}
generator = std::move(g);
}
void FunctionSampler::setGenerator(std::unique_ptr<SamplePointsGenerator> g)
{
if(!g)
{
throw std::runtime_error("Function sampler generator cannot be set to empty pointer.");
}
generator = std::move(g);
}
```
The safest option would be to use constructor and setter methods like these which prevent setting the generator to an empty pointer, and then **remove the constructor allowing the incomplete object to be constructed**. The generator can then be changed later using the setter method.
There are times where it may be appropriate to allow a class to have a member which is set to an empty smart pointer or `nullptr`; this is a decision that you will have to make depending on the desired behaviour of the class.
In C++ we cannot guard against all possible violations to memory: C++ grants access to virtual memory and any part of the memory _can_ be pointed to, read, and altered. The guards help to prevent mistakes and misuse, but they cannot prevent machiavellian sabotage!
## Section 2: Templates & Function Overloading
In these exercises we'll explore function templates and class templates. We'll start by writing a simple function template and making use of operator overloading to apply it to a number of different types.
### Exercise 1:
In `Polynomial.h` and `Polynomial.cpp` there is a simple function `quadratic` which calculates $ax^2 + bx + c$ for integer inputs `a`, `b`, `c`, and `x`.
If we were to rewrite this function for `double`, `float`, `uint`, and many others, it would look exactly the same except for the parameter / return types. This is therefore an ideal candidate for a template!
1. Rewrite the `quadratic` function as a template function to generalise over the parameter/return type.
- Separate your declaration and defintion between the header and cpp file just like normal.
2. Write explicit instantiations for types `int` and `double` in the `Polynomial.cpp` file.
- See the bottom of [the section on organising and compiling code with templates](https://github-pages.ucl.ac.uk/research-computing-with-cpp/04cpp3/sec02Templates.html) if you're not sure how to do this.
3. In `Templates.cpp`, make calls to both the `int` version and the `double` version and print out the results to check that they both work.
4. In `Templates.cpp` add the following line: `double d_quad = quadratic(1.4, 2, 3.9, d_x);`. This uses _implicit instantiation_ i.e. we have not given the compiler the precise type that we want in `<>` but have left it to the compiler to infer. Try to recompile and work out why it fails.
- Hint: consider why `double d_quad = quadratic(1.4, 2.0, 3.9, d_x);` does pass instead, and what assumptions the compiler is making about the types of numbers.
5. Fix this line by using explicit instantiation instead.
### Solution:
In `Polynomial.h` we need to declare the function as a template, with one `typename` template parameter. (I've called it `numType` since we're expecting a numeric type of some kind.)
```cpp=
template <typename numType>
numType quadratic(numType a, numType b, numType c, numType x);
```
In `Polynomial.cpp` we then define the function (note that we have to re-use the template keyword):
```cpp=
template <typename numType>
numType quadratic(numType a, numType b, numType c, numType x)
{
return a*x*x + b*x + c;
}
template int quadratic<int>(int, int, int, int);
template double quadratic<double>(double, double, double, double);
```
After the function definition are our instantiations for the `int` and `double` type. This means that right now these are the only types for which this function is defined. Our `main` code, which only includes the header `Polynomial.h` can't instantiate it for any other types, since it doesn't have access to the definition, only the declaration.
In `Templates.cpp` (using implicit instantiation):
```cpp=
int main()
{
double d_x {1.52};
double d_quad = quadratic(1.4, 2.5, 3.9, d_x);
std::cout << d_quad << std::endl;
int i_x {5};
int i_quad = quadratic(0, 2, 9, i_x);
std::cout << i_quad << std::endl;
return 0;
}
```
**N.B.** You can choose to use implicit or explicit instantiation here. Explicit instantiation would look like this:
```cpp=
int main()
{
double d_x {1.52};
double d_quad = quadratic<double>(1.4, 2.5, 3.9, d_x);
std::cout << d_quad << std::endl;
int i_x {5};
int i_quad = quadratic<int>(0, 2, 9, i_x);
std::cout << i_quad << std::endl;
return 0;
}
```
Implicit instantiation can fail however, like in our example:
```cpp=
double d_quad = quadratic<double>(1.4, 2, 3.9, d_x);
```
The error message is illuminating however, so let's break it down:
```bash=
Templates.cpp:11:47: error: no matching function for call to ‘quadratic(double, int, double, double&)’
11 | double d_quad = quadratic(1.4, 2, 3.9, d_x);
| ^
In file included from Templates.cpp:2:
../include/Polynomial.h:6:9: note: candidate: ‘template<class numType> numType quadratic(numType, numType, numType, numType)’
6 | numType quadratic(numType a, numType b, numType c, numType x);
| ^~~~~~~~~
../include/Polynomial.h:6:9: note: template argument deduction/substitution failed:
Templates.cpp:11:47: note: deduced conflicting types for parameter ‘numType’ (‘double’ and ‘int’)
11 | double d_quad = quadratic(1.4, 2, 3.9, d_x);
```
1. The first part says `no matching function for call to ‘quadratic(double, int, double, double&)’`.
- This shows us that the compiler is interpreting `1.4`, `3.9`, `d_x`, and the return type as doubles but `2` as an `int`.
2. The next part indicates that there is a candidate function which it tried to match with the inferred types:`candidate: ‘template<class numType> numType quadratic(numType, numType, numType, numType)’`
- This is our template. Note in particular that the return type and all parameter types are the same (`numType`).
3. The final part tells us why the compiler eventually fails: `deduced conflicting types for parameter ‘numType’ (‘double’ and ‘int’)`
- The compiler has interpreted the parameters as a mix of `double` and `int`, and can't decide which one ought to be substituted for `numType`. In other words, there is no substitution for `numType` which allows for both `double` and `int` in the signature, since they all must be the same type.
Using an explicit instantiation `quadratic<double>` fixes this problem because when the compiler knows that the type of all the arguments is `double`, the `2` is implicitly cast to `double`.
### Exercise 2:
We've provided another implementation of the `Fraction` class from last week. This version includes a `Simplify` method, and also `Multiply` and `Add` methods which multiply or add together two fractions. In fact, being able to multiply and add is all that we need to meet the requirements of our templated polynomial function! In this exercise we'll overload the `*` and `+` operators for our fraction class, and show that we can then use this with our template.
1. In `Fraction.hpp` add declarations to overload `operator*` and `operator+`. You can finds examples of this [in the notes on "Using Templates with Overloaded Functions"](https://github-pages.ucl.ac.uk/research-computing-with-cpp/04cpp3/sec02Templates.html).
2. In `Fraction.cpp` add definitions for these operators.
- Overloading the `*` and `+` operators allow us to define fractions `f1` and `f2`, and then write expressions like `f1+f2` instead of `f1.Add(f2)`. This will match what is in our `quadratic` function, and other arithmetic functions!
3. Rather than adding yet another instantation to `Polynomial.cpp`, let's make our template a bit more flexible. Remove the definition of `quadratic` from `Polynomial.cpp` and place it directly in the header `Polynomial.h` instead.
- This will make the full template definition available in anything that includes the header file, allowing other parts of the code to generate instantiations for other types without them having to be explicitly instantiated ahead of time in the `Polynomial.cpp` file.
- Putting templated code directly in the header file is very common, and the basis of (and motivation for) many header-only libraries. Header only libraries put _all_ their code directly in header files instead of `cpp` files, and thus the libraries are not separately compiled and linked. This is usually to take advantage of heavy templating in the code.
4. Add code to your `main` function to apply your quadratic function using `Fraction` types and print the result.
- We have overloaded the `<<` operator for you so that you can use `Fraction` with `cout`. E.g. for a `Fraction f`, we can write `std::cout << f << std::endl`.
### Solution:
Add the following declarations to `Fraction.h` inside the `Fraction` class declaration
```cpp=
Fraction operator*(Fraction y);
Fraction operator+(Fraction y);
```
and add the following definitions (or equivalent) to `Fraction.cpp`
```cpp=
Fraction Fraction::operator*(Fraction y)
{
return Multiply(y);
}
Fraction Fraction::operator+(Fraction y)
{
return Add(y);
}
```
Modify `Polynomial.h` to include the full definition:
```cpp=
#pragma once
template <typename numType>
numType quadratic(numType a, numType b, numType c, numType x)
{
return a*x*x + b*x + c;
}
```
In your `main` you can now add some extra code like:
```cpp=
Fraction f(2, 8);
std::cout << f << std::endl;
std::cout << quadratic(Fraction(1,9), Fraction(7,8), Fraction(9,11), f) << std::endl;
```
### Exercise 3
It's now time to try writing a class template instead. Write a class to represent an $N \times M$ matrix with elements of arbitrary type in `MatrixTemplate.h`. (You can make this header only for the sake of making the template easy to use.)
Try to consider the following points as you design your class:
- How should you store your data for your matrix? Is you solution memory safe?
- How should data be accessed? What should be public and private?
- How should you initialise your matrix?
- What functionality should a matrix class have?
- Will your template work with `int` and `double`?
- Will your template work with `Fraction`? If not, what other functionality would you need to overload `Fraction` to make it usable with your matrix template?
Use your class in `main` in `Templates.cpp` to create a matrix and test some of your functionality.
### Solution:
There are many, many ways of writing a matrix class in C++, so we will go over some key points and an example here. You may well have chosen to implement it differently, but should still think about the same ideas.
There are almost endless possibilities for functionalities to put into a Matrix class so we will just implement a simple class which outlines the major considerations of designing a class like this.
First, we will want to template our class using some numerical type which we will call `numType`:
```cpp=
template<typename numType>
class Matrix
{
public:
Matrix(uint N, uint M)
{
for(uint i = 0; i < N; i++)
{
vector<numType> row(M);
data.push_back(row);
}
}
void printMatrix()
{
for(auto &row : data)
{
for(auto &x : row)
{
std::cout << x << " ";
}
std::cout << std::endl;
}
}
private:
vector<vector<numType>> data;
};
```
This is a simple start point which provides some useful properties:
- We can print our matrix using range based loops, so this code is agnostic regarding the size of the matrix.
- We store our data as a vector of vectors. This provides a 2D vector which can be accessed using `data[i][j]`.
- We overload the `()` operator to allow access to our data, so if we have a matrix `M` we can access $M_{2,3}$ with `M(2,3)`. This returns _by reference_ so that we can use it to alter values as well as read them.
- We make use of the `at()` method provided by `vector` to do automatic bounds checking for us. If you don't do this, you will need to make sure manually that the user never tries to read or assign to an element that is outside of the bounds of your matrix.
- The data is `private`, which means that the user only interacts with the class through e.g. the `()` operator. This means that we can change the _implementation_ (swapping out the way we store the data for something else like an array) without affecting the _interface_ and therefore not impacting any code that is built using our class.
- `vector` handles memory allocation and freeing for us so our class will not cause memory leaks.
- The constructor takes `uint` (unsigned ints) to enforce that `N` and `M` are greater than zero.
What if we want to use it with something like `Fraction`? Well, we have initialised the matrix to the default of the type (for `int`, `float` etc. this is `0`), but `Fraction` doesn't have a default constructor, so we would need to add one to our `Fraction` class to use it with this version of the Matrix class.
```cpp=
Fraction::Fraction() : numerator(0), denominator(1) {}
```
Alternatively you would need to write another Matrix constructor which would take a fraction or vector of fractions in order to use that class.
It would make sense to add some functionality to this class, for example overloading the addition and multiplication operators. In order to do this, you should check that the dimensions of your two matrices are compatible: two matrices which are added together must bother be $N \times M$, whereas for multiplication an $N \times M$ matrix can multiply a $M \times M^\prime$ matrix to form an $N \times M^\prime$ matrix. Naturally, we can do this by testing the properties of the matrices and throwing an exception if they are incompatible, which you will already know how to do. So here let's explore another option: enforcing correctness using templates. Consider the following class:
```cpp=
template<typename numType, uint N, uint M>
class Matrix
{
public:
Matrix()
{
for(uint i = 0; i < N; i++)
{
vector<numType> row(M);
data.push_back(row);
}
}
numType& operator()(uint i, uint j)
{
return data.at(i).at(j);
}
void printMatrix()
{
for(auto &row : data)
{
for(auto &x : row)
{
std::cout << x << " ";
}
std::cout << std::endl;
}
}
Matrix<numType, N, M> operator+(Matrix<numType, N, M> Mat2)
{
Matrix<numType, N, M> result;
for(uint i = 0; i < N; i++)
{
for(uint j = 0; j < N; j++)
{
result(i,j) = (*this)(i,j) + Mat2(i,j);
}
}
return result;
}
template<uint M2>
Matrix<numType, N, M2> operator*(Matrix<numType, M, M2> Mat2)
{
Matrix<numType, N, M2> result;
for(uint i = 0; i < N; i++)
{
for(uint j = 0; j < M2; j++)
{
result(i,j) = 0;
for(uint k = 0; k < M; k++)
{
result(i,j) += (*this)(i,k) * Mat2(k,j);
}
}
}
return result;
}
vector<vector<numType>> data;
};
```
This has some additional properties:
- The dimensions of the matrix `N` and `M` are now templated as well. We can instantiate a $2 \times 3$ integer matrix as `Matrix<int, 2, 3> myMatrix;`
- The overloaded `operator+` is only compatible with other matrices of the same type and dimensions. (We could actually relax this if we wanted, and use a second numeric type to allow for e.g. adding integers and floats.)
- The overloaded `operator*` has an additional template parameter `M2`, which defines the number of columns in the second matrix. This allows us to multiple together matrices which are not square but meet the correct conditions for matrix multiplication i.e. the number of columns in the first matrix is equal to the number of rows in the second. This operator will not be defined for matrices which are not compatible, and therefore code which tries to multiply such matrices will not compile.
- These operators use only `+` and `*` arithmetic so are compatible with arithmetic types such as `int`, `float`, `double`, and `Fraction` as substitutions for the templated type.
- The obvious drawback of this method is that the dimensions of matrices need to be determined at compile-time, but the trade-off is correctness enforced by the type system.
- An additional drawback is that this may lead to bloat in the executable, as different classes and functions may be compiled for different sized matrices.
**N.B.** The `this` keyword is a pointer to the current object, so when we write `(*this)(i,j)` we are calling the `(i,j)` operator on the current object (by derefencing the `this` pointer).
## Section 3: Resource Acquisition Is Initialisation (RAII)
In this section we'll explore the RAII pattern in the context of file access. We'll start with a pre-written example in an old-school C style approach, and update this by wrapping our file access in a class implementing RAII, and look at how files are accessed in modern C++.
### Exercise 1:
The focus will be on a function called `printProcessedFile`, which opens a file and reads data from it, passes each piece of data into a function (in this case `factorial`) and prints it out, and then finally closes the file again.
1. Take a look at `RAII.cpp`. Initially, we have a C-style approach to opening and reading a file (with some C++ exceptions thrown added in). Let's go through the steps that it takes to open, read, and close the file in :
- The file is opened with `std::fopen`. If this fails an exception is thrown.
- The file that we will read is called `testFile.txt`, and contains integers separated by whitespace.
- Each piece of data is read individually using `std::fscanf`, and passed to the `factorial`, until we reach the end of the file `EOF`.
- The file is closed with `std::fclose`.
2. Under what circumstances might the file be _opened_ but not _closed_?
- Hint: Check the `factorial` function.
3. Modify the `testFile.txt` so that an exception is thrown by `factorial` during `printProcessedFile`. Compile and run the program and check the output to see that we indeed have not closed the file. (Look out for the `file opened` and `file closed` couts.)
### Solution:
`factorial` is only defined for $n \ge 0$, and throws an exception if it receives a negative number. If `factorial` throws an exception, then `printProcessedFile` will terminate early before closing the file.
You can cause this to happen by modifying `testFile.txt` to include a negative number e.g. `1 9 -4 2 5`.
Compiling and running will show that the file is not closed:
```bash=
File opened.
1! = 1
9! = 362880
-4! = Exception caught: n! is invalid for n < 0
We no longer have access to the file handle.
```
### Exercise 2:
- Write a class called `FileManager` in `FileManager.h` and `FileManager.cpp`. This class should implement RAII by meeting the following conditions:
1. Resources should be acquired (i.e. the file should be opened) in the constructor.
- The constructor should throw an exception if opening the file fails.
2. Resources should be released (i.e. the file should be close) in the destructor.
- **Be careful that you don't try to close a file that isn't open as this will cause a seg fault.** This is similar to trying to delete a pointer which is not allocated.
- Add `cout` statements to the constructor and destructor to report when the file is opened and closed.
- You should also add a function `getData` which reads a piece of data from the file.
- Rewrite `printProcessedFile` to use your `FileManager` class instead of handling file opening and closing directly. Test that it works, and that the file is closed _even if an exception is thrown by_ `factorial`.
### Solution:
In `FileManager.h` we need to give the class a constructor, destructor, a member variable which represents the handle to the file, and a function for reading a piece of data from it.
```cpp=
#pragma once
#include <string>
class FileManager
{
public:
FileManager(std::string);
~FileManager();
bool getData(int&);
private:
FILE *file;
};
```
We then move the functionality for opening, reading, and closing the file from `printProcessedFile` into the `FileManager` class in `FileManager.cpp`:
```cpp=
#include "FileManager.h"
#include <iostream>
FileManager::FileManager(std::string filename)
{
file = std::fopen(filename.c_str(), "r");
if(!file) {
std::string err_msg = "Failed to open file " + filename;
throw std::runtime_error(err_msg);
}
std::cout << "File opened." << std::endl;
}
FileManager::~FileManager()
{
if(file)
{
std::fclose(file);
std::cout << "File closed." << std::endl;
}
}
bool FileManager::getData(int &n)
{
if (std::fscanf(file, "%d", &n) != EOF)
{
return true;
}
else
{
return false;
}
}
```
Most of the functionality has now been removed from `printProcessedFile` and it looks like this:
```cpp=
void printProcessedFile(std::string fileName)
{
FileManager FM(fileName);
int n;
while(FM.getData(n))
{
std::cout << n << "! = " << factorial(n) << std::endl;
}
}
```
Notice that we never need to manually close the file or destroy the object: the object will be destroyed when it goes out of scope (either by reaching the end of the function or by an exception being thrown) and the file will be closed when the object is destroyed.
Running with a negative number in our file gives us an output like this:
```bash=
File opened.
1! = 1
9! = 362880
-4! = File closed.
Exception caught: n! is invalid for n < 0
We no longer have access to the file handle.
```
Notice that in between the function being interrupted and the exception being caught the class destructor is being called and reporting "File closed."
### Exercise 3:
As we've already seen with smart pointers (which implement a kind of RAII for pointers to heap memory), modern C++ provides tools to handle this kind of resource management for us for many common situations. Indeed, similar to smart pointers, there are file reading classes in standard C++ which implements RAII for us: `fstream` (file I/O), `ofstream` (output), and `ifstream` (input). Best practice is to use these existing RAII-implementing structures where available (since these will be reliable, well tested solutions) and build your own RAII implementing classes when they are not.
You should now modify `printProcessedData` to use `ifstream` (since we are only reading and not writing data).
1. Add an include for `<fstream>` to `RAII.cpp`.
2. In `printProcessedFile`, replace your `FileManager` with `std::ifstream`. The constructor for `ifstream` takes a `std::string` representing the filename `testFile.txt`.
3. The loop is very similar but uses the condition: `while(filestream >> n)`, where `filestream` is your `ifstream` variable.
- This places the next piece of data in the file into the variable `n` and evaluates to true if it was not the end of file marker.
- You can then pass `n` to the factorial function inside the loop as usual.
Re-compile and run your code to test that it still works.
### Solution:
```cpp=
void printProcessedFile(std::string filename)
{
std::ifstream filestream(filename);
int n;
while(filestream >> n)
{
std::cout << n << "! = " << factorial(n) << std::endl;
}
}
```