A set of simple(ish) C++ templates which implement sum and option
types. They serve a similar purpose to boost::variant
and boost::optional
but are implemented on top of Qt's QVariant container.
Update (2017-09-23): A variant
class has been added to the C++ standard library for C++17 (std::variant) and you can find polyfills for compilers that don't ship with it yet. Consider using that instead of this code in your project.
Either<T1,T2>
represents either a value of type T1
or
a value of type T2
. You can think of it as a QVariant which is restricted
to being either a T1
or T2
rather than any type which QVariant supports.
Either<T1,T2>
instances can be constructed directly from an instance
of T1
or T2
or by using the some(T)
function which returns a type that
can be implicitly cast to any Either
type that has T
as one of its
types.
Simple example:
// parse a value which might either be a
// string or an integer
Either<QString,int> value = parseValue(data);
if (value.is1st()) {
qDebug() << "Read string " << value.as1st();
} else {
qDebug() << "Read integer " << value.as2nd();
}
Maybe<T>
represents either a value of type T or no value. You can think of it
as a QVariant which is restricted to either being a T or a null type.
Maybe<T>
instances are constructed using the just(T)
or nothing()
functions.
just(T)
handles pointers specially. If passed a null pointer it returns
a Maybe
representing no value. This is useful when working with existing code
which uses a null pointer to represent no value.
Simple example:
// try to read the size of a file, which may fail if
// the file is not readable
Maybe<int> size = fileSize(path);
if (size) {
std::cout << "file size is " << size.value() << " bytes" << std::endl;
} else {
std::cout << "could not read file size" << std::endl;
}
See TestMaybe.cpp
for unit tests which show example usage of the Maybe
and
Either
templates.
The templates use QVariant
as the underlying storage and the same performance considerations apply. A QVariant in Qt 4 is 12 bytes in size on most platforms - 8 bytes for the data itself or a pointer to the data, plus 4 bytes for the type tag and flags. Pointers and primitive types of 8 bytes in size or less are stored within the QVariant. Larger types are stored in heap-allocated storage.
Unlike boost::variant
, no attempt is made to handle exceptions that may be thrown when constructing or copying types. The state of a Maybe
or Either
after a failed copy constructor call is undefined.