Bad start
Regard classes with start and stop methods with suspicion
Init method in a sheep’s clothing
1
2
3
4
5
6
7
8
9
10
class some_class {
public:
void start() {
// ...
}
void stop() {
// ...
}
};
Innocent classes with start
and stop
methods are looking for trouble. They
are just aliases for classes with init
and cleanup
methods. Or they came
with different names (setup
and teardown
are popular with test frameworks)
They raise questions like:
- how does the user of the class ensure
stop
is always called? - how do the constructor and
start
divide the responsibility to initialize? - if
start
fails (e.g. throws), is thenstop
also called? - what happens if
stop
throws? - how do the destructor and
stop
divide the responsibility to cleanup?
Some people think they have obvious answers to the above questions, but the more you dig, the less palatable the outcomes.
To pick just the first question, here is a potential (bad) answer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void some_fn() {
some_class a;
bool need_to_stop = true;
try
{
a.start();
// ...
need_to_stop = true;
a.stop();
}
catch(...)
{
if (need_to_stop) {
a.stop();
}
}
}
Compare that with using constructor/destructor (even with a complex initialization):
1
2
3
4
void some_fn() {
some_class a = complex_initialization_fn();
// ...
}
So what’s the solution?
Don’t use classes with start
/stop
methods.
But I have to use a start method
Say you’re dealing with legacy code e.g. using a test framework with setup
and teardown
. Then the solution of containment might work. See
here for how to bridge to using the standard C++ lifetime
workflow.
But I really have to use a start method
For example to fix this threading bug. There we have base
class periodic_thread
that might call a method of the derived class
on_fire
, from a different thread, before the derived class
some_periodic_thread
virtual table was initialized.
The naive solution is to defer starting the thread in a start
method for the
periodic_thread
class. In addition to the problems mentioned above, this
approach spreads. The derived class some_periodic_thread
needs to have a
start
method, and it’s usage in a function is more complex.
Whenever you use the class you need additional work whenever it is used, either directly in a function, or when aggregated either as a member or as a base class.
For an alternative, look at the std::thread
class. It does not have
start
/stop
. Instead, it receive the things it needs (e.g. the function to
call), in the constructor. That means that natural usage requires that the
scope of the function to call from the thread will extend the life of the
std::thread
instance.