HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Implementation similar to boost::optional for modelling function failure

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
functionoptionalforfailureimplementationsimilarboostmodelling

Problem

The idea being you can have a simple type called maybe that wraps another type, which provides the ability to possibly not return a value from a function. I'm sure it's not perfect yet, but it's got the broad strokes I'm looking for. I'm mostly looking for corner cases I've missed and anything else I've overlooked.

```
#ifndef MAYBE_H_
#define MAYBE_H_

#include

namespace {
// implementation enabled for non-reference types
template
struct maybe {
maybe() : valid(false) {} // default constructor

maybe(const T& val) : valid(true) { // contructor from wrapped value
new ((void*)storage) T(val);
}

maybe(const maybe &other) { // copy constructor
valid = other.valid;
if (valid) {
new ((void*)storage) T(other.get_ref());
}
}

maybe(maybe &&other) : maybe() { // move constructor
std::swap(valid, other.valid);
std::swap(storage, other.storage);
}

~maybe() { // destructor
if (valid) {
get_ref().~T();
}
}

// assignment operator, uses copy-and-swap trick
maybe& operator =(maybe other) {
swap(*this, other);
return *this;
}

// type-coercion operator to get back to wrapped value
// calling without checking validity causes bad_exception
operator T&() {
if (!valid) {
throw std::bad_exception();
}
return get_ref();
}

// for checking validity of container
explicit operator bool() { return valid; }
private:
bool valid;
alignas(T) char storage[sizeof(T)];

T& get_ref() const { return static_cast((void)storage); }
};

// implementation enabled

Solution

Let's go over it bit-by-bit:

  • You should sprinkle around quite a lot of constexpr and noexcept-markers.



If at least one instantiation of a template is potentially constexpr, marking the template thus is not an error. Not marking it means none is.

  • union{T value;}; better shows the intent and is simpler than alignas(T) char storage[sizeof(T)];.



  • Do not values with byte-for-byte unless they are trivial types. Doing so is simply wrong.



  • The move-ctor should move, not swap. That might be far less expensive anyway.



  • Your base variant should gain a specialization for trivially copyable types.



  • Your base-variant should have a variadic-template ctor for constructing the value directly.



  • Your references-variant does not need a separate valid-flag: *NULL is not valid, thus a null pointer serves adequately.



  • Only include the minimal headers you need in your include-file.



  • Do not use an unnamed namespace, or every translation-unit will have its own unique template (which is different from all the others).



As a side-note, boost::optional should serve for the case you describe.

Context

StackExchange Code Review Q#68686, answer score: 6

Revisions (0)

No revisions yet.