zoo icon indicating copy to clipboard operation
zoo copied to clipboard

Create a template adaptor for optional semantics.

Open rambl30n opened this issue 2 years ago • 3 comments

std::optional introduces inefficiencies in the case where the space for the discriminator could be employed by the alternative. In other words, the data structure in question might already have an intrinsic discriminant that an application could use without using an additional field.

rambl30n avatar Feb 22 '23 02:02 rambl30n

I believe the phrase "the space for the discriminator could be employed by the alternative" intends to mean that a holder of an optional field may provide a discriminant.

thecppzoo avatar Feb 22 '23 23:02 thecppzoo

For this to be a template adapter, there are three choices:

  1. The optional keeps a reference to the holder (this reference may be a pointer or C++ reference), but then this pointer/reference space negates the benefit of discriminating in the holder
  2. The optional operates like boost::intrusive_ptr that defines an unqualified call API from the holder, however, this does not have the convenience of the optional managing destruction of the object (destructors have no arguments) hence it would not be a proper RAII design.
  3. The optional might require a helper class in the same way that boost::intrusive_ptr allows to satisfy the intrusive pointer API by using CRTP on the helper boost::intrusive_ref_counter. The big difference is that this helper would be required. This helper would have the discriminant for the optional. This option will be investigated.

It does not work to use CRTP directly (a holder inherits from an optional template on the type and the holder) since then managing the optional value would require using the derived class, which won't be yet-constructed on construction and would be already-destroyed on destruction of the base class (the optional). The other option of CRTP in which optional is the derived class does not work either: it would limit the design to classes that have at most one optional.

thecppzoo avatar Feb 22 '23 23:02 thecppzoo

There is a crude way to implement this: a "big" template that takes as parameter the "holder" (the object where you want both the optional and the discriminant), and tacks the extra field, the optional,

template<typename OptionalType, typename TypeToExtend>
struct Optional {
    TypeToExtend tte;
    AlignedStorageFor<OptionalType> storage_;
};

This can be made to work with some suitable API between Optional and TypeToExtend, but it is ugly the pattern of a template that uses composition in this way.

In general, what is wanted is that the meaning of the member "optional" is determined by sibling members. This is already problematic, let's say there is a member called optionalPresent as in

struct HasOptional {
    SomeTypeWithBooleanConversion optionalPresent;
    SomeWayToIndicateOptional optionalString;
};

Let's say there is a way, in the destructor of SomeWayToIndicateOptional for optionalString that it finds the way to reference member optionalPresent, so it can destroy a string if there is a string there. The problem is that then the code can change to this:

struct HasOptional {
    SomeWayToIndicateOptional optionalString;
    SomeTypeWithBooleanConversion optionalPresent;
};

with the order of the members reversed, then, when destroying optionalString, optionalPresent has already been destroyed.

Then, this suggests optionalPresent ought to be implemented as a base class, to guarantee the order, but then it would be intrusive, an ugly design.

It seems there is no good way to implement optional as an adaptor.

Additionally, the only way to be able to access the discriminant from the optional is by converting the address of the optional to the address of the holder, and I can only think of how to do this via the standard macro offsetof.

Thus, it seems the lesser evil is to capture these semantics of "these members control the meaning of those other members" via macros...

thecppzoo avatar Feb 23 '23 02:02 thecppzoo