[Patch] Implementation of n3672/<optional>
Luc Danton
lucdanton@free.fr
Tue Jul 30 06:46:00 GMT 2013
Hello everyone,
I've been working on a candidate implementation of N3672 "A proposal to
add a utility class to represent optional objects". Guidance, advice and
recommendations are welcome.
The code is available as a patch at
https://gist.github.com/mickk-/51706f9619c5daf8b72f
Note that as a new would-be contributor I haven't done any of the
legwork regarding copyright assignment. What follows are some of my
design choices and some pain points I encountered on the way. As not all
of the decisions I made were obvious and some involve tradeoffs, I
welcome all input to make the implementation more amenable to being
incorporated in libstdc++. Additionally it is my first attempt at
contributing so I may very well have messed up uglification, indentation
or other established conventions. I've attempted to be feature-complete
(e.g. regarding aspects such as constexpr, noexcept and
exception-safety) without going too much out of the way to provide
additional functionality, with one notable exception.
Namely I've used a technique that makes e.g. T =
std::optional<std::unique_ptr<int>> properly move-only, so that e.g.
static_assert( !std::is_copy_constructible<T>(), "" ); is not triggered.
A simpler implementation can instead make instantiating e.g. the copy
constructor generate an error when the underlying type isn't copy
constructible and leave it at that while still being compliant. In my
experience choosing the former option is nicer for generic code. (Due to
a possible brittleness in the implementation I made sure to write tests
that cover a lot of situations. This was an important concern as there
are upcoming changes in C++14 that GCC does not entirely implement yet
regarding deleted special members. I'm hoping that any breakage that
would be revealed would be a consequence of using 'exotic' types.)
I introduced a bits/enable_special_members.h header that contains the
implementation that makes this technique possible. It may be useful when
or if there is e.g. an std::variant which also aims to play nice with
traits/generic code. As it is boilerplate heavy I would especially
appreciate some eyeballs here for catching potential copy-and-paste
errors. I'm also worried about the number of instantiations that the use
of a unique tag type may lead to. I may have been overly cautious here
in an attempt to prevent ambiguous bases.
There is an unrelated wart that the entire implementation of all the
constructors and special members of std::optional is exactly duplicated
a second time. AFAICT there is no avoiding that to fulfill both
requirement of constexpr constructors and a trivial destructor where
applicable.
I hit a snag when trying to implement std::bad_optional_access. At first
I tried to mimic e.g. std::bad_function_call, by providing the exception
type in <optional> as mandated, adding __throw_bad_optional_access(const
char*) in bits/functexcept.h and putting the appropriate definitions in
src/c++11/functecept.cc. However I'm not sure this would quite work as
in its current form I've set up <optional> to only compile with
-std=c++1y. Given that there are ABI considerations anyway when it comes
to these matters I've put off the issue by 'implementing' both the
exception class and __throw_bad_optional_access(const char*) inline in
<optional>. std::optional is coded against __throw_bad_optional_access
anyway and thus moving the __throw... declaration and definition to
their correct place should be painless.
The no-throw, fail-bad accessors such as operator* and operator-> do not
currently do anything special in debug mode as I couldn't figure out how
to do that. Again, help in these matters would be welcome (keeping in
mind those operators have constexpr overloads although I assume that
preprocessor-oriented conditional compilation would do the trick).
All tests I have provided compile save for a handful. Half of those
don't pass because GCC rejects &a.b as a constant expression where b is
a variant member of a union-like class. Since it does not complain about
a.b I assume it's an incomplete feature and/or a bug. For the remaining
cases I could not figure out what to do regarding:
Remarks:
Instantiations of this function template for which less<T>{}(*x, *y) is
a core constant expression, shall be constexpr functions.
which governs the behaviour of e.g. o < p where o and p are optional
objects and the context is a constant expression.
More information about the Libstdc++
mailing list