Learning Concepts in C++ Part 2: Sets and Numbers
last edited on 2023-09-28
link to repository https://github.com/jgsuw/math_concepts_cxx
In my previous post, I outlined my plan to learn Concepts and Constraints in C++20 by implementing mathematical abstractions using primarily constrained templates. My end goal is to implement a template library for the Special Euclidean Group in three dimensions and its Lie Algebra. To reach this destination, several heirarchical concepts need to be developed.
So where to begin? In mathematics, one may construct abstractions from many different starting places. For instance, to create the Natural numbers one might begin with the Peano axioms. However, there are many other ways to go about constructing \(\mathbb{N}\), such as using Zermelo-Frenkel set theory, lambda calculus, etc.
Taking a step back, what is the purpose of implementing a mathematical abstraction as a template? Since I am using C++, it seems reasonable that the purpose is to compile programs that use the abstractions to do computation. C++ being compiled and statically typed, any computations it performs must compile to operations on basic types like int, float, double
etc.
With this in mind, I chose to begin with a concept I called PointSet
, which represents a collection of elements that share the same type.
The code means this: a template class S
fits the concept PointSet
if S
has an underling type
and a static method contains
which will return a boolean (true if \(x \in S\) and false otherwise). Here is an example of something which does not satsify the concept:
MySet
is an attempt to model the set of positive real numbers, where typename T
in the template might be float
or double
or some other floating / fixed point number type. Unfortunately, MySet
does not satsify PointSet
because MySet::type
is an unknown identifier (we did not include it in the definition). The fix is simple:
We can test this change with a template function:
which constraints the template parameter S
to the PointSet
concept. Putting this all together into a short program,
which happily compiles! Armed with a working constrained template, next I tried creating a few different templates for number sets. Here are simple templates for Natural numbers, Integers, Rationals, and Reals.
You may at this point noticed these templates have lots of static constexpr
properties and methods are static inline
. The reason for this is I view abstractions as literally static in an ontological sense. The set of Natural numbers for instance is a single static entity. On the other hand, classes in C++ very often contain mutable state which can differ amongst instances (objects) of an individual class. The static
keyword ensures that the properties like one, zero, contains
can be accessed without constructing an object of the class NaturalNumbers
e.g. without having to declare NaturalNumbers<int> my_object();
. Moreover, the constexpr
and inline
keywords require that the expressions must be available for evaluation at compile time. This seems appropriate, since the point of concepts is to enforce template properties also at compile time.
So we have some templates now that represent a few different number sets. Up to this point I have imposed only two requirements on a concept, which is relatively simple. Things will get more complicated as I progress to \(\text{SE}(3)\). Recalling that \(\text{SE}(3)\) builds upon concepts like groups and vectorspaces, a natural next step is to try and implement a concept Group
.