Imagine that we have a function that does not throw an exception and returns a value or indicates that an error has occurred. In Java or C# programming languages, such cases are handled by comparing a return value from a function value with a null
pointer. If the function returned null
, then an error has occurred. In C++, returning a pointer from a function confuses library users and usually requires slow dynamic memory allocation.
Ladies and gentlemen, let me introduce you to the Boost.Optional
library using the following example:
The try_lock_device()
function tries to acquire a lock for a device and may succeed or not, depending on different conditions (in our example it depends on some try_lock_device_impl()
function call):
#include <boost/optional.hpp> #include <iostream> class locked_device { explicit locked_device(const char* /*param*/) { // We have unique access to device. std::cout << "Device is locked\n"; } static bool try_lock_device_impl(); public: void use() { std::cout << "Success!\n"; } static boost::optional<locked_device> try_lock_device() { if (!try_lock_device_impl()) { // Failed to lock device. return boost::none; } // Success! return locked_device("device name"); } ~locked_device(); // Releases device lock. };
The function returns the boost::optional
variable that can be converted to a bool
. If the returned value is equal to true
, then the lock is acquired and an instance of a class to work with the device can be obtained by dereferencing the returned optional variable:
int main() { for (unsigned i = 0; i < 10; ++i) { boost::optional<locked_device> t = locked_device::try_lock_device(); // optional is convertible to bool. if (t) { t->use(); return 0; } else { std::cout << "...trying again\n"; } } std::cout << "Failure!\n"; return -1; }
This program will output the following:
...trying again
...trying again
Device is locked
Success!
boost::optional<T>
under the hood has a properly aligned array of bytes where the object of type T
can be an in-place constructed. It also has a bool
variable to remember the state of the object (is it constructed or not?).
The Boost.Optional
class does not use dynamic allocation and it does not require a default constructor for the underlying type. The current boost::optional
implementation can work with C++11 rvalue references but is not usable with constexpr.
Note
If you have a class T
that has no empty state but your program logic requires an empty state or uninitialized T
, then you have to come up with some workaround. Traditionally, users create some smart pointer to the class T
, keep a nullptr
in it, and dynamically allocate T
if non empty state is required. Stop doing that! Use boost::optional<T>
instead. It's a much faster and more reliable solution.
The C++17 standard includes the std::optional
class. Just replace <boost/optional.hpp>
with <optional>
and boost::
with std::
to use the standard version of this class. std::optional
is usable with constexpr.
Boost's official documentation contains more examples and describes advanced features of Boost.Optional
(like in-place construction). The documentation is available at the following link: http://boost.org/libs/optional.