#include <exception>
#include <functional>
#include <memory>
namespace ro {
/*
* A class that may or may not hold a value in it.
*/
template <typename T>
class Maybe
{
public:
/*
* A deleter that doesn't actually delete the pointer. This is used to make sure that
* the thread_local static instance on the stack doesn't get deleted when going out
* of scope
*/
struct NoopDeleter
{
void operator()(Maybe<T>*) {}
};
using pointer_t = std::shared_ptr<Maybe<T>>;
/*
* Gets an pointer to a Maybe that's nothing
*/
static pointer_t nothing();
/*
* Gets a pointer to a Maybe that just have a value.
*/
static pointer_t just(const T& value);
public:
Maybe() = default;
virtual ~Maybe() = default;
/*
* Returns if this Maybe is nothing
*/
virtual bool isNothing() const = 0;
/*
* Gets the value, if this instance has one. Throws a runtimer_error otherwise.
*/
virtual T get() const = 0;
/*
* Gets the value held or the passed in value otherwise.
*/
T getOrElse(const T& defaultValue) const
{
if (isNothing())
{
return defaultValue;
}
return get();
}
/*
* Gets the value stored or throws the exception as supplied by the method passed in
*/
T getOrThrow(const std::function<std::exception()>& exceptionSupplier) const
{
if (isNothing())
{
throw exceptionSupplier();
}
return get();
}
/*
* Binds a function to convert the stored value (if any) to another of the same type
*/
pointer_t bind(const std::function < T(const T&)>& func) const
{
return map<T>(func);
}
/*
* Binds a function to convert the stored value (if any) to another Maybe of the same type
*/
pointer_t flatBind(const std::function<pointer_t(const T&)>& func) const
{
return flatMap<T>(func);
}
/*
* Maps the current value (if any) to another type.
*/
template <typename U>
typename Maybe<U>::pointer_t map(const std::function<U(const T&)>& func) const
{
return flatMap<U>([&](const T& val) { return Maybe<U>::just(func(val)); });
}
/*
* Maps the current value (if any) to another type, using the method that returns a
* Maybe of the mapped type.
*/
template <typename U>
typename Maybe<U>::pointer_t flatMap(const std::function<typename Maybe<U>::pointer_t(const T&)>& func) const
{
if (isNothing())
{
return Maybe<U>::nothing();
}
return func(get());
}
};
template <typename T>
class Nothing : public Maybe<T>
{
public:
virtual bool isNothing() const override
{
return true;
}
virtual T get() const override
{
throw std::runtime_error("No value has been set for this.");
}
};
template <typename T>
class Just : public Maybe<T>
{
public:
Just(const T& value)
: mValue(value)
{
}
virtual bool isNothing() const override
{
return false;
}
virtual T get() const override
{
return mValue;
}
private:
const T mValue;
};
template <typename T>
typename Maybe<T>::pointer_t Maybe<T>::nothing()
{
thread_local static Nothing<T> nothingInstance;
static typename Maybe<T>::pointer_t nothing(¬hingInstance, NoopDeleter());
return nothing;
}
template <typename T>
typename Maybe<T>::pointer_t Maybe<T>::just(const T& value)
{
thread_local static Just<T> justInstance(value);
typename Maybe<T>::pointer_t just(new (&justInstance) Just<T>(value), NoopDeleter());
return just;
}
}
#include <iostream>
#include <sstream>
int main() {
try {
auto meaningOfLife = ro::Maybe<int>::just(10)
->bind([](int n) {
return n * 2;
})
->bind([](int n) {
return n + 22;
})
->map<std::string>([](int n) {
std::stringstream ss;
ss << n;
return ss.str();
})
->getOrThrow([] {
return std::runtime_error("There's no meaning???");
});
std::cout << meaningOfLife << " (" << typeid(meaningOfLife).name() << ")" << std::endl;
} catch (std::exception& e) {
std::cout << "EXCEPTION: " << e.what() << std::endl;
}
return 0;
}