C++ Matrix & Functional Programing

The purpose of this post is to show and explain a bit more about practical usage of  C++ “lambda power”.  This time in developing a light, fully functional, fast, and hopefully  not that often seen, matrix creation utility. Functional programming style.

GitHub gist is here.

All nicely done for you

You are preparing Functional Programing, matrix something library. Let’s say some matrix operations. And you do know why the core of that library should be something a bit better than T[rows][cols]. Thus I am assuming you need quick and easy to use, but fully functional, core of your matrix suite. The one you can use straight away.  The core API where you actually contain the functionality of  keeping the matrices and making them available.

Usage of my single function API is simple.

You need to preserve those matrix dimensions.  Changing the cell value is simple.

The same call is used to both change the value of a matrix cell, or get to the value of the  cell.

The whole API is just one function with two arguments: row and column ordinal. How is matrix actually created, is it on stack or on heap, you do not need to know, Just use it.As the core of your matrix  FP solution.

So. Why is “my” solution good? Because it is simple, it is standard C++ and completely decouples form the tiny core that depends on the std lib. Which  means the two std lib types used can be replaced with the same but developed “in house”. Which in turn means this matrix solution  can be used in mission critical or embedded or IOT runtime environments. Let us first study the obvious alternatives.

The full std:: lib way

Stack means small(er) matrices, with nothing to worry about, regarding freeing the memory used by the matrix. Also, one can create and use those 2D arrays at compile time.

Let start with using  C++ std:: lib types. For stack based matrix we shall use std::array.

Note: coding style in this part is a bit pedestrian. That is deliberate, it  helps understanding.

Above is somewhat equal to int mx_1[2][3] . But here we use std::array  as one very handy and simple std type, for generic stack based 2D arrays of type T. And unlike native arrays, std::array instances can be moved, can be used with standard templates and algorithms easily.

Heap based standard solution

Heap is for much larger  run-time matrices. With the added issue of memory fragmentation. And strict run-time usage.

At the first thought, for  the heap based array I decided to use (also c++17 standard) std::unique_ptr<T[]>, basically a smart pointer to array. Here is a basic, complex but workable solution.

First to note: I made a wrong choice here. The std::vector<T>  is a much more elaborate type, but crucially, with the added benefit of giving to the whole mechanism, ability to be  both copied and moved around.  Also, above can be expanded, made more complex and involved; if for example we extend the functionality  so we can create heap based matrix of any size, or we can create a structure around this core, etc.

The Alternative

In any case, I am sure you will agree, not very simple looking code. To use it you probably just have to believe, I know what I am doing and that this will all work in all sorts of applications you might have.

I somehow do not subscribe to that point of view. Standard C++, 17 and beyond have rapidly increased in complexity. In my opinion, unwarranted complexity. These two solutions so far, just look too complex to me.

Armed with the C++ core language “Lambda Expressions” feature,  and two tiny, “base” functions, I might be so bold to present all you need to create and use standard C++ std lib 2D arrays in a very FP fashion.

All std lib usage is nicely hidden (aka encapsulated) and decoupled inside two tiny lambdas. Without your code knowing or worrying are they on the stack or on the heap. Beside you of course knowing the unique limitations of both kinds of memory.

Without further cogitations, here is the front of the mechanism:

Please take your time, going through the code above.  It is really packed with a lot of modern C++. Showing the “power of lambda”.

What surprises most readers, is the capture statement of the lambda above:  [ arry = source_() ]  .That is a legal C++.  Lambda capture statement is how users pass  lambda object constructor arguments, and name members to be used inside lambda.

Thus above we say: the result of the call source_()will be copied to the member arry upon constructing the lambda object. Watch carefully. The synopsis of the function object generated by compiler to implement this lambda is approximately this:

The key point of the above is: when implemented that lambda represents so called “local class”.  Let us repeat once more: We return the instance of a local class, when returning lambda from inside a function.

And this is the secret sauce. This gives us the greatest degree of encapsulation and decoupling. We use the inner lambda returned.  It has access to the closure where it was born. Most importantly to the memory block given to the constructor of the closure where our matrix lambda was born.

Remember  we do return by reference. And the lambda is “mutable” which is yet another, but powerful secret sauce.

API Sampling

How do we use this in practice. Provided we all understand why is it done as it is. First the inevitable macros, we actually used at the beginning of this post, without telling you.

As in the gist provided, for the above macros we  need two tiny functions to provide 2D arrays allocated on stack or on the heap . arr_stack and arr_heap.

Above we have completely encapsulated the mechanism of making arrays and thus decoupled the solution from this mechanism completely. Lets talk about this FP tiny API a bit more.

The requirements for actual types used above are: [ ] (aka indexing) operator and the ability to both be moved and copied. In case you want to replace them with something else you might fancy better.

Functional programming

The type of the lambda is irrelevant, its functionality is what matters. This is a single function API for both 2D array types: stack and heap. Now we can write matrix manipulation functions that are completely decoupled from matrix creation issues.

We simply have a matrix  completely encapsulated in a function. The full test, again in a FP style is at the bottom of that gist, already mentioned.

This is one very small but useful utility. Showing a lot of good sides of a mixture of modern C++ and  functional programming. All simply enabled with lambdas aka closures.

Usability

Using this mechanism we do “carry around” our matrices. Wherever we can copy or move the lambda instance we will take the access of the 2Darray kept inside it. I think you might have already noticed that.

This is giving the ability to pass these matrices by value and also return them by value.  And it all “just works”. Thanks to lambda implementation and thanks to few key points of this design.

You will quickly find out, to actually use this API , FP style is the best fit. As in examples above. Which does not mean OOP style is excluded. It is only, this solution is not suited for OOP. As it is not simple to keep lambda as a member in a class.

Finally

Several measures can be added to stop the abuse, but as it is, this utility can be used in very real, demanding projects. Also, both std::array and std::vector,quality alternative implementations, can be found on the net. That is in case your code is running in mission critical environments.

So little C++ so much good!
So little C++ so much good!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.