jubilant-funicular
Option.h
1 #ifndef NTA_OPTION_H_INCLUDED
2 #define NTA_OPTION_H_INCLUDED
3 
4 #include <functional>
5 
6 #include "nta/MyEngine.h"
7 #include "nta/format.h"
8 
9 namespace nta {
10  namespace utils {
16  template<typename T>
17  class Option {
18  private:
19  using type = typename std::remove_reference<T>::type;
21  struct Nop { Nop(const type&& _) {} };
22 
23  using storage_type = typename std::conditional<std::is_reference<T>::value,
24  void*, T>::type;
25  using placement_type = typename std::conditional<std::is_reference<T>::value,
26  Nop, type>::type;
27 
29  Option(const T& d) : m_some(true) {
30  if constexpr (std::is_reference<T>::value) {
31  // Maybe I should just memcpy instead
32  new(&m_data) const void*(std::addressof(d));
33  } else {
34  new(&m_data) placement_type(std::move(d));
35  }
36  }
37 
38  typename std::aligned_storage_t<sizeof(storage_type), alignof(storage_type)> m_data;
39  bool m_some;
40  public:
42  Option() : m_some(false) {}
43  Option(const Option&) = default;
44  template<typename S>
45  Option(const Option<S>&);
46  ~Option() { m_some = false; }
48  static Option some(const T& data) { return Option<T>(data); }
50  static Option none() { return Option<T>(); }
52  explicit operator bool() const { return m_some; }
54  bool is_some() const { return m_some; }
56  bool is_none() const { return !m_some; }
60  T get() const;
62  T unwrap() const { return get(); }
64  T get_or(const T& optb) { return m_some ? get() : optb; }
65  T unwrap_or(const T& optb) { return get_or(optb); }
67  void destroy();
70  template<typename S>
71  Option<S> map(std::function<S(T)> func);
72  template<typename S>
73  Option<S> map(std::function<Option<S>(T)> func);
74  template<typename S>
75  S map_or(std::function<S(T)> func, const S& def);
76  void map(std::function<void(T)> func);
77  };
78  template<typename T> template<typename S>
79  Option<T>::Option(const Option<S>& orig) {
80  static_assert(std::is_convertible_v<S, T>,
81  "Option: attempted invalid conversion from Option<S> to Option<T>");
82  // This line isn't sketch at all
83  *this = orig ? some(orig.unwrap()) : none();
84  }
85  template<typename T>
86  T Option<T>::get() const {
88  if (!m_some) {
89  assert(false && "Tried getting data from a none Option");
90  } else if (std::is_reference<T>::value) {
91  return (T)**reinterpret_cast<const type * const *>(&m_data);
92  } else {
93  return (T)*reinterpret_cast<const type*>(&m_data);
94  }
95  }
96  template<typename T>
98  if (m_some) {
99  reinterpret_cast<type*>(&m_data)->~type();
100  }
101  m_some = false;
102  }
103  template<typename T> template<typename S>
104  Option<S> Option<T>::map(std::function<S(T)> func) {
105  return m_some ? Option<S>::some(func(get())) : Option<S>::none();
106  }
107  template<typename T> template<typename S>
108  Option<S> Option<T>::map(std::function<Option<S>(T)> func) {
109  return m_some ? func(get()) : Option<S>::none();
110  }
111  template<typename T> template<typename S>
112  S Option<T>::map_or(std::function<S(T)> func, const S& def) {
113  return m_some ? func(get()) : def;
114  }
115  template<typename T>
116  void Option<T>::map(std::function<void(T)> func) {
117  if (m_some) func(get());
118  }
119  template<typename T, typename S, typename std::enable_if_t<can_check_equality<T, S>, void*> = nullptr>
120  bool operator==(const Option<T>& lhs, const S& rhs) {
121  return lhs.is_some() && lhs.unwrap() == rhs;
122  }
123  template<typename T, typename S, typename std::enable_if_t<can_check_equality<T, S>, void*> = nullptr>
124  bool operator==(const Option<T>& lhs, const Option<S>& rhs) {
125  return (lhs.is_none() && rhs.is_none()) ||
126  (lhs && rhs && lhs.unwarp() == rhs.unwarp());
127  }
128  template<typename T, typename S, typename std::enable_if_t<can_check_equality<T, S>, void*> = nullptr>
129  bool operator==(const T& lhs, const Option<S>& rhs) {
130  return rhs.is_some() && lhs == rhs.unwrap();
131  }
133  template<typename T>
134  Option<T> make_some(const T& data) {
135  return Option<T>::some(data);
136  }
138  template<typename T>
140  return Option<T>::none();
141  }
142  template<typename T>
143  struct Formatter<Option<T>> {
144  std::string operator()(const Option<T>& arg) {
145  using decayed = typename std::decay<T>::type;
146  if (arg.is_some()) {
147  if constexpr (std::is_invocable_v<Formatter<decayed>, decayed>) {
148  return format("Some({})", arg.unwrap());
149  } else {
150  return "Some";
151  }
152  } else {
153  return "None";
154  }
155  }
156  };
157  }
158 };
159 
160 #endif // NTA_OPTION_H_INCLUDED
nta::utils::make_some
Option< T > make_some(const T &data)
Replacement for calling Option<T>::some()
Definition: Option.h:134
nta::utils::Option::Option
Option(const T &d)
Private constructor (use some or none instead)
Definition: Option.h:29
nta::utils::Formatter
Specialize this struct to use custom types with format.
Definition: format.h:23
nta::utils::Option::some
static Option some(const T &data)
Creates an Option holding some data.
Definition: Option.h:48
nta::utils::Option::destroy
void destroy()
Turns this into None variant, calling destructor if necessary.
Definition: Option.h:97
nta::utils::Option::Option
Option()
Defaults to none variant.
Definition: Option.h:42
nta::utils::Option::get
T get() const
Definition: Option.h:86
nta::utils::Option
Definition: Option.h:17
nta::utils::format
std::string format(const std::string &fmt, Args &&... args)
Definition: format.h:88
nta::utils::Option::none
static Option none()
Creates a None variant Option.
Definition: Option.h:50
nta::utils::Option::get_or
T get_or(const T &optb)
Return the data held by this Option or optb if it's None.
Definition: Option.h:64
nta::utils::Option::map
Option< S > map(std::function< S(T)> func)
Definition: Option.h:104
nta
Definition: Animation2D.h:6
nta::utils::Option::is_some
bool is_some() const
Does this hold some data.
Definition: Option.h:54
nta::utils::make_none
Option< T > make_none()
Replacement for calling Option<T>::none()
Definition: Option.h:139
nta::utils::Option::Nop
Only exists to avoid a warning when using Option<T&>
Definition: Option.h:21
nta::utils::Option::is_none
bool is_none() const
Does this hold nothing?
Definition: Option.h:56
nta::utils::Option::unwrap
T unwrap() const
unwrap and get are the same thing
Definition: Option.h:62