Writing Interfaces: Stay true in booleans

Radek Vít
3 min readMar 10, 2021

--

When faced with passing multiple options as arguments to functions, C++ programmers will use enum class without much hesitation. Since C+11, it feels like we have moved away from magic numbers and magic string values, and in most cases, it’s for the best. There seems to be one exception: when we only have two options to choose from.

Here, I will try to convince you to stop overusing bools that don’t convey true/false or yes/no.

Rule of thumb

For making interfaces safer, more expressive, and nicer to use, I will advocate for the following rule of thumb:

If you read out the function call with its arguments, bool arguments and return values must be able to be substituted with true/false or yes/no.

When bools are appropriate

For example: the C++ function void MyClass::set_done(bool done); fits this perfectly. If we call foo.set_done(true);, we can read this as “Set done to true”. In the same way, calling bool MyClass::is_done() const; with foo.is_done() can be read out as “Is it done? Yes/no.”.

When bools are not appropriate

We often use bools in cases where the argument or answer cannot be yes or no. Using enums instead will provide clarity at the call site, without us having to look up definitions of the arguments.

struct BadWindow {
void show_window(bool showInForeground);
};
enum class WindowOpenMode {
Foreground,
Background,
};
struct GoodWindow {
void show_window(WindowOpenMode mode);
};
int main() {
BadWindow badWindow;
GoodWindow goodWindow;
// show window yes?
badWindow.show_window(true);
// show window in the foreground!
goodWindow.show_window(WindowOpenMode::Foreground);
}

Even though we only have one argument for this function, using enums has already made the call sites much clearer. Imagine how much of a difference this approach will make for functions with multiple binary choices!

// this
do_stuff(true, false, false);
// can turn into this
do_stuff(SuccessAction::Report,
FailureAction::None,
Pants::None);

Preventing bugs by dropping bools

I think I have demonstrated how using enums can make our interfaces nicer to use and more expressive, but I have yet to show how they are safer.

In C++, argument names in declarations don’t have to match the definition. When using bools, it’s possible to make a mistake (or forget to flip the declaration when refactoring). Then, if your caller only looks at your header file, he may use the other bool value than they may intend.

// show_window.h
void show_window(bool showBackground);
// show_window.cpp
// which of these two should it be, anyways?
void show_window(bool showForeground) {
// ...
}

This mistake may not be particurarly common, but it’s possible to get this wrong by accident. By switching to an enum class argument here, this mistake becomes impossible to make:

// show_window.h
enum class WindowOpenMode {
Foreground,
Background
};
void show_window(WindowOpenMode mode);
// show_window.cpp
// the argument now always means the same thing
void show_window(WindowOpenMode mode) {
// ...
}

Final word

We programmers are often lazy, so booleans come as an easy substitute for defining what we actually want to convey in arguments and return values. But we usually spend most of our time reading and using code, not writing it. With just a little more effort, we can make reading our code a better experience for everyone.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response