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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With