Donnacha Forde

Donnacha on GitHub (Blogs & Posts, Lectures and Code)

home/blogs+posts/software-engineering/object-construction

Object Construction

The thought process around object construction when using the espresso library constructs.

Introduction

In this blog, I explore construction patterns for initializing the library. As described in design influences and considerations, my goal was to provide a way to initialize the library for writing to stdout without excluding alternative rendering.

Influences

When thinking about design, I find it helps to have a conversation with yourself to try to identify ‘baggage’ - what pre-conceptions and preferences you’re bringing to the table on day-one.

Design Evolution

Taking the programmer’s experience of the library into consideration, I wanted a clear way to model and capture command-line argument info, as follows:

Args args(...);

args.add("option1");
args.add("option2");
args.add("option3");

args.addAlias("option1", 'o');

Next, I wanted the user to declare a manager object that, under the covers, defined the relationship between the args and the arg-renderer. Early on, I let the user define the Argument-Manager directly but once I had incorporated the ability to render the args in more ways than stdout, a factory made more sense.

Originally, the code would have had to be declared as follows:

StdoutArgRenderer stdoutArgRenderer;
ArgMgr argMgr(stdoutArgRenderer);

The idea being that if a developer wanted to show command-line argument info in a dialog box, they could define their own render and initialize as follows:

WindowsArgRenderer windowsArgRenderer;
ArgMgr argMgr(windowsArgRenderer);

This was possible because I had defined an interface for arg-rendering - i.e. IArgRenderer to allow the user to provide their own renderer. The ArgMgr class holds a reference of type IArgRenderer and in turn, the object delegates all print output to this instance. So, the trick is simply to provide the ArgMgr instance with a valid ArgRenderer.

However, I really wanted this to encapsulate this detail for the vast majority of users so I decided to employ a Factory that would do this simple initialization for me.

ArgManager argMgr = ArgManagerFactory::createInstance();

Under the covers, an early attempt took this approach:

ArgMgr ArgManagerFactory::createInstance()
{
	StdoutArgRenderer stdoutArgRenderer;
	return createInstance(stdoutArgRenderer);
}

ArgMgr ArgManagerFactory::createInstance(IArgRenderer& argRenderer)
{
	ArgMgr argMgr(argRenderer);
	return argMgr;
} The mechanics of this approach meant that I had to provide a copy-constructor implementation for ArgMgr, though this was trivial. You can see that the cleanest implementation for the factory methods involved passing back a copy of the object rather than a pointer or reference (thus avoiding any messy responsibilities for deletion). 

Of course, this approach didn’t work.

Even though this succeeded in providing the ArgMg object with a valid reference to a renderer object, declaring the stdoutArgRenderer on the heap meant that it got destructed when the factory method returned. The valid reference referenced invalid memory causing an Access Violation.

Incidentally, things work when the user explicitly declares the ArgRenderer (as above) because it will still exist on the heap.

My first reaction was that, despite my best efforts, I was going to have to rely on the renderer object being allocated on the heap. I couldn’t utilize concrete objects and rely on dynamic types at runtime. The ArgMgr class couldn’t know the concrete type of the object it was interacting with here. It had to use a pointer or reference in order for that dynamic relationship. In this instance, it’s clear that the GC approach deployed in Java provides a greater degree of freedom in facilitating Dynamic Injection (DI) and the Inversion of Control (IoC) pattern.

Frustrated that I was going to be forced to rewrite the code to use dynamically allocated objects (on the heap) and annoyed with all that entailed (See: Item 27 in More Effective C++ by Scott Meyers), I just took a break from it…

Eureka

I’ve read several books on engineering productivity and came across many references describing how people had a light-bulb moment while away from the keyboard. That’s exactly what happened: it occurred to me that I could use the static keyword to declare the stdout renderer.

Because of the way the language has evolved, C++ reuses keywords so, as of the time of writing, static had 3 distinct uses. But, when used within a function or method, it meant that instance was only created once and never destroyed.

ArgMgr ArgManagerFactory::createInstance()
{
	static StdoutArgRenderer stdoutArgRenderer;
	return createInstance(stdoutArgRenderer);
}

Simply declaring the stdoutArgRenderer as static meant the object effectively behaved as though it’d been provided by the user, further up the stack.

Great! This worked a treat and meant that I was now able to meet my fussy design goals. The only minor annoyance being that I didn’t think of it sooner… :-)


Donnacha Forde

May 2021

linkedIn.com/in/donnachaforde