Faking an Options Hash in C++
Consider a method that takes a number of optional parameters. Such a method is often a code smell, but there are times when it is the best of several ugly options.
As I’ve written about before, Ruby 2’s keyword arguments or options Hash pattern provide an affordance that solves this problem nicely.
Let’s say we’re building a simple API for generating HTML content. We
might have a method for creating a <div>
element. <div>
elements
can have a number of attributes, but they are all optional.
With Ruby keyword arguments, we might do this:
We can easily specify <div>
elements with only a subset of the
options, and we can specify them in any order.
In C++, the only affordance we have is to use positional parameters with default values.
This works. But the problem is that, in order to override a later option, we must also provide all earlier options which means knowing their default values. Also, we must provide the options in the order specified. And, when we look at a method call, we need to try to figure out what all of the arguments mean.
We can try to optimize the parameter order such that the most commonly overridden parameters come first, but there’s not really any good way to win.
A Better Way
I was recently faced with this issue in an API I was designing, and I came up with a different solution.
I introduced a DivOptions
struct with a
fluent interface.
I’m not fond of the trailing underscore naming convention here; I’d
try to come up with better instance variable names. I might also make
the instance variables private and add getter methods to access them.
This struct
is pretty localized and only used internally by the
div()
method, so I’m not too worried about that level of
encapsulation.
Here’s how to use DivOptions
:
It’s not quite as direct as the Ruby keyword version, but it has a lot of the same advantages. I only need to provide the arguments I want to override. I can provide them in any order. And the method names communicate which argument is which.
The one downside is that I now have to go look at the DivOptions
constructor to figure out what the default parameter values are; I
can’t see them in the declaration of the div()
method any more.
Next time you need to build a C++ API with a lot of optional parameters, consider using this pattern and let me know how it works out for you.