Re: What's your experience with optional values?
On Dec 27, 1:47 am, =D6=F6 Tiib <oot...@hot.ee> wrote:
On Dec 26, 10:17 pm, DeMarcus <use_my_alias_h...@hotmail.com> wrote:
In databases you provide NULL if the value is missing. When
using objects on the free store in C++ you can use NULL to
represent a missing value. When using non-pointer values you
may not.
I know several ways to solve this, but what I'm looking for
is a /uniform/ way to deal with missing values both for
non-pointer values and free store values.
What sort of uniformity you want? There is difference when
something is empty, unknown, failed/invalid (as fallible) or
missing (as optional).
This is a naming problem. The traditional name for the idiom
implemented in boost::optional is Fallible: although the
implementations are more or less identical, the names imply
a completely different use. In data base type uses, Nullable
would be the preferred name. The best name I've seen to date is
Maybe, which seems fairly neutral with regards to why the value
might not be there. (Historically, Fallible was more or less
universal before someone at Boost decided otherwise. And I've
used my "Fallible" for things like cached values, where neither
Fallible nor Optional really correspond to the use case.)
When dealing with non-pointer values I can do the following.
boost::optional<int> getMyInt();
It is member function declaration? It may result with
get-and-get- getted idiom:
int intFromT = t.getMyInt().get();
Or more likely:
int intFromT = t.getMyInt().elseDefaultTo(someDefault);
(I'm not familiar with boost::optional, so I don't know what
name they actually use for "elseDefaultTo", but some such
function should definitely exist.) Otherwise, you save the
optional, and verify its validity before trying to access its
value.
When dealing with free store values I can do the following.
std::unique_ptr<int> getMyInt();
Or just int*. (int* is, of course, the idiomatic equivalent of
optional<int>.)
If the object actually is on the free store, and the getter is
actually a factory function, then you can consider auto_ptr<int>
(except, of course, that this would never be the case for int).
However, I would like to have something uniform for my whole
application. Something like.
my::opt<int> getMyInt();
my::opt<int*> getMyInt();
my::opt<std::unique_ptr<int>> getMyInt();
where my::opt works like boost::optional for non-pointer
values and as a transparent container if a pointer (to avoid
the unnecessary check for the object since we already can
check if the pointer is NULL).
With some hard work I could probably come up with some template
solution, but my question is:
* Does this look intuitive to you? Would this uniformity add to the
understanding and consistency of the code?
This feels over-engineered. Lot of classes have invalid or exceptional
states anyway in them and it is more likely that they do right thing
in context. So there is a danger that by making them all uniform you
lose valuable context information (is it empty, unknown, not
available, failed, invalid or missing).
I could also just use std::unique_ptr everywhere I need optional values=
,
No you couldn't. std::unique_ptr has very specific semantics,
which often don't correspond to what you want. In cases where
you are actually returning an lvalue (if the object exists),
e.g. something like AssocArray<T>::get(key), returning a pointer
is the idiomatic equivalent of optional<T&>/Fallible<T&> etc.
(Implementing such a class so that it works for references is
over-engineering.)
but with big structures of data it would be a performance hit to
allocate memory for each non-pointer member.
The simplest solution would be to just go for
boost::optional when using non-pointer types and
std::unique_ptr for pointer types, but I thought it could be
a value having a uniform name. Do you agree with me?
When i want uniform semantics for different types then i write
a family of function overloads. Something like bool IsMissing(
T const& ) that expresses such state.
Good idea. In general. Most of the time, it's
over-engineering, but from time to time, it's justified.
Note that both the pointer idiom and data base use would suggest
something along the lines of == NULL or != NULL. I've never had
to go that route systematically, but the possibility should also
be considered.
--
James Kanze