Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Usage of std::optional vs. "unused/default" values

I am writing a class that represents a button. This button may or may not have various attributes like text written on it, shortcut, texture or flat color fill. So if for example, this button doesn't have any texture set, texture drawing process is skipped.

My first solution was to use made up default values that would say that the given attribute is unused (if alpha value of color was 0 the color fill drawing would be skipped etc.).

Other option I have is to use newly added std::optional which would be much clearer and simpler to use.

Here are the 2 mentioned examples:

class Button {
    void draw() {
        if (fill)
            drawRectangle(*fill);
        if (sprite)
            drawSprite(*sprite);
        if (font)
            drawText(name, *font);
    }

    std::optional<std::string> font;
    std::optional<std::string> sprite;
    std::optional<Color> fill;
}

class Button {
    void draw() {
        if (fill.alpha != 0)
            drawRectangle(fill);
        if (sprite != "")
            drawSprite(sprite);
        if (font != "")
            drawText(name, font);
    }

    std::string font;
    std::string sprite;
    Color fill;
}

What can the advantages and disadvatages of using std::optional be in this case? What I'm mainly interested in, are the memory usage and overhead differences.

Also should I just, instead of using if to check whether optional contains value, call value() and catch the exception?

like image 490
Maroš Beťko Avatar asked Oct 11 '25 14:10

Maroš Beťko


2 Answers

As far as overhead goes, it's mostly in the form of space. optional always takes up whatever it's storing, plus a boolean, plus any extra padding to make alignment work out. For example, std::string is often implemented as 24 bytes with 8 byte alignment. An optional<string> would then be 25 bytes, but because of alignment it will end up being 32 bytes. For a primitive type (int, or an enum), then it will typically double the storage needed from 4 to 8 bytes, or something similar.

As far as performance goes, in this case there's going to be no difference outside cache effects (if the optimizer is smart). Comparing a std::string to an empty string literal will probably get optimized to a call to std::string::empty (you should probably write it that way), which is just checking if an integer is zero, which is the same as your comparison for Color, which is basically the same as checking whether a boolean is zero.

That said I do like optional; I think it more clearly communicates the intent of the code. However if you have very obvious sentinel values then maybe it's less valuable.

In some cases you can have your cake and eat it too with compact optional: https://github.com/akrzemi1/compact_optional. It has identical external interface to a regular optional, but you give it a sentinel value and it uses that sentinel to store the missing state. May not work well with all classes though.

like image 71
Nir Friedman Avatar answered Oct 14 '25 05:10

Nir Friedman


Although std::optional may incur the overhead of a extra boolean value, it also carries a descriptive purpose here: it fully expresses the concept you are trying to put into code, which makes that this code directly shows the reader what is going on. Because this is UI, and the boolean overhead is relatively small, I'd say go for it.

I contest the statement that std::optional was meant only for function returns. That would be ridiculously limited.

like image 35
rubenvb Avatar answered Oct 14 '25 05:10

rubenvb