summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2023-08-03 17:39:59 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2023-08-03 17:39:59 +0200
commit345480d2c0647a1a4bf8b7915d78155a261ea988 (patch)
tree7191575069fafd76796c839731314bf216c1c991
parent034a5fddfb292f22659f293d72ceb576f5c61d82 (diff)
A5: Final
-rw-r--r--a5/exercise.tex139
-rw-r--r--a5/matrix.cc8
2 files changed, 112 insertions, 35 deletions
diff --git a/a5/exercise.tex b/a5/exercise.tex
index 9491afe..2544cff 100644
--- a/a5/exercise.tex
+++ b/a5/exercise.tex
@@ -1,18 +1,24 @@
\title{A5: Generic Programming}
\input{preamble.tex}
-I decided to use std container for the elements of the matrix and
-initially went with \texttt{std::vector<int>}. But since we were going
-to do arithmentics on the data, and no requirements were made as to
-the arithmentics actually being those of a real matrix, I figure why
-not use \texttt{std::valarray<int>} and have all arithmentic
-operations be per-entry, re-suing the behaviour of
-\texttt{std::valarray} directly.
-
-In the change I discovered that \texttt{std::valarray} and
+In this exercise a matrix class is being developed and gradually
+evolved into a typesafe templated component.
+
+\bigskip
+
+I decided to use one of the standard containers for storing the elements of
+the matrix and initially went with \texttt{std::vector<int>}. But
+since we were going to do arithmentics on the data, and no
+requirements were made as to the arithmentics actually being those of
+a real matrix, I figure why not use \texttt{std::valarray<int>} and
+have all arithmentic operations be per-entry, re-using the behaviour
+of \texttt{std::valarray} directly.
+
+During this change I discovered that \texttt{std::valarray} and
\texttt{std::vector} ctor args for creating with a size and a default
value for all items are reversed, which would not be caught by the
compiler with both of them being integral:
+
\footnotesize\begin{lstlisting}[language=C++]
explicit vector( size_type count,
const T& value = T(),
@@ -22,9 +28,13 @@ valarray( const T& val, std::size_t count );
\end{lstlisting}\normalsize
That, I think, is rather unfortunate.
-When using a std container, all memory managing is handled by the
+When using a standard container, all memory managing is handled by the
container, even when throwing an exception inside the constructor, so
-no need to do anything clever inside the matrix implementation itself.
+tere is no need to do anything clever inside the matrix implementation
+itself. Otherwise a allocating base-class could have been made for
+memory ownership as seen during one of the presentations.
+The implementation for the statically typed \texttt{Imatrix} can be
+seen in the \texttt{imatrix.h} file.
I first wrote the entire \texttt{Imatrix} class with the values
dynamically allocated inside the \texttt{std::valarray} but wanted to
@@ -33,34 +43,93 @@ explore having the matrix dimenions available at compile-time
so they should be checkable at compile-time). I did this with template
size arguments.
-This removed a lot of error checks on matrix dimension matching, which
-is part of the type itself - nice!
+First of all, this removed a lot of error checks on matrix dimension
+matching, which is now part of the type itself.
+It also removed the need for the two integer members from the first
+imeplementation, carrying the matrix dimensions, so the change now
+also saved 16 bytes of memory.
-It also removed the need for the two integer members carrying the
-width and height - we now also saved 16 bytes of memory!
+And, best of all, we get a compile error if for example we try
+multiplying two matrices where their dimensions doesn't adhere to the
+algebraic rules!
-... and we get a compile error if for example we try multiplying two
-matrices where their dimensions doesn't adhere to the algebraic rules!
+For the \texttt{Row()} and \texttt{Column()} I briefly considered
+returning a span to be able to return references to a row or column
+for dynamic iteration, but decided to stick with
+the \texttt{std::vector} as described in the assignment.
+The code for the size-templated \texttt{Imatrix<N,M>} can be seen in
+the \texttt{imatrix\_nm.h} file.
-Final step is to templatize the underlying typeof the matrix. Done so
-by simply replacing int with T and all the places where 0 is assigned
-to the value type, replacing it with the default value T{}
+\bigskip
+
+Next step was to templatize the underlying type of the matrix. This
+was more or less done by simply replacing \texttt{int} with \texttt{T}
+and all the places where 0 is assigned to the value type, replacing it
+with the default value \texttt{T\{\}}.
+For the \texttt{Move()} function a $0$ assignment was needed and this
+I replaced with an empty initializer list, \texttt{\{\}}.
+The new templatized class is called \texttt{Matrix} and can be found in
+the \texttt{matrix.h} file.
When instantiating a template only the required/used functions are
generated (or so it appears).
-In other words, I can instante a \texttt{Matrix<Chess\_piece,2,2> c1}
-without getting any compile errors because I don't actually call
-anything on it.
-If I add a \texttt{c1 + 1} somewhere it will complain about not being
-able to add, but not any of the other unused operations.
-When adding the requirements of concepts, I will get errors for all
-the uses, not just the ones I use. In other words, concepts might end
-up requirering more work to be done by a developer than strictly
-needed. This I think is actually not that good.
-Adding the requirement to each of the member functions might be
-better.
-
-
-I actually made some errors in the matrix subscripts in the matrix
-multiplication/division code, which was shown to me by an assertion.
+In other words; a \texttt{Matrix<Chess\_piece,2,2> c1} can be instantiated
+without getting any compile errors as long as no algebraic methods are
+being called on it.
+If the line \texttt{c1 + 1} is added somewhere it will complain about
+not being able to add with \texttt{int}, but not any of the other unused
+operations.
+When adding the requirements of concepts, I expect to get errors for
+all the uses, not just the ones used in the program.
+In other words, concepts might end up requirering more work to be done
+by a developer than strictly needed. This might not always be
+desirebale - or at least it is something that should be thought about
+when designing a new component.
+One option to mimic the same behaviour, but with concepts, could
+perhaps be to add requirements to each of the member functions. This
+is however not what I did in this exercise.
+
+I made a concept, \texttt{supports\_modulo<T1,T2>}, used for only
+``activating'' the modulo operator if the underlying type supports it.
+
+I made an extensive, templated, test function that exercised all
+operator types of any of the matrix types - and here the compiler errors
+for \texttt{Chess\_piece} were not very helpful (the same goes
+for \texttt{std::string}). This can be seen in the \texttt{matrix.cc}
+file.
+In the test program, to prevent performing modulo operations on
+matrices that doesn't support them, I used
+the \texttt{supports\_modulo} concept together with a \texttt{if
+constexpr} to only try to call the module operator when enabled for
+the class.
+
+I made another concept, \texttt{supports\_matrix<T>}, used for the
+underlying type of the \texttt{Matrix<T>} class itself, requirering
+algebraic functionality on the underlying type.
+
+After adding requirements for all the needed operators,
+the \texttt{Chess\_piece} and \texttt{std::string} matrices now failed
+as expected but this time with a much shorter and to-the-point error
+message telling me exactly what would be needed for them work.
+
+I added empty dummy operator functions to fulfill the requirements (and
+commented out the \texttt{std::string} one) and now the program
+compiles and runs as expected.
+
+In hindsight, using the \texttt{std::valarray} may have resulted in me
+writing less code in the \texttt{Matrix} implementation, but because
+of implementation details inside \texttt{std::valarray} I may have
+ended up having to write more algebraic operators
+on \texttt{Chess\_piece} to get it to work.
+
+\bigskip
+
+The approach of first writing the statically typed class, then
+templatizing it, and finally analyzing the actual requirements of the
+underlying type and expressing those as a concept requirements, was a
+nice way to ``eat the elephant'', being able to focus on just one
+aspect of the code at one time. My guess is also that it would have
+been hard to foresee what the actual type requirements would end up being
+without having written the code yet.
+
\end{document}
diff --git a/a5/matrix.cc b/a5/matrix.cc
index 4a34fa0..b76526c 100644
--- a/a5/matrix.cc
+++ b/a5/matrix.cc
@@ -3,6 +3,7 @@
#include "matrix.h"
#include <iostream>
+#include <string>
template<typename T, std::size_t N, std::size_t M>
std::ostream& operator<<(std::ostream& s, const Matrix<T,N,M>& m)
@@ -224,4 +225,11 @@ int main()
Matrix<Chess_piece, 2, 3> m2;
test(m1, m2);
}
+
+ std::cout << "Matrix<std::sring, .., ..>:\n";
+ {
+// Matrix<std::string, 3, 2> m1;
+// Matrix<std::string, 2, 3> m2;
+// test(m1, m2);
+ }
}