Mission Impossible / How to create datatypes which cannot contain invalid state
Open Source Your Knowledge, Become a Contributor
Technology knowledge has to be shared and made accessible for free. Join the movement.
This example implements a serial port protocol which is request/response based. It assumes a that:
- the send and receive is long running
- the current status can be obtained for debugging reasons
- calls to send_request are synchronized by the caller
Example
1
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// {
class SerialPort {
public:
void connect();
std::string send_request(const std::string& request);
void print_state(std::ostream& os) const;
private:
mutable std::mutex guard_;
bool is_connected_ = false;
bool is_sending_ = false;
bool is_receiving_ = false;
};
void SerialPort::connect() {
long_running_task();
std::scoped_lock lock{guard_};
BOOST_ASSERT_MSG(not is_connected_, "port already connected yet");
is_connected_ = true;
}
std::string SerialPort::send_request(const std::string& request) {
{
std::scoped_lock lock{guard_};
BOOST_ASSERT_MSG(is_connected_, "port not connected yet");
BOOST_ASSERT_MSG(not is_sending_, "detected race");
BOOST_ASSERT_MSG(not is_receiving_, "detected race");
is_sending_ = true;
}
long_running_task();
{
std::scoped_lock lock{guard_};
BOOST_ASSERT_MSG(is_connected_, "detected race");
BOOST_ASSERT_MSG(is_sending_, "detected race");
BOOST_ASSERT_MSG(not is_receiving_, "detected race");
is_sending_ = false;
is_receiving_ = true;
}
long_running_task();
{
Enter to Rename, Shift+Enter to Preview
The problem here is, that we have three bool variables which would result in possible state combinations, of which only
is_connected | is_sending | is_receiving |
---|---|---|
false | ignored | ignored |
true | false | false |
true | false | true |
true | true | false |
This leads to the following results:
- A person which reads the code has to look up all the various asserts in order to understand the existing invariants
- A person which debuggs the code has to keep values of supposedly relevant variables in his head, which are actually not used
- It is very easy for a different programmer to change the code in such a way that the previous invariant is no longer given
Now try to refactor the SerialPort example to use an enum instead.
Open Source Your Knowledge: become a Contributor and help others learn. Create New Content