#102 – Two simple, practical uses for enums in C++

Enumerations are used to store a bundle of information. They have numerous applications. This Nibble will demonstrate 2 such applications.

  1. Returning status codes
  2. Implementing a simple Finite State Machine (FSM)

1. Returning status codes

Suppose a function validates a password.

A weak version might return integers:

int validatePassword(std::string_view password){
    if (password.empty()) {
        return -1;
    }

    if (password.length() < 8) {
        return -2;
    }

    if (password == "password123") {
        return -3;
    }
}

This technically works, but the return values are cryptic. What does it mean to return -2?

A caller must remember what each number means:

-1 // empty?
-2 // too short?
-3 // too obvious?
0  // success?

That is fragile. The code forces the reader to translate numbers into meaning.

An enum makes the result explicit:

#include <iostream>
#include <string_view>

enum class PasswordResult {
    Success,
    Empty,
    TooShort,
    TooCommon
};

PasswordResult validatePassword(std::string_view password){
    if (password.empty()) {
        return PasswordResult::Empty;
    }

    if (password.length() < 8) {
        return PasswordResult::TooShort;
    }

    if (password == "password123") {
        return PasswordResult::TooCommon;
    }

    return PasswordResult::Success;
}

void printResult(PasswordResult result){
    switch (result) {
        case PasswordResult::Success:
            std::cout << "Password accepted\\n";
            break;

        case PasswordResult::Empty:
            std::cout << "Password cannot be empty\\n";
            break;

        case PasswordResult::TooShort:
            std::cout << "Password is too short\\n";
            break;

        case PasswordResult::TooCommon:
            std::cout << "Password is too common\\n";
            break;
    }
}

int main(){
    printResult(validatePassword(""));
    printResult(validatePassword("wolf"));
    printResult(validatePassword("password123"));
    printResult(validatePassword("StrongerPassphrase"));

}

// Terminal output:
Password cannot be empty
Password is too short
Password is too common
Password accepted

2. A simple FSM

When enums are paired with a switch/case block they produce a simple FSM, where the states are stored in the enum.

Let’s simulate a washing machine!

#include <iostream>

enum class WasherState {
    Idle,
    Filling,
    Washing,
    Draining,
    Finished
};

WasherState nextState(WasherState state){
    switch (state) {
        case WasherState::Idle:
            return WasherState::Filling;

        case WasherState::Filling:
            return WasherState::Washing;

        case WasherState::Washing:
            return WasherState::Draining;

        case WasherState::Draining:
            return WasherState::Finished;

        case WasherState::Finished:
            return WasherState::Finished;
    }

    return WasherState::Finished;
}

void printState(WasherState state){
    switch (state) {
        case WasherState::Idle:
            std::cout << "Idle\\n";
            break;

        case WasherState::Filling:
            std::cout << "Filling\\n";
            break;

        case WasherState::Washing:
            std::cout << "Washing\\n";
            break;

        case WasherState::Draining:
            std::cout << "Draining\\n";
            break;

        case WasherState::Finished:
            std::cout << "Finished\\n";
            break;
    }
}

int main(){
    WasherState state { WasherState::Idle };

    for (int step { 0 }; step < 5; ++step) {
        printState(state);
        state = nextState(state);
    }

}

// Terminal output:
Idle
Filling
Washing
Draining
Finished