Lucky Resistor
Menu
  • Home
  • Learn
    • Learn C++
    • Product Photography for Electronics
      • Required Equipment and Software
    • Soldering for Show
  • Projects
  • Libraries
  • Applications
  • Shop
  • About
    • About Me
    • Contact
    • Stay Informed
  •  
Menu

Use Enum with More Class!

Posted on 2019-07-292022-09-04 by Lucky Resistor

You may be familiar with enum values, but do you know about enum classes?

This great feature was introduced with C++11 to solve several problems with the regular enum declaration.

Now I will explain the enum class declaration and demonstrate its practical uses. Although the examples I provide are intended for the Arduino Uno, the concept will work with any platform and with all modern C++ compilers.

A Display Interface

Let’s assume we create modular firmware code and create an interface for the display of our device.

#pragma once

namespace Display {

enum Backlight {
  Off,
  On
};

enum StatusLed {
  Off,
  Red,
  Yellow,
  Blue,
};

void setBacklight(Backlight backlight);

void setStatusLed(StatusLed statusLed);

}

If you compile this example, it will cause a failure and display this error message:

Display.hpp:11:3: error: redeclaration of 'Off'
   Off,

The regular enum declaration will put all of its enumerators into the same namespace as the declaration itself. The list of declared identifiers will look similar to this:

Display::Backlight      [enum-name]
Display::Off            [enumerator]
Display::On             [enumerator]
Display::StatusLed      [enum-name]
Display::Off            [enumerator] <== conflict!
Display::Red            [enumerator]
Display::Yellow         [enumerator]
Display::Blue           [enumerator]
Display::setBacklight   [function]
Display::setStatusLed   [function]

Because Off is already declared by the Backlight enum, its second declaration by StatusLed causes a name conflict.

Doubtful Solutions

Prefixes

A common “solution” used to avoid name conflicts is to put a prefix in front of each name:

enum Backlight {
  BacklightOff,
  BacklightOn
};

enum StatusLed {
  StatusLedOff,
  StatusLedRed,
  StatusLedYellow,
  StatusLedBlue,
};

Occasionally, you read code that uses an underscore to separate the prefix from the name.

#pragma once

namespace Display {

enum Backlight {
  Backlight_Off,
  Backlight_On
};

enum StatusLed {
  StatusLed_Off,
  StatusLed_Red,
  StatusLed_Yellow,
  StatusLed_Blue,
};

void setBacklight(Backlight backlight);

void setStatusLed(StatusLed statusLed);

}

This underscore is introduced because the author instinctively perceives a loss of readability.

While these solutions work perfectly fine, they introduce cumbersome, repetitive code, and repetitive code always indicates a design problem.

Artificial Namespaces

Another common solution is to put the enum declaration into another namespace:

#pragma once

namespace Display {

namespace Backlight {
enum Backlight {
  Off,
  On
};
}

namespace StatusLed {
enum StatusLed {
  Off,
  Red,
  Yellow,
  Blue,
};
}

void setBacklight(Backlight::Backlight backlight);

void setStatusLed(StatusLed::StatusLed statusLed);

}

The previous example uses a namespace declaration, which creates a very ugly duplication of the name in the interface. A more creative variant is to encapsulate the enum into its own class:

#pragma once

namespace Display {

class Backlight {
public:
  enum Enum {
    Off,
    On
  };
  Backlight(Enum value) : _value(value) {}
  // operators, getters, etc.
private:
  Enum _value;  
};

class StatusLed {
public:
  enum Enum {
    Off,
    Red,
    Yellow,
    Blue,
  };
  StatusLed(Enum value) : _value(value) {}
  // operators, getters, etc.
private:
  Enum _value;  
};

void setBacklight(Backlight backlight);

void setStatusLed(StatusLed statusLed);

}

Both of these solutions allow for clear and simple use of the interface, which is an improvement over the first ones.

#include "Display.hpp"

void setup() {
  Display::setBacklight(Display::Backlight::On);
  Display::setStatusLed(Display::StatusLed::Red);
}

void loop() {
}

Using an Enum Class

Enum classes work like a regular enum declaration, but put all enumerators into a new namespace:

#pragma once

namespace Display {

enum class Backlight {
  Off,
  On
};

enum class StatusLed {
  Off,
  Red,
  Yellow,
  Blue,
};

void setBacklight(Backlight backlight);

void setStatusLed(StatusLed statusLed);

}

Let’s now look at all identifiers declared in the previous example:

Display::Backlight           [enum-name]
Display::Backlight::Off      [enumerator]
Display::Backlight::On       [enumerator]
Display::StatusLed           [enum-name]
Display::StatusLed::Off      [enumerator]
Display::StatusLed::Red      [enumerator]
Display::StatusLed::Yellow   [enumerator]
Display::StatusLed::Blue     [enumerator]
Display::setBacklight        [function]
Display::setStatusLed        [function]

The declaration of the enum is simple and the two Off enumerators are separated in their own namespaces.

Syntax

Declaring an enum class is nearly identical to a regular enum declaration. The only difference is the class keyword after enum:

enum class [enum name] {
  [enumerator 1],
  [enumerator 2],
  [enumerator 3],
  ...
};

As is true for regular enum declarations, you can set an explicit base type and provide an initialiser for each enumerator:

enum class Speed : uint8_t {
  Slow = 0x10u,
  Medium = 0x40u,
  Fast = 0xa5u,
};

Usage

Enum classes are used in the exact same way as regular enum types. The only difference is the additional namespace you must put in front of the enumerator:

#include "Display.hpp"

void setup() {
  Display::setBacklight(Display::Backlight::On);
  Display::setStatusLed(Display::StatusLed::Red);
}

void loop() {
  Display::setStatusLed(Display::StatusLed::Blue);
  delay(200);  
  Display::setStatusLed(Display::StatusLed::Yellow);
  delay(200);  
  Display::setStatusLed(Display::StatusLed::Red);
  delay(200);  
}

This can lead to repetitive code with long identifier names, which is less than ideal. You can easily solve this problem using local using declarations:

#include "Display.hpp"

void setup() {
  using namespace Display;
  setBacklight(Backlight::On);
  setStatusLed(StatusLed::Red);
}

void loop() {
  using namespace Display;
  setStatusLed(StatusLed::Blue);
  delay(200);  
  setStatusLed(StatusLed::Yellow);
  delay(200);  
  setStatusLed(StatusLed::Red);
  delay(200);  
}

Here, the using namespace declaration is only working with an actual namespace, but not with a class.

Let’s assume we created a class-based Display interface as shown in the following example:

#pragma once

class Display
{
public:
  enum class Backlight {
    Off,
    On
  };
  
  enum class StatusLed {
    Off,
    Red,
    Yellow,
    Blue,
  };
  
  void setBacklight(Backlight backlight);
  void setStatusLed(StatusLed statusLed);
};

The using namespace will not work with the Display class. Instead, we can declare new names in our namespace for the enum types:

#include "Display.hpp"

using Backlight = Display::Backlight;
using StatusLed = Display::StatusLed;

Display gDisplay;

void setup() {
  gDisplay.setBacklight(Backlight::On);
  gDisplay.setStatusLed(StatusLed::Red);
}

void loop() {
  gDisplay.setStatusLed(StatusLed::Blue);
  delay(200);  
  gDisplay.setStatusLed(StatusLed::Yellow);
  delay(200);  
  gDisplay.setStatusLed(StatusLed::Red);
  delay(200);  
}

When Should I Use Enum Classes?

You should use an enum class if the additional namespace will improve the readability of your code.

  • The enum is used on its own in an interface, as demonstrated with the Display interface.
  • The enumerator names may be confusing out of context, like Off and On.
  • If you prefix each enumerator name, like Backlight_Off and Backlight_On.

If you already introduced a namespace, and the enumerator names are unique and make sense, you should use a regular enum declaration.

When deciding, always consider how your interface is used. Your goal must be to make this code clear and readable. 

Conclusion

Using enum classes will keep the names of enumerators simple and prevent name conflicts. The namespace in front of each enumerator associates the name with the enum type, increasing the readability of the code.

If you have questions, missed any information, or simply wish to provide feedback, simply add a comment below or ask a question on Twitter!

Learn More

While you wait for the next article, check out these:

C++ Templates for Embedded Code (Part 2)

C++ Templates for Embedded Code (Part 2)

Templates are a powerful feature of the C++ language, but their syntax can be complex. Here I will continue with the second part of the article. Although the examples I provide are for the Arduino ...
Read More
How and Why to Avoid Preprocessor Macros

How and Why to Avoid Preprocessor Macros

While most naming conflicts in C++ can be solved using namespaces, this is not true for preprocessor macros. Macros cannot be put into namespaces. If you try to declare a new class called Stream, but ...
Read More
Units of Measurements for Safe C++ Variables

Units of Measurements for Safe C++ Variables

In your program code, you often deal with unitless values. You can add the units of measurements in the variable or function name, or by adding a comment, but there is still the risk you ...
Read More
Auto and Structured Binding

Auto and Structured Binding

This article is just a short follow-up article for “Write Less Code using the ‘auto’ Keyword”. Structured binding is something handy, introduced in C++17. Therefore, only the latest compiler will support it. If you are ...
Read More
How to Deal with Badly Written Code

How to Deal with Badly Written Code

Sadly there is a ton of badly written code out in the wild. Hardware related code, seem to suffer more in this regards. I imagine, many developer in this segment are unwillingly to invest time ...
Read More
It's Time to Use #pragma once

It’s Time to Use #pragma once

In my opinion, preprocessor macros and the outdated #include mechanism are one of the worst parts of the C++ language. It is not just these things are causing a lot of problems, even more, it ...
Read More

9 thoughts on “Use Enum with More Class!”

  1. Zaar Hai says:
    2019-09-17 at 15:53

    Can enum classes somehow help with enum value checking? E.g. having IRKeys enum that defines the relevant subset of the IR codes (buttons) and then checking whether received value is relevant by some sort of dynamic casting? dynamic_cast() does not seem helpful in this case. Any other ideas? I’d hate to write switch() statement of 30+ keys…

    Reply
    1. Lucky Resistor says:
      2019-09-17 at 18:03

      Large switch statements can often be replaced with maps. For embedded code, keys can be checked sequentially. If each key will lead to a function call, just create a static map of function pointers.

      Reply
  2. Zaar Hai says:
    2019-09-19 at 07:07

    Can you elaborate on maps please? (I’m on Arduino and only could find map() function which does not seem to help here). By enums are not sequential – just a list of hex values that IR remote produces.

    Reply
    1. Lucky Resistor says:
      2019-09-19 at 15:57

      A map in its most basic form:

      enum class Button {
        A,
        B,
        None,
      };
      struct MapEntry {
        uint8_t code;
        Button button;
      };
      const MapEntry entryMap[] {
        {0x10, Button::A},
        {0xaf, Button::B},
        {0, Button::None}, // end mark.
      };

      Now, you just write a function which scans through this map and converts the code to the button enum. This is the smallest and probably most efficient solution to run on a microcontroller. Anything fancy like B-trees will just use a lot of RAM and only run slightly faster than a sequential search over ~60 entries like this.

      Reply
      1. Zaar Hai says:
        2019-09-29 at 14:41

        Thank you very much. I’ve successfully applied this approach and it made the code so much prettier and easier to read. Awesome! https://github.com/haizaar/doll-house-lights/blob/master/src/doll-house-lights/Rainbow.hpp#L57

        Though contrary to your HAL-common example [1] I couldn’t specify array verbatim when constructing a map – had to define array first and then pass its pointer. Though this way it’s be bit cleaner, since I decided to pass size as well and not count on special “last” element.

        Thanks again! It will be great to see a blog article on this pattern.

        [1] https://github.com/LuckyResistor/HAL-common/blob/master/EnumStringMap.hpp#L35

      2. Lucky Resistor says:
        2019-09-29 at 14:56

        Using an array as initializer depends on the C++ language version. I use C++17 or later in my build environment, while e.g. most Arduino platforms are still at C++11. I use the same approach using a separate pointer for readability as well.

  3. Zaar Hai says:
    2019-09-19 at 07:12

    Another question please – do you know a convenient way to convert enum class to String? AFAIK you can’t inherit enum class to add your own methods and writing separate conversion fucntion is doable for our enum user has to know about and call it.

    Reply
    1. Lucky Resistor says:
      2019-09-19 at 16:12

      The simplest and compactest way is, again, just creating a simple map with the enum values and the strings. You can create a generic wrapper around this kind of “maps” using a template class. Just have a look into my HAL library: https://github.com/LuckyResistor/HAL-common Search for the “EnumStringMap.hpp” file.

      If you like to add functions to an “enum”, enum class is not the correct solution. In this case, just write a regular class and embed am enum in this class.

      class Foo { public: enum Type { A, B }; Foo(Type value): _value(value) {} private: Type _value; };

      Now you can access the enum members like using an enum class: Foo::A. But now you have a class, and can add own conversion methods and operators to handle your special cases. You find one example for this principle in the Flags.hpp file in the HAL library.

      Reply
  4. jade says:
    2023-02-03 at 07:34

    This still an easy read, and a good article. Thanks for making it.

    Reply

Leave a Reply Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Stay Updated

Join me on Mastodon!

Top Posts & Pages

  • Storage Boxes System for 3D Print
  • Event-based Firmware (Part 1/2)
  • Build a 3D Printer Enclosure
  • Yet Another Filament Filter
  • Circle Pattern Generator
  • Circle Pattern Generator
  • Real Time Counter and Integer Overflow
  • Projects
  • Logic Gates Puzzle 11
  • Units of Measurements for Safe C++ Variables

Latest Posts

  • Better Bridging with Slicer Guides2023-02-04
  • Stronger 3D Printed Parts with Vertical Perimeter Linking2023-02-02
  • Logic Gates Puzzle 1012023-02-02
  • Candlelight Emulation – Complexity with Layering2023-02-01
  • Three Ways to Integrate LED Light Into the Modular Lantern2023-01-29
  • The 3D Printed Modular Lantern2023-01-17
  • Rail Grid Alternatives and More Interesting Updates2022-12-09
  • Large Update to the Circle Pattern Generator2022-11-10

Categories

  • 3D Printing
  • Build
  • Common
  • Fail
  • Fun
  • Learn
  • Projects
  • Puzzle
  • Recommendations
  • Request for Comments
  • Review
  • Software
Copyright (c)2022 by Lucky Resistor. All rights reserved.