Runtime polymorphism without dynamic memory allocation

Another one on polymorphism

This time is about not using heap allocation while having runtime polymorphism. I will use std::variant for this, so nothing new. What got my attention is how the polymorphic objects are used if stored in a variant. This is the main topic of this short article.

The use case is a factory function that creates polymorphic objects.

Virtual inheritance

I’m taking it step by step, starting with the classic approach using virtual inheritance. For this, I need some pointers, of course.

#include <cassert>
#include <memory>

struct P {
    virtual int f(int) const = 0;
    virtual ~P() = default;
};

struct A : P {
    int f(int in) const override {return in + 1;}
};

struct B : P {
    int f(int in) const override {return in + 2;}
};

std::unique_ptr<P> factory(char o) {
    switch(o) {
        case 'a': return std::make_unique<A>();
        default: return std::make_unique<B>();
    }
}

int main() {
    assert(factory('a')->f(1) == 2);
    assert(factory('b')->f(1) == 3);
}

std::variant

The std::variant solution is to have a variant with all the possible types instead of pointers to the types. This will avoid heap allocations. And it will break the need for inheritance, having objects that are not coupled to a base class anymore. Continue reading Runtime polymorphism without dynamic memory allocation

Polymorphism with lambda functions

This is my 100th article and it was celebrated.

100th article

 

As I’m a fan of polymorphism, I play with different approaches on this subject. I want to find new ways of dealing with polymorphic objects under constrained scenarios. Not all of them are great, but every time I learn something new that I should or should not apply in real situations.

This time, the self-imposed context is:

    • C++11
    • Using only the standard library
    • Using std::array to have a homogenous list of objects

I want a list of objects where each object behaves differently.

struct Object {
    int id{};
    int value{};
};

std::array<Object, 2> objects;

 

But I cannot simply add a method to Object because I want a different method attached to each Object from the array. I was spinning around attaching lambdas to those objects for a while when it hit me: I could use another object to wrap my original one.

 

The wrapper knows about Object (T) and its corresponding lambda (Function)

template <typename T, typename Function>
struct Callable {
    std::function<Function> func_;
}

and it’s callable for simple use.

template <typename T, typename Function>
struct Callable {
    void operator()() {}
}

 

I use a lambda function with its first parameter being the original object (Object), the equivalent of this (think of self in Python). And any other parameters.

using MyObject = Callable<Object, void(Object&, int)>;

MyObject object{[](Object& object, int i) {
    object.id = i;
    object.value = i;
}};

 

When I call the object’s “behavior”, I pass all the arguments except “this”.

object(1);

 

How does it happen?

Callable is based on CRTP to be an Object and to pass an instance of Object (“this”) to the lambda function. It inherits from Object (template argument T) and safely casts itself to Object, thus obtaining an instance of Object.

template <typename T, typename Function>
struct Callable : T {
    template <typename... Args>
    void operator()(Args&&... args)
    {
        func_(static_cast<T&>(*this), std::forward<Args>(args)...);
    }

    std::function<Function> func_;
};

 

What’s wrong with this approach?

There are two possible deal-breakers depending on your context:

    • std::function prevents Callable to be inlined
    • Callable adds some memory overhead

And that’s it with my learning purpose experiment. Here’s the full source code: Continue reading Polymorphism with lambda functions

A more decoupled approach on static polymorphism

This is a follow-up of the Executing tasks based on static polymorphism article, which I recommend to be read for the full picture of what is about to come, as it offers details on why I study this approach and how I implemented it (compile-time iteration of a tuple).

My first attempt on C++ compile-time polymorphism is designed around a task struct. The requirement for a task is to implement an Execute method that will perform some work. This requires that the task struct is mine. Otherwise, if there’s some information provided by another library through some struct, I can wrap it in a task that has the required Execute method.

Inspired by some of Sean Parent’s talks about runtime polymorphism and some of its issues, I found another way of implementing static polymorphism. One that does not have any requirement for the input structs; they don’t need to implement a method nor they must be wrapped in other structs.

Along with the requirements in the previous article, I add these ones:

    • A container with multiple objects of different types so I have a list of items
    • For each object in the container, something different must be performed depending on its type (by iterating all objects, not handling it manually)
    • Objects in the container are provided by someone else and cannot be changed
    • C++11/14 compatible

This approach starts with some input structs in a tuple (the container); references to the objects constructed of the structs to prevent copies:

namespace input {
    struct A {
        int a;
    };

    struct B {
        int b;
    };
}

using Objects = std::tuple<input::A&, input::A&, input::B&, input::A&>;

Continue reading A more decoupled approach on static polymorphism

Executing tasks based on static polymorphism

If you want to skip reading and get to the code, then see some kind of C++ static polymorphism in the GitHub repository.

I’m a fan of tasks. I enjoy each time I implement any kind of system that executes tasks, from very simple ones (based on a list of tasks executed on a single thread) to multithreaded ones with features like cancellation and dynamic concurrency level.

My first encounter with the idea of having a list of objects that I iterate and call a function for each one of them was when I had to implement a feature that dynamically restricted users’ access to some content based on various conditions. You can think about a lot of if statements. I don’t like if statements, I struggle to avoid them because each one of them brings new responsibility, test cases, and maintenance costs. And I really enjoy seeing product and management people happy when their requirements are implemented at reasonable times. And being productive is a lot about code.

A way to decouple code is, of course, to separate concerns. And if you’re in a case with multiple concerns serving the same concept, you could use a polymorphic approach. You can have an interface for a task, some tasks implementations, and something to run all the tasks when needed. Everything you’ll see in this article is a light version because I’m focusing on the idea, not on details.

#include <array>
#include <iostream>
#include <memory>

class Task {
   public:
    virtual void Execute() = 0;
};

template <typename T>
struct Executor {
    T& tasks;

    void Execute()
    {
        for (auto& task : tasks) {
            task->Execute();
        }
    }
};

class A : public Task {
   public:
    void Execute() override { std::cout << 1; }
};

class B : public Task {
   public:
    void Execute() override { std::cout << 2; }
};

int main()
{
    using Tasks = std::array<std::unique_ptr<Task>, 2>;
    Tasks tasks{
        std::make_unique<A>(),
        std::make_unique<B>(),
    };

    Executor<Tasks> executor{tasks};
    executor.Execute();
}

 

Now I’ll add some constraints for learning purposes. Sometimes performance matters and you can’t always use the latest C++ standards, so I’ll go with these:

    • C++ 11
    • No heap allocations
    • Minimum stack allocations
    • No virtual methods

And I’m entering the world of templates because this can help me achieve compile-time polymorphism (C++ static polymorphism), and that’s what I’m aiming for. Continue reading Executing tasks based on static polymorphism

Polymorphism

Polymorphism is one of the principles I always guide myself by. It is, for me, a way of thinking. It always reminds me that any piece of code will be replaced one day by another. Or there will be other similar pieces that will be used in some cases.

Things will always change and many times I have no idea what will come. I could spend time trying to guess new situations (which can be a waste of resources) or I could be prepared for when the time comes.

I always think about each entity/object/model/structure of my application. What does it represent? Could there be any other similar entities? Could there be more entities exactly like it, no just one of it? What is its relation to other entities?

Let’s say I got to the need of more similar entities. How will I pass them to functions? What code will I change if I want to change only one of them, add a new one or remove an old one? How could I implement the behavior differences between them? If X then do this, if Y then do this, if Z then do this?

I know it’s easy to just throw some code, some if statements, and to duplicate some code because I need just one quick thing to do a little different. Why should I think of design and architecture? I just need some code to do something. And this is how projects end up, in weeks, months, or years, being very hard to maintain and understand. It’s always “just this one thing”, but 1 + 1 + 1 + 1 + 1 + 1 is 5. Oh, no, it’s 6.

It took me a lot of time to see these things and the learning never stops, but it pays off. I often read and practice to find better ways of understanding my data. How data is modeled is one of the most important aspects, because it will affect the entire project. The extra time invested now will replace the much more time required each time I need to change something.

Even small things can and should be prepared for the future and, if I have a keep-things-simple mindset, I don’t let myself fall into over-engineering. I don’t implement the future, I’m just ready for it. Are you?