Data Oriented Programming Practice - 0

Recently I developed the desire to think about how we program. An activity that you spend a lot of your lifetime on, you want to make it an enjoyable one!

As a happy programmer, I am a good programmer. I want to have an easy time programming. I want to produce source code that I like. I don’t want to write code that will hinder me from proceeding some time in the future. Or to put it another way, I don’t want to encounter roadblocks I created myself earlier.

I’ve had this situation so many times and I started to call it hangover, because it felt like waking up and finding myself in a situation where reworking the whole thing felt like an inhumane task, a total defeat. But at the same time proceeding in the same manner was not responsible, it would cause pain. Mostly the solution was some workaround or ugly hack that questioned the whole architecture so far. Then I treated it as a blind spot from then on, or kept saying ‘Yeah that needs some rework some point down the line’ at best.

I want to refine my practice, to avoid the mentioned situation in the future. I think a key aspect is to incorporate the topics iteration and refactor into the process naturally. To review and iterate over a piece of code should not feel like destroying and rebuilding it.

Another desired property of my source code shall be inclusiveness: to use or to work with the code should not impose any behaviour to the user, i.e., the source code should be compatible to other programming styles and practices.

So here I start presenting my current programming practice! I think it is a very simple one, and I enjoy performing it.

Imagine we are working in an already existing codebase and want to add a certain functionality. At first we forget all entry points and places the code should later be plugged into etc. and just open a scope:

{
    //...
}

and ask

  1. What data do we operate on?

  2. What are the core lines of code that solve the problem?

and then we write down these lines of code at an adequate level of quality, including some necessary input and output example data. Then, if not yet possible, we arrange it so that we can compile and run this code snippet in isolation! In my case, this often means i just put the code snippet temporarily at the first uppermost location in the main function of my program and then compile and run as usual.

Before I finish this first post on programming practice, I want to provide an example. We want to print some intensity image to the console using ASCII characters. So let the input data be a two-dimensional array of intensities (float) and the output data a two-dimensional array of ASCII characters:

#include <vector>
#include <array>
#include <cmath>
int main() {

    {
        //input data
        size_t width = 50;
        size_t height = 20;
        std::vector<float> inputMem(width * height);
        //fill with some values
        for(int hI = 0; hI < height; hI++) {
            for (int wI = 0; wI < width; wI++) {
                inputMem[hI * width + wI] =
                std::sin(10.0f * 3.1415f * (float)wI / (float) width) *
                std::sin(12.0f * 3.1415f * (float)hI / (float) height);
            }
        }
        float * input = inputMem.data();

        //output data
        size_t outputSize = width * height + height;
        std::vector<char> outputMem(outputSize);
        char * output = outputMem.data();

        //map to ascii art
        const size_t mpN = 6;
        std::array<char,mpN> mapping = {' ','.',':','o','=','@'};
        for(int hI = 0; hI < height; hI++) {
            for(int wI = 0; wI < width; wI++) {
                auto inputIdx = hI * width + wI;
                auto outputIdx = hI * (width + 1) + wI;
                output[outputIdx] =
                mapping[
                    (
                        std::max(0.0f,
                        std::min(1.0f, input[inputIdx]))
                        + 0.05f
                    ) *
                    (mpN - 1)
                ];
            }
            output[hI * (width + 1) + width] = '\n';
        }

        //print
        fwrite(output, sizeof(char), outputSize, stdout);
    }
    return 0;

    //...
}

when run this produces

o==o      o==o      o==o      o==o      o==o
    .oo.      .oo.      .oo.      .oo.      .oo.
    .oo.      .oo.      .oo.      .oo.      .oo.
o==o      o==o      o==o      o==o      o==o

    o==o      o==o      o==o      o==o      o==o
.oo.      .oo.      .oo.      .oo.      .oo.
.oo.      .oo.      .oo.      .oo.      .oo.
    o==o      o==o      o==o      o==o      o==o

o==o      o==o      o==o      o==o      o==o
    .oo.      .oo.      .oo.      .oo.      .oo.
    .oo.      .oo.      .oo.      .oo.      .oo.
o==o      o==o      o==o      o==o      o==o

    o==o      o==o      o==o      o==o      o==o
.oo.      .oo.      .oo.      .oo.      .oo.
.oo.      .oo.      .oo.      .oo.      .oo.
    o==o      o==o      o==o      o==o      o==o

This is our first prototype to solve the given problem, but how do we plug that into our existing code base? See the next post!