C++ Polymorphism with no inheritance

Different products but same messages

Working Wandbox-ed modern C++ code, is HERE.

C++ Polymorphism with no inheritance is by no means, somewhat advanced concept. But, this is just because it is (in C++) based on different concepts v.s. some other good and popular languages where it is in the foundations  of the language design.

Lessons from GO

For example in GO LANG polymorphism is definitely not tied to inheritance. And this is one very much popular and good programing language, indeed.

I have prepared “earlier” one (I hope) succinct and useful GO LANG example of interfaces, types and messages dancing together.

GO is simple and that is interactive online code. So do not be afraid and at least try it once. I have tried to comment it extensively.

GO LANG OO concepts are inheriting (pun intended) from SmallTalk. Not from C++ or Simula. GO interface, can be seen conceptually as a collection of messages.

After the above, GO structs (types) can implement the message “Start”. They do not inherit the interface in order to implement it, as one does in C++. In GO you implement the message (aka method) that GO interface exhibits.

 

There is no interface keyword in C++. Usually, C++ interface is Abstract Base Class (ABC). Admittedly (at least to me) GO LANG ABC’s are an unnecessarily complex subject and we better quickly jump back to standard C++ we know and love (well at least some of us).

Slight Detour

Subtyping is actually what C++ developers think of as polymorphism. Common base class as a parent to subtypes implementing the same interface of the said common base.

Parametric Polymorphism is what C++ developers call “generic programming”. In “typeless” languages (think JavaScript) that kind of polymorphism is actually transparent. It just happens. (some are calling this ‘delegation’ but I do not think that is the right name to use in this context)

some_object above is not any kind of base class. It is “just” an object. In JavaScript, an object is implemented as a collection (dynamic array) of functions.

[nextpage title=”The standard C++ way”]

By now, I think, we have (more than ) enough inspiration and pointers to the actual Architecture and Implementation of the modern C++ solution.

It might be the best course of action to imagine a code using a non-existent solution and then implement it. Let us use the “Modern Factory” we have actually (shamefully) implemented with the help of inheritance and a native pointer to its base.

The engine inside the Automobile is a simple native pointer, private to the Automobile class “facade”.

class Automobile {
     mutable IEngine * the_engine_ {} ;
// ... the rest goes here

Nice simple and usable. But, the wrong concept.

I do not like inheritance especially not in the production code. It delivers code that is sneakily becoming more and more difficult to change and maintain.  If you are curious about why there are “few” more reasons. For which to understand please do use YouTube or whatever you prefer.

Just make it a template

We need no abstract base classes, pointers and inheritance. C++ has all we need to avoid that.

The concept is: Engine is a concrete class which “has” or “implements” some particular method (the message).

Back to our Modern Car Factory, we see, rather unsurprisingly, to use the engine, we need a start() method.

Next, we make an Automobile into the template and give it an engine parameter.

Nice, simple and usable? Stop here. Before anybody gets lost, we need to know…

Where are we now?

Instead of a pointer to a polymorphic root (aka base) we have “just” a template. This is where we are, and this is where I want us to be.

We have replaced the pointer with the template argument.

The key: we have no inheritance hierarchy (of Engines).

Beside using templates, I have seen other extremely complex solutions to avoid inheritance.

Here, I am simply saying: Focus on the message you need to pass to the object. Implement the abstraction that has the messages, not the interface offspring that implements the interface with all the messages required now and coming or not in the future.  To do this one can use templates. Simple. But.

Every template instantiation delivers a new type. C++ is a statically typed language. That is: compiled types are made by compile-time template instantiations. Therefore.

Can we not compose types at runtime?

Thus we can not code a factory method, in the templates scenario, that assembles  Automobiles at runtime.

Why not? To implement the above. in the architecture I have just described, we would need to have statically built aka “pre-built” model (c++ type) for each combination customer might request. We would need to have an internal catalogue (a map maybe) matching each possible combination, of instances of existing types.

Ok then, let us imagine, our factory is allowing customers to (seemingly) mix and match “bases of the cars” with engines and wheels. Our internal catalogue that is now required might be similar to this standard c++ std::map:

The first problem above is one very static system, declared, defined and closed at compile time. For each new future combination, we would need to add code and recompile our car factory software system.

The second problem is we have no common type “automobile” as we have no inheritance. Thus we can not declare the internal catalogue aka c++ map.

That would be the only way to have an internal catalogue of all possible types.

Just a reminder:  in standard C++, Abstract types cannot be used as parameter types, as function return types, or as the type of an explicit conversion.

Here I do realize there is no point of pushing this template based paradigm further. I would need (again) an ABC to be able to “herd” all the current and future Automobile types.

Where do we go from here? What gives?[nextpage title=”Start again, think modern C++”] I am by now, obviously, a car factory architect, and also, I might be some modern C++ user with more or fewer skills at that.  Let me start with what I know.

I “need” to have a car factory that will allow mix-and-match service. Customers will “say” what they want to mix-and-match and the factory will actually need to mix-and-match parts to deliver the required combination.

Mix? Match?

What immediately springs to mind is a “mix-in pattern”. A beast among the patterns. A make or break for modern programming languages. Something that made “experts” to reconsider JavaScript as a language to appreciate.

Like this (rather good but complex) text about “Many talents of JavaScript“. Same concept but appropriate language.  Not C++. I already mentioned JavaScript on the top of this article. So, was that useful? Yes, at least to confirm we are on the right conceptual path.

So let us (again) code in modern C++ these parts we will need to mix-in to compose the automobiles.

From now on we are in the namespacecar_factory, so we will omit this from the code. Let’s start with the “spare parts”:

Our factory will let customers choose wheels and engines. By using the above tags customers will communicate what they want. The key point: At runtime.

We have just got two different automobile models straight and brand new from the assembly line.  From the same factory.  Without an internal catalogue of all possible models, pre-made and ready.

Next, we want to use the models made into automobiles.

I am sure you can appreciate this is a POC code. Thus it might look rather not real,  but it actually has to work as a real conceptual base for the real car factory software solution.

Back to work. Let us prepare our engines and wheels we know we will need.

Please ignore the modern C++ paraphernalia here, like,constexprfinal, etc.  This POC will work without them.  They are here just to make code later on, more malleable to not-a-poc coding of production quality, for you.[nextpage title=”Car Front”] Now the “hard part”. Thinking time :)  Consider this simple sentence: You sit in your car and you start it.

The Car Interface

How do you “start the car” as a person/driver/user who is not a car assembly expert? By using the “interface” to the car. What you see, sitting in the car, is just the “interface”. The keyhole, where you slot in your keys and start the car. Which activity, in turn, starts the engine that happens to be part of the car.

I do not want to use the term “interface” here, it is heavily overloaded, I will use the term “front”.

We need to have this base part of the car. Oops, yet another overloaded term: “base”. Rather:  The core component into which we will add the parts to assemble the car. The core component of the model that the customer requested. That key part will act as the “front” to the finished car. Remember no “interface” inheritance here. Just polymorphism. Only C++ methods as messages.

Now our car_front needs to understand the message “start”. And it needs to know what to do in order to serve the driver.

Now the car front has the ability to understand the “start()” message. Good. But why this “engine type”  argument? This is so that message is generic. This generic start() message will make the car_front in itself generic. Without making the car_front a template. Which we have shown before we do not want.

The Concept

Thus the concept is: the car_front is a collection of activities the car from our factory can do. With messages (methods), it understands so that the driver can request any of those activities. By driver passing the messages to it. In C++ terminology by executing the methods on this.struct

Perhaps the car_front can be called a “courier”.  (no, not a dispatcher, yet another overloaded term). An entity that has the vocabulary of messages it knows how to delegate. To the subordinates it fronts. Sometimes we recognize this as a “proxy” pattern.

Factory at last

Now many of you who have persevered bravely this far might be confused. Thus, let me thus quickly deliver the core code that makes the car assembly line.

Here, I have implemented the “factory method” as a generic “lambda mixer”. This allows me to deliver the runtime functionality where the customer can decide (yes at runtime) what model she wants and to receive not-a-template private (aka inner) class representing the finished car.

And this is our “holy grail”: a polymorphic solution, with no inheritance. All the cars from my factory have the same methods. But they do not inherit from the common base. No inheritance whatsoever.

As they say: If you know how to drive the diesel, you know how to drive petrol :)

Summary

I have presented a solution, that I might not call a “pattern”, but rather a mechanism. As a pattern,  we can describe it conceptually, but unlike a pattern, we can generate several blueprints and implementations, from this one concept.

However, the key advantage, (I have not done before in all of my C++ years) is the ability to create dynamically, at run-time,  an assembly.

Combination of parts, contained in one standard struct. That client obtains and uses it, to reach the functionality the assembly is a front of. Perhaps a concept very close to mix-in but broader in functionality and applicability. With the key advantage: no inheritance for sub-classing or for sub-typing. No smart pointers either.

To distinguish it from some other “mix-in” implementations, I am calling it “Lambda Mixer”(TM).  Maybe in a subconscious attempt to draw a parallel with a skilled cocktail master?  Who knows.

This is one long multi-page post already. In a very near future, I shall formalize this mechanism and will discuss it’s flexibility, applied to some other use cases.

Working Wandbox-ed modern C++ code is HERE.