Simple matrix for the rest of us

Are we done here, then?

Contents

No. We are not done here yet.  I would like to turn your attention to the problem hidden inside and its solution in dbj::static_matrix .

A general problem description.

If everything is static inside the type it is all shared, between all instances of the same type. Consider this:

If we create a few template definitions we get the same number of unique  types, like so:

So far so good. But. count is shared for all the instances of the same type.

Since the count is shared on the type level (it is static type member) one change applies to every other instance of that type. That is not so good unless it is a design requirement.

Same problem in the context of dbj::static_matrix

Let us assume dbj::static_matrix has this problem. Let us assume that is the issue:

We are getting 3 identical matrix outputs. This is because there is no instance bound data inside, it is all shared between all instance of the unique type mx9 There is a single data member inside and it is static,  so-called “class-wide”

// just a pod 2D array on the stack
inline static value_type data_[R][C]{};

Thus each instance of the template definition (aka the type) for the same template arguments T,R,Ccombination will share the same 2d array hidden inside.

What matters here are no objects (aka instances) but types aka classes. Which are in this case template instantiations.

Differentiate between same types

What are we going to do? We need, for example, two 3×3 matrices of int’s but we could not rely on instances to keep them separate in the memory.

We could redesign and re-do this template so that nothing is static, true. But this will lead to completely different run-time semantics. And we want, compile time. We want our matrices as fast as possible, as simple as possible, and not made on the (slower) heap.  Here is the solution.

Make  different template definitions

Add type Id as the template argument, and use it to make them definitions different.

Thus, template arguments, on our matrix,  will be  these:

There is a new template argument:UID_ It is quite enough for the UID_ to be just an ever increasing number.  When we create template definitions (aka types), simplified view on what we have now is this:

The last template argument effectively creates two distinct types above, which are actually both the same ‘thing’: A static matrix, of size 3×3 with integers in the cells. On the stack, all static. Simple, fast and nothing on the heap. But two different types.

The implementation

The UID_ argument.  This is what makes it possible. And, UID_ has to be a compile time constant. My quick (but not dirty) implementation:

#define DBJ_UID __COUNTER__ + 100

Where __COUNTER__ is MSVC/GCC/CLANG predefined macro value giving 101,102,103 … on the level of the compilation unit.

Application-wide solution

I assume we all know what the “compilation unit” is in C/C++.  Sometimes called “translation unit”. I also assume you do the following every time.

  1.  Use the “all is in the headers” approach.In essence modern variant of “Single Compilation Unit” application composition method. In this scenario,  the__COUNTER__ macro will produce ID’s unique on the level of application. Thus all the types of the dbj::static_matrix<T,R,C, DBJ_UID> will contain the unique ID’s.
  2. Use namespaces.In this scenario even if you do not use technique 1 you will have separate types because they will be adorned with namespaces.static_assert( ! std::is_same_v <space_a::mx9 , space_b::mx9 >);

The two above or just the second technique will solve all the problems on the application level. In the very unlikely event, you have them.

Conclusion

As ever if anybody needs advice on how to use dbj matrix,  in her project please do let us know, in the comments below, or through any other modern communication channel available.

Enjoy the standard C++