enumarray for better data arrays – EasyHack

In LibreOffice C++ code, there are many cases where developers have string literals that should be used in the code. If these are messages in the graphical user interface (GUI), they should be added to the translatable messages. But, there are many cases where the string literals has nothing to do with other languages, and they should be used as they are. In this case, enumarray is helpful. Although they are not limited to string literals, and can be used for any data.

Using Symbolic Constants

In old C code, using #define was the preferred way one could give a name to a string literal or other kinds of data. For example, consider this code:

const char[] FRAME_PROPNAME_ASCII_DISPATCHRECORDERSUPPLIER = "DispatchRecorderSupplier";
const char[] FRAME_PROPNAME_ASCII_ISHIDDEN = "IsHidden";
inline constexpr OUString FRAME_PROPNAME_ASCII_LAYOUTMANAGER = "LayoutManager";
const char[] FRAME_PROPNAME_ASCII_TITLE = "Title"_ustr;
const char[] FRAME_PROPNAME_ASCII_INDICATORINTERCEPTION = "IndicatorInterception";
const char[] FRAME_PROPNAME_ASCII_URL = "URL";

And also, the relevant states:

#define FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER 0
#define FRAME_PROPHANDLE_ISHIDDEN 1
#define FRAME_PROPHANDLE_LAYOUTMANAGER 2
#define FRAME_PROPHANDLE_TITLE 3
#define FRAME_PROPHANDLE_INDICATORINTERCEPTION 4
#define FRAME_PROPHANDLE_URL 5

Although this C code still works in C++, it is not the desired approach in modern C++.

Using enumarrays

In modern C++ code, you can use enumarry, which is defined in o3tl in LibreOffice. The above code becomes:

enum class FramePropNameASCII
{
    DispatcherRecorderSupplier,
    IsHidden,
    LayoutManager,
    Title,
    IndicatorInterception,
    URL,
    LAST=URL
};

And also, the string literal definitions:

constexpr o3tl::enumarray<FramePropNameASCII, OUString> FramePropName = {
    u"DispatchRecorderSupplier"_ustr,
    u"IsHidden"_ustr,
    u"LayoutManager"_ustr,
    u"Title"_ustr,
    u"IndicatorInterception"_ustr,
    u"URL"_ustr
};

Why an enumarray?

The names are much more readable, as they do not have to be ALL_CAPPS, as per convention for symbolic constants in C. Their usage is also quite easy. For example, one can use [] to access the relevant string literal:

- xPropSet->getPropertyValue( FRAME_PROPNAME_ASCII_LAYOUTMANAGER ) >>= xLayoutManager;
+ xPropSet->getPropertyValue( FramePropName[FramePropNameASCII::LayoutManager] ) >>= xLayoutManager;

Final Notes

In LibreOffice, enumarrays are not limited to string literals, and they can be used with other data. This task is filed as tdf#169155, and if you like, you may try finding some instances in the code and modernize it using enumarrays.

To learn more about LibreOffice development, you can refer to TDF Wiki. You may follow this blog to read about EasyHacks, tutorials and announcements related to LibreOffice development. Read the rest

Read More

enum class instead of unscoped enum – EasyHack

Since C++11 when enum class (also named scoped enum) is introduced, it is preferred to plain enum which is inherited from C programming languages. The task here is to convert the old enum instances to enum class.

Rationale

enum class has many benefits when compared to plain enum, as it provides better type safety among other things. Implicit conversion to integers, lack of ability to define the underlying data type and compatibility issues were some of the problems with plain enum that enum class solved in C++11. Although since then enum has improved and one can specify underlying type in the scoped enumerations.

Plain enums pollute namespace, and you have to pick names that are too long, and have to carry the context inside their names. For example: INETMSG_RFC822_BEGIN inside enum _ImplINetRFC822MessageHeaderState. With an enum class, it is simply written as HeaderState::BEGIN. When placed inside a file/class/namespace that makes it relevant, it is much easier to use: it is more readable, and causes no issues for other identifiers with possible similar names.,

See this change:

You can read more about that in:

Finding Instances

You may find some of the instances with:

$ git grep -w enum *.cxx *.hxx|grep -v "enum class"

When you count it with wc -l, it shows something more than 2k instances.

Examples Commits

You can see some of the previous conversions here, which is around 1k changes:

$ git log --oneline -i -E --grep="convert enum|scoped enum"

This is a good, but lengthy example of such a conversion:

Implementation

First of all, please choose good names for the new enum class and values. For example, you may convert APPLICATION_WINDOW_TITLE into Application::WindowTitle. Therefore, do not use the old names as they were.

Converting enum to enum class is not always straightforward. You should try to understand the code using the enum, and then try to replace it with enum class. You may need to add extra state/values for situations where 0 or -1 or some default value was used. There are cases where a numerical value is used for different conflicting purposes, and then you have to do some sort of conflict resolution to separate those cases.

You may end up modifying more and more files, and a few static_casts where they are absolutely necessary because you are interpreting some integer value read from input. These are the places where you should check the values yourself in the code. You have to make sure that the numerical value is appropriate before casting it to the enum class.

If you want to do bitwise operations, you should use o3tl::typed_flags, for example:

enum class FileViewFlags
{
    None = 0x00,
    MultiSelection = 0x02,
    ShowType = 0x04,
    ShowNone = 0x20,
};

template<> struct o3tl::typed_flags : o3tl::is_typed_flags<FileViewFlags, 0x26> {}

Then, you may use it like this:

    if (nFlags & FileViewFlags::MULTISELECTION)
        mxTreeView->set_selection_mode(SelectionMode::Multiple);

Please note that 0x26 is the mask, and is calculated by applying OR over all possible values. All the values must be non-negative.

Final Notes

This is a simple development task for LibreOffice also known as EasyHack, which is filed in Bugzilla as tdf#168771. These small tasks are defined to help newcomers to LibreOffice development community to improve their skills with LibreOffice coding.

You may find other instances related to C++ here:

Read the rest

Read More