Skip to content

Commit

Permalink
patterns: document the bool Init pattern
Browse files Browse the repository at this point in the history
While people probably shouldn't add new uses of this pattern much, it
appears in a lot of places in the codebase so it's worth writing up.

Change-Id: Ia21a8597f2aadfe9a202fb6058fc856a2439f6e9
Bug: None
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3642419
Commit-Queue: Elly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1002326}
  • Loading branch information
Elly Fong-Jones authored and Chromium LUCI CQ committed May 11, 2022
1 parent 25d5210 commit d222d67
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
58 changes: 58 additions & 0 deletions docs/patterns/bool-init.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# The Bool Init Pattern

The `bool Init()` pattern allows a class to have initialization behavior which
can fail. Since Chromium C++ doesn't allow exceptions, ordinarily constructors
aren't allowed to fail except by crashing the entire program.

In practice, this pattern looks like this:

class C {
public:
C();

// nodiscard is best practice here, to ensure callers don't ignore
// the failure value. It is otherwise VERY easy to accidentally ignore
// an Init failure.
[[nodiscard]] bool Init();
};

and then client classes need to do something like this:

auto c = std::make_unique<C>(...);
if (!c->Init())
return WELL_THAT_DIDNT_GO_SO_WELL;

## When To Use This Pattern

Probably don't. The factory pattern or unconditional initialization are
alternatives that don't require client classes to remember to call `Init()`
every time they use your class.

This pattern is often used internally as part of a class, but having a public
`Init` method that clients of your class are expected to call is error-prone -
it is too easy for client classes to forget to call it, and detecting that error
requires runtime checking inside your class. As such, you should not add a
public `Init` method. It is also important for *subclass* clients to remember to
call `Init`, which adds yet another source of error.

However, this pattern is sometimes used internally as part of the factory
pattern, in which case the factory often looks like this:

// static
std::unique_ptr<C> C::Make(...) {
auto c = std::make_unique<C>(...);
if (!c->Init())
c.reset();
return c;
}

That particular use, where there is a single `Init` call site and clients cannot
otherwise acquire an uninitialized instance of `C`, is much safer but the risks
involved in subclassing `C` still apply.

## Alternatives / See also:

* The [builder](builder-and-parameter-bundle.md) pattern
* Unconditional initialization (arrange your object so that initializing it
cannot fail)
* RAII in general
3 changes: 2 additions & 1 deletion docs/patterns/builder-and-parameter-bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ which lets us avoid naming the temporary builder object.
which should not be changed once the object is "in use"
* Your class has invalid sets of configuration options that should be prohibited
* Your class has complicated configuration options of any sort that want runtime
checking (places you might have previously used the `bool Init()` pattern)
checking (places you might have previously used the [bool Init()
pattern](bool-init.md))

## Alternatives / See Also

Expand Down

0 comments on commit d222d67

Please sign in to comment.