Enum class. Why should you care?

The usage of enums is pretty common and it might seem that this language feature is well-known to everybody. And yet, despite the fact that the C++11 standard has almost 4 years, some people are stuck with old style enum instead of using the enum class version. In this article I will discuss the differences between C++98 enum and C++11 enum class and prove that the latter one is almost always better idea.

C++98 unscoped enum

C++98 style-enum is unscoped, which means that the names of enumerators are available in global scope. It results in global namespace pollution and the growing possibility of the name conflict. For example, consider an enum which defines the message type. See what happens if you define somewhere a variable called error. Will you be able to spot the bug in a big program?

enum MsgType
{
    request,
    ack,
    error
};

int process()
{
   int error = 0;
   /* ... */
   MsgType currentMsgType = getType();
   if (currentMsgType == error){ /* ... */}
}

To avoid such mistakes the names of enumerator should be more unique, e.g. begin with the enum type prefix, like MsgType_request. You could also wrap the enum with struct or class and refer to enum values with the class name specified. Other (and possibly better) option is using C++11 scoped enum.

C++11 scoped enum

enum class MsgType
{
    request,
    ack,
    error
};

Such enum is called scoped enum, because the names of enumerators are not known to global scope. To use them you need to specify the enum class name and use the scope resolution operator ::.

int process()
{
   int error = 0;
   /* ... */
   if (currentMsgType == MsgType::error){ /* ... */}
}

Now the risk of name conflict with enumerators reached zero level. The enum class name is required also by creation and initialization of enum values.

   MsgType currentMsgType = MsgType::request;
   MsgType previousMsgType(MsgType::ack);

This also allows you to create many enum classes with equal enumerators names. Such code is totally legal and safe — the names are not conflicting with each other since you use them with enum class name explicitly.

enum class MsgType
{ request, ack, error };
enum class ActionType
{ request, ack, error };
enum class Status
{ on, off, error };

Implicit conversions. Is it what you want?

There is also another big difference between C++98 enum and C++11 enum class: unscoped enums are implicitly convertible to integers, while the latter one are not. Code below (C++98 enum) would compile with no warnings, but does it have sense in all cases? Maybe you would like to have some compiler tip, that this part of code looks suspicious?

MsgType crtMsg = request;

double someResult = crtMsg/2;
double anotherResult = sin(crtMsg);
bool correctSize = (crtMsg < 126.5);

The same code, for C++11 enum class will not compile:

MsgType crtMsg = MsgType::request;

double someResult = crtMsg/2;
// error! no match for operator /
double anotherResult = sin(crtMsg);
// error! cannot convert MsgType to double
bool correctSize = (crtMsg < 126.5);
// error! no match for operator <

To compile the code you need to explicitly cast the enum to int with static_cast. That will allow you to spot some bugs at early stage.

Forward declaration of enum

The advantages of forward class declaration instead of including headers are widely known: the compile time is shorter, the translation unit is not so polluted with redundant symbols, the unnecessary recompilation of sources when the header changes can be avoided. If it so great, then why not use it for enums? Let’s try it with C++98.

enum MsgType; // forward declaration

class C
{
public:
    bool process(MsgType msg);
};

We added the forward declaration in header of the C class and the proper include in source file. Anyway, something is wrong — the code does not compile. The compiler says: error: use of enum 'MsgType' without previous declaration
enum MsgType;

It’s because the underlying type of enum is not known. The compiler needs to know how many bytes will be required to store the enum and it cannot be decided based on the forward declaration like above. It’s necessary to see the biggest value stored inside this enum. The presented enum MsgType could be stored on 1 byte — the biggest value to store (error) is equal to 2. However it’s totally up to compiler how many bytes will be used. My compiler decided to use 4 bytes for that.

But does it mean that the forward declaration of enum is not possible? Let’s try the same with enum class. Now it works! We just discovered another difference between C++98 enum and C++11 enum class. The underlying type of enum class is implicitly known by the compiler (it’s int by default).

However, I must mention that for C++98 enum the forward declaration is also possible. It’s only required to specify underlying type manually:

enum MsgType : int; // forward declaration

class C
{
public:
    bool process(MsgType msg);
};

In conclusion, the forward declaration is possible for both C++98 and C++11 enums, but for enum classes it’s a little bit easier.

Enum as a struct member

Another consequence of unknown underlying type appears when the C++98 enum should be a struct member. The struct size can be different when the code is compiled on various machines!

Summary

  • C++11 enum class does not pollute namespace like C++98 enums do,
  • C++11 enum class prevents implicit conversions which provides you more safety,
  • C++11 enum class size is defined (int by default),
  • C++11 enum class forward declaration is easier.

Data driven tests with QTest

Qt provides functional and convenient framework for automate tests. It’s easy to setup, since it does not require any third party libraries nor special configuration. It also contains special mechanisms for Qt specific testing, like signals and slots handling, anyway it’s also good for non-Qt C++ project.

Qt test Setup

The only one requirement is to have Qt installed.

Then, open your test .pro file and add:

QT += testlib

In the test .cpp file, include:

#include <QtTest>

That’s all. You are able now to write your QT test cases. Pretty simple!

Data driven tests

One very useful feature of Qt Tests is data driven testing. It allows you to create a test case with many various data sets without repeating the same code many times, or using loops.

Imagine you want to test a sorting function which takes as an argument the reference to vector of int and performs the sorting in place.

void sort(std::vector<int>& data);

Of course it’s necessary to test it with many inputs. You could start with writing following code:

class SelectionSort_test : public QObject
{
    Q_OBJECT

public:
    SelectionSort_test();

private Q_SLOTS:

    void sort_test();
};
void SelectionSort_test::sort_test()
{
    // C++11 extended initialization list
    vector<int> inputVector_1({5,8,9,2,0});
    vector<int> result_1({0,2,5,8,9});
    sort(inputVector_1);
    QCOMPARE(inputVector, result);

    vector<int> inputVector_2({0,1,0,1,0});
    vector<int> result_2({0,0,0,1,1});
    sort(inputVector_2);
    QCOMPARE(inputVector_2, result_2);

    vector<int> inputVector_3({9,8,7,6,5});
    vector<int> result_3({5,6,7,8,9});'
    sort(inputVector_3);
    QCOMPARE(inputVector_3, result_3);
}

It’s a bit repetitive and the code size grows very fast with adding new data sets. Besides, if you need to modify the test case, you need to do the change n times, what may lead to some mistakes.

That’s where the data driven testing should be used. To start with data driven testing, you need to define another private Q_SLOT with the name of the test function + “_data” postfix, like:

private Q_SLOTS:
    void sort_test();
    void sort_test_data();

The sort_test_data() method contains the data passed to sort_test() method. The body of sort_test_data() needs following elements: first, use QTest::addColumn to define the parts of your data set. Here we need input vector of type vector of int and the result also of type vector of int. Then use QTest::newRow to fill the data sets with data; each row is separate data set.

void SelectionSort_test::sort_test_data()
{
    QTest::addColumn<vector<int>>("inputVector");
    QTest::addColumn<vector<int>>("result");

    QTest::newRow("set 1")
            << vector<int>({5,8,9,2,0})
            << vector<int>({0,2,5,8,9});

    QTest::newRow("set 2")
            << std::vector<int>({0,1,0,1,0})
            << std::vector<int>({0,0,0,1,1});

    QTest::newRow("set 3")
            << std::vector<int>({9,8,7,6,5})
            << std::vector<int>({5,6,7,8,9});
}

After your data sets are prepared, you just need to load them into the test function and run the test. Use QFETCH macro to load the columns defined in sort_test_data() function. The syntax is:

QFETCH(data_type, column_name(no quotes) );

This macro will create local variables with names equal to column names. Note that inside QFETCH macro you shall not use quotes around the column name. If you try to fetch data from not-existing column (wrong column name), the test will assert.

void SelectionSort_test::sort_test()
{
    QFETCH(vector<int>, inputVector);
    QFETCH(vector<int>, result);

    sort(inputVector);
    QCOMPARE(inputVector, result);
}

This is how we created readable, easy to modify test with many various data sets. Above method will run QCOMPARE for every data set from sort_test_data function.

Custom data types

If you want to run the Qt data driven tests with the custom types, you can receive an error: Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt’s meta-object system. The solution is already given in the message. You just need to add this macro into the header of your custom class. The macro should be outside the namespace, just like presented in below CFoo example:

#ifndef CFOO_H
#define CFOO_H

#include <QMetaType>
#include <string>

namespace F
{
class CFoo
{
public:
// some declarations...
private:
    int m_number;
    std::string m_name;
};
}

Q_DECLARE_METATYPE(F::CFoo)
#endif // CFOO_H

Test results

The results exactly show which check with which data set failed or succeeded:

PASS   : SelectionSort_test::sort_test(set 1)
PASS   : SelectionSort_test::sort_test(set 2)
PASS   : SelectionSort_test::sort_test(set 3)
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted
********* Finished testing of SelectionSort_test *********

You can check out my github repository to take a look on the project with QT tests. The applied project structure was created with help of dragly.org post (projects in QtCreator with unit tests) and modified for my needs.

Binary literals

Introduced by: C++14

Until C++14, standard C++ allowed to define numbers in:

  • decimal notation:
    int number = 7;
  • hexadecimal notation:
    int number = 0x7;
  • octal notation:
    int number = 07;

Anyhow, without special compiler extensions it was not allowed to define numbers in binary format. It changes from C++14. The core language supports binary literals. It’s possible to define integer with 0b or 0B prefix to represent binary number.

int number = 0b0111;

It’s worth noticing that the GCC compiler has offered the possibility to define binary numbers since GCC 4.3.

It may be not the most crucial feature of C++ standard, but I believe there are cases when use of binary representation will improve readability of the code.

deprecated attribute

Introduced by: C++14

Attribute deprecated is a new feature of C++14 standard. It can be mostly useful for programmers who develop classes shared between various teams or projects.

When a method of some class becomes obsolete and this class is used only in its owners’ team, it can be just removed and all the occurrences of this function call can be replaced with new valid method calls. This operation becomes unrealizable when the function is a part of shared interface used by various teams. Deleting the obsolete method would be harmful in that case; we cannot know who, where and how often is calling that method and how long they would need to refactor their code to suit new interface. We want to allow them using the deprecated function.

Anyway we would like to tell them somehow that they use the obsolete function and it’s recommended to replace all the calls. And that’s the purpose of attribute deprecated in C++14.

Deprecated in short words

Calling the method marked as deprecated will result in compiler warning. It’s possible to provide custom warning message.

Example of usage

class CSharedInterface
{
public:
    [[deprecated]]
    void run();
};

Calling the method run() will result in compiler warning:
warning: ‘void CSharedInterface::run()’ is deprecated (declared at mainwindow.h:9) [-Wdeprecated-declarations]

Custom warning message can point to new recommended method.

    [[deprecated("use start instead")]]
    void run();

Then the compiler gives warning:

warning: ‘void CSharedInterface::run()’ is deprecated (declared at mainwindow.h:9): use start instead [-Wdeprecated-declarations]

Not only methods can be deprecated; it’s also possible to mark classes, variables, typedefs, enums and other entities.

Final specifier in C++11

Except of override specifier, which helps with detecting not-really-overriding methods, C++11 introduces also another specifier helpful for working with inheritance: final specifier. This one can be included to function declaration or a class definition when you want to make sure that this method will not be overriden or the class will not be used as a base class.

Continue reading

Override specifier

Introduced by: C++11

Overriding virtual functions is one of the most important mechanisms of C++ object oriented programming. The derived class method is overriding base class method when following requirements are met:

  1.  the base class method has virtual keyword,
  2. derived class method has:
    • the same name,
    • the same input parameters,
    • the same const-ness,
    • compatible return type.

If at least one of these requirement is not fulfilled, developer can end with not-overriding method and not even know about it. The C++11 standard introduces specifier override, designed for easy detection of such mistakes.

Continue reading