Search code examples
c++classschemacode-generation

Represent an optional attribute as a C++ class member


I am generating C++ code based on a schema. There are entities and each entity contain attribute(s) with each attribute having a corresponding data type. Now the problem is some of these attributes are "optional", meaning that they do not have to be part of the class declaration. However, in C++, something is either a member of class or not a member of class, there is no concept such as "optional data member".

Entities will be the class names, and attributes will be the class members. I am not sure how I can represent attributes marked as "optional" the existing C++ concepts.


Solution

  • The canonical answer is std::optional. It is a semantically accurate way to express a value that might or might not exist in a model.

    When creating such a model, for each optional field in a schema, you generate a corresponding std::optional-wrapped entry. Then, when deserializing, you can use std::none to mark a missing entry, and it during accessing, the client code must check for the existence of actual value1.

    If your objects are large, though, and you want to avoid storing the empty space unnecessarily2, the next alternative is std::unique_ptr. It has a downside of having pointer semantics, but otherwise remains a valid tool for such a case.

    If the members are very dynamic, e.g. the possible set is in dozens or hundreds, but typical utilization will see just a few, a key-value store (such as std::map) might be a better idea; then you're limited to store just one type of a value. This might be mitigated a bit with std::variant being used as the map value type, which gives you one of many possibilities, or std::any, which can effectively hold anything while losing type safety.

    The decision will ultimately depend on your exact model and usage characteristics.


    1 If the client code expects T, and the access for a field gives them optional<T>, the unwrapping step will/shall check for the existence of an actual value. This is the main reason optional is used.

    2 sizeof(optional<S>) will most commonly be sizeof(S) + 1, but can get bigger because of the alignment rules. Performance&Memory section of this article shows it nicely.