C++ requires you to think after a copy/paste

Well yes, I understand. Starting to understand C++ and then be confident requires years. And good code and advice on that path are often priceless. A lot of copy paste is still inside some of my projects, too.

But you also know that without proper C++ education and sound grasp of key concepts, copy paste will not take you very far. You are capable of abstract thinking and you can deliver solid C++ code. So let me show you how applying this kind of thinking can produce much better C++ while refactoring your daily copy/paste from Stack Overflow.

So here we go.

As a random starting point, we shall take this article from good old Wikipedia. You have decided to design your C++ code before coding and that is commendable. You have decided, based perhaps on some Stack Overflow comment by someone, to investigate this “Policy Based Design”.  The article is pretty clean and simple and it also contains an obligatory C++ code example. Ready to be slurped up and incorporated into your design.

For the purpose of this post, I will focus only on the HelloWorld class as presented in there, and take you trough thinking+refactoring steps to considerably improve the result. In both clarity and usability of the current last release in here.

Ok, this is the original code.

The article explains the rest. We focus on the code above. What I have noticed and disliked first is the API this produces. It requires users to compose the class from other classes, before being able to use anything. And this is even less logical when we realise that default language is English and that it is very often just used as it is, without switching to other languages. So let us “design in”, this realisation we have just made.

While “at it”we have also realised that UNICODE is what anybody will want most od the time so we can safely introduce WideWriterPolicy as our default one.  So here we are. the first step on our “think and refactor trip” has produced better API and thus improved the comfort of its users. Making them less error prone.

Usage pattern, compared to the one in the article. looks obviously simpler. And better at that.

A deeper dive

That was a bit of a thinking applied and a bit of a basic C++ templates knowledge used. But we can do much better than that. Going further, I have to say I do not like multiple inheritances at all. “Diamond problem” and the such.  No.  So why do we have it here?

I might be so bold to advise you to do a short detour to my post on why the class inheritance is bad.  These dayas templates are giving brittle composability of the solution and that is a good thing. We compose the solution out of parts and make it obvious to the user, in the code. Inheritance is not required for that. Inheritance makes the inheritors bloated. It gives them a lot of stuff they do not need. In this case, a classHelloWorld will inherit whatever is in the base policy classes. And it uses just one method from each of them. And they might grow and get heavy and complex. Making the life difficult for the classHelloWorld, in essence, this might stay as it is.

And then this using declarations. Yet another C++ artefact from the past. These are so-called “access declaration”. Deprecated in modern C++, so the author has added the “using” keyword. , in essence, fighting with inheritance.  I think you are by now enough of the theory. Here is the next improvement.

Inheritance is removed. And suddenly it is wondrously obvious how unnecessary it was. Template arguments are completely and simply replacing the base classes. Since there is no inheritance we do not need member access declarations to enforce the access to the required methods from the base classes.

It is perhaps not your favourite bed-time reading but do some casual stroll through standard C++ library code. It is 99% this kind of design. Templates, default template arguments, and no inheritance. Alex Stepanov was really ahead of it’s time. And still is.

But wait! What are these two static’s inside the run() method?!

Always limit the class members

Eh? What is this C++ “salto-mortale” now; in here? Ok, let us go step by step through this last improvement. What are the “class members”? Without ISO terminology and language escapades, I would propose a simple definition:

Class member is anything that is not a class method

And what this has to do with that two statics above inside the method run? What other ways of using methods of  Language Policy and Output Policy we have?

Well, perhaps the “default” C++ idiom, that springs to mind, might be:

Two “normal” class members. Nicely made private and then simply used from inside run(). But there are problems with this design.

  • HelloWorld class size has grown
    • It got two more members.
    • Each instance of that class will be that much bigger in the applications using it.
  • Proper class members treatment
    • Class moving semantics have to contain code taking care of this two members also
      • Are both members moveable? Swappable?
    • Class copying has to contain code, taking care of this two members too
      • Are they both copyable?

Ok, says you, I can make them both static. Each instance of the HelloWorld class does not need her own policy instances, after all. That approach creates two instances per class, not per object.  Ok, that is better, but still, it will make the final app bigger. Many different classes can use these two policy classes. And not every one of them will be used at runtime.  And yes, these two policy classes will have to be resilient in the presence of multiple threads.

You are thinking while refactoring and you propose “a clever solution”:

By using the default arguments feature you instantiate both policy class upon caller calling the run()  method. Voila: No class members! My comment: this makes instances of two classes upon every call of the run() method.  This can slow down the code considerably, it all depends on the time and resources required to instantiate the policy classes. No members true, but different problems introduced.

Instead, I have designed my code to do nothing until the run() method is called.

There are no new class members, and class copying and moving and constructing and destructing, stay the same as before. At runtime upon the first call, inside a run method, two static instances will be made. On each subsequent call, they will be just used. This is a good thing and this is called “lazy instantiation”. Do not make if not used.

Conclusion

The code above is by no means “final” solution. I have kept it deliberately at the safe distance from (too much) of C++14, C++17 and standard library abstractions. Yet we have learned a lot, and we have some useful code. No to copy paste but to re-use in the present form.

Of course, do not forget to add error checking before you publish it 🙂

Leave a Reply