PrevUpHomeNext

boost::optional<...>

Its mapper class: optional_mapper<...>

If T is any mapped type, then boost::optional<T> is a mapped type. Furthermore, it is statically mapped: its mapper class is always optional_mapper<T>, and the compiler knows that when it is compiling your code.

In addition to mapping boost::optional<T>s to and from column formats, optional_mapper<T> provides an is_initialized() method, operators -> and *, and a get_value_or() method, which act as analogs of familiar features boost::optional<T>.

If omt is an instance of optional_mapper<T>, then:

Just as it is a run-time error to execute a normal C++ expression that applies * or -> to an uninitialized boost::optional, it is a run-time error to execute a server-side expression that applies * or -> to data that represents an uninitialized boost::optional.

Example

struct movie_info {
    std::string title;
    boost::optional<std::string> producer;
    std::string director;
};
QUINCE_MAP_CLASS(movie_info, (title)(producer)(director))

extern table<movie_info> movie_infos;

const query<movie_info> those_with_producers =
    movie_infos
    .where(movie_infos->producer.is_initialized());

const query<std::string> producers =
    those_with_producers
    .select(* those_with_producers->producer);

const query<std::string> padded_producers =
    movie_infos
    .select(movie_infos->producer.get_value_or("<unknown>"));

const query<std::string> top_credits =
    movie_infos
    .select(movie_infos->producer.get_value_or(movie_infos->director));

Order

Optionals cannot currently be compared on the server side with the relational operators, and attempts to do so are caught at compile time.

Optionals can, however, be sorted on the server side with order(). Then boost::none compares equal to boost::none and less than anything else, and in other cases the contents of the two optionals are compared.

Representation

If it weren't for boost::optionals, every C++ type would be mapped to columns with NOT NULL constraints.

Most of the time, optional_mapper<T> simply waives this rule: it maps boost::optional<T> to the same set of columns as the representation of T, sans their NOT NULL constraints. Then an uninitialized boost::optional<T> is represented by all columns being NULL.

That approach breaks down occasionally. Consider boost::optional<boost::optional<float>>. It has two distinct null-like values:

const boost::optional<float> no_float;
assert(! no_float);

const boost::optional<boost::optional<float>> nothing1;
const boost::optional<boost::optional<float>> nothing2(no_float);

assert(! nothing1);
assert(nothing2);
assert(nothing1 != nothing2);

The simple, efficient strategy of putting NULLs for nones cannot be applied to boost::optional<boost::optional<float>>, because a NULL column would be ambiguous between nothing1 and nothing2.

So optional_mapper<T> distinguishes two cases:

For example, float cannot be represented as NULL, so boost::optional<float> gets the optimized representation (one column). Thus boost::optional<float> can be represented as a NULL; so boost::optional<boost::optional<float>> gets the unoptimized representation (two columns). But that representation incudes a flag, which is never NULL. Therefore boost::optional<boost::optional<boost::optional<float>>> gets the optimized representation (still only two columns). And so on.


PrevUpHomeNext