You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

430 lines
7.4 KiB

1 year ago
  1. #pragma once
  2. #include <exception>
  3. #include <type_traits>
  4. #include "aligned_union.h"
  5. namespace vlc
  6. {
  7. class TryException : public std::logic_error
  8. {
  9. public:
  10. using std::logic_error::logic_error;
  11. };
  12. class TryUninitialized : public TryException
  13. {
  14. public:
  15. TryUninitialized() : TryException("Try is not initialized")
  16. {
  17. }
  18. };
  19. template<typename T> class Try
  20. {
  21. enum class State : int32_t
  22. {
  23. None,
  24. Success,
  25. Failure
  26. };
  27. public:
  28. using value_type = T;
  29. Try() : m_state(State::None)
  30. {
  31. }
  32. ~Try()
  33. {
  34. switch (m_state)
  35. {
  36. case State::Success:
  37. reinterpret_cast<T*>(&m_storage)->~T();
  38. break;
  39. case State::Failure:
  40. reinterpret_cast<std::exception_ptr*>(&m_storage)->~exception_ptr();
  41. break;
  42. case State::None:
  43. break;
  44. }
  45. }
  46. explicit Try(const T& v) : m_state(State::Success)
  47. {
  48. static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible");
  49. new(&m_storage) T(v);
  50. }
  51. explicit Try(T&& v) : m_state(State::Success)
  52. {
  53. new(&m_storage) T(std::forward<T>(v));
  54. }
  55. explicit Try(std::exception_ptr exc) : m_state(State::Failure)
  56. {
  57. new(&m_storage) std::exception_ptr(exc);
  58. }
  59. Try(const Try& src) : m_state(src.m_state)
  60. {
  61. static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible");
  62. switch (m_state)
  63. {
  64. case State::Success:
  65. new(&m_storage) T(*reinterpret_cast<T*>(&src.m_storage));
  66. break;
  67. case State::Failure:
  68. new(&m_storage) std::exception_ptr(*reinterpret_cast<std::exception_ptr*>(&src.m_storage));
  69. break;
  70. case State::None:
  71. break;
  72. }
  73. }
  74. Try& operator = (const Try& src)
  75. {
  76. static_assert(std::is_copy_constructible<T>::value, "T must be copy constructible");
  77. m_state = src.m_state;
  78. switch (m_state)
  79. {
  80. case State::Success:
  81. new(&m_storage) T(*reinterpret_cast<T*>(&src.m_storage));
  82. break;
  83. case State::Failure:
  84. new(&m_storage) std::exception_ptr(*reinterpret_cast<std::exception_ptr*>(&src.m_storage));
  85. break;
  86. case State::None:
  87. break;
  88. }
  89. return *this;
  90. }
  91. Try(Try&& src) : m_state(src.m_state)
  92. {
  93. switch (m_state)
  94. {
  95. case State::Success:
  96. new(&m_storage) T(std::move(*reinterpret_cast<T*>(&src.m_storage)));
  97. break;
  98. case State::Failure:
  99. new(&m_storage) std::exception_ptr(std::move(*reinterpret_cast<std::exception_ptr*>(&src.m_storage)));
  100. break;
  101. case State::None:
  102. break;
  103. }
  104. }
  105. Try& operator = (Try&& src)
  106. {
  107. m_state = src.m_state;
  108. switch (m_state)
  109. {
  110. case State::Success:
  111. new(&m_storage) T(std::move(*reinterpret_cast<T*>(&src.m_storage)));
  112. break;
  113. case State::Failure:
  114. new(&m_storage) std::exception_ptr(std::move(*reinterpret_cast<std::exception_ptr*>(&src.m_storage)));
  115. break;
  116. case State::None:
  117. break;
  118. }
  119. return *this;
  120. }
  121. T& value() &
  122. {
  123. throwIfFailure();
  124. return *reinterpret_cast<T*>(&m_storage);
  125. }
  126. const T& value() const &
  127. {
  128. throwIfFailure();
  129. return *reinterpret_cast<const T*>(&m_storage);
  130. }
  131. T&& value() &&
  132. {
  133. throwIfFailure();
  134. return std::move(*reinterpret_cast<T*>(&m_storage));
  135. }
  136. const T& operator * () const
  137. {
  138. throwIfFailure();
  139. return value();
  140. }
  141. T& operator * ()
  142. {
  143. throwIfFailure();
  144. return value();
  145. }
  146. const T* operator -> () const
  147. {
  148. throwIfFailure();
  149. return &value();
  150. }
  151. T* operator -> ()
  152. {
  153. throwIfFailure();
  154. return &value();
  155. }
  156. std::exception_ptr& exception()
  157. {
  158. return *reinterpret_cast<std::exception_ptr*>(&m_storage);
  159. }
  160. const std::exception_ptr& exception() const
  161. {
  162. return *reinterpret_cast<const std::exception_ptr*>(&m_storage);
  163. }
  164. bool isSuccess() const
  165. {
  166. return m_state == State::Success;
  167. }
  168. bool isFailure() const
  169. {
  170. return m_state == State::Failure;
  171. }
  172. template<bool isTry, typename R>
  173. typename std::enable_if<isTry, R>::type
  174. get()
  175. {
  176. return std::forward<R>(*this);
  177. }
  178. template<bool isTry, typename R>
  179. typename std::enable_if<!isTry, R>::type
  180. get()
  181. {
  182. return std::forward<R>(value());
  183. }
  184. template<typename Exc, typename F> bool recoverWith(F&& func) const
  185. {
  186. if (!isFailure())
  187. {
  188. return false;
  189. }
  190. try
  191. {
  192. std::rethrow_exception(exception());
  193. }
  194. catch (Exc& e)
  195. {
  196. func(e);
  197. return true;
  198. }
  199. catch (...)
  200. {
  201. }
  202. return false;
  203. }
  204. private:
  205. State m_state;
  206. typename vlc::aligned_union<4, T, std::exception_ptr>::type m_storage;
  207. void throwIfFailure() const
  208. {
  209. switch (m_state)
  210. {
  211. case State::Failure:
  212. std::rethrow_exception(exception());
  213. break;
  214. case State::None:
  215. throw TryUninitialized();
  216. break;
  217. case State::Success:
  218. break;
  219. }
  220. }
  221. };
  222. template<> class Try<void>
  223. {
  224. public:
  225. using value_type = void;
  226. Try() : m_has_value(true)
  227. {
  228. }
  229. explicit Try(std::exception_ptr exc) : m_has_value(false), m_exception(exc)
  230. {
  231. }
  232. Try(const Try& src) : m_has_value(src.m_has_value), m_exception(src.m_exception)
  233. {
  234. }
  235. Try& operator = (const Try& src)
  236. {
  237. m_has_value = src.m_has_value;
  238. m_exception = src.m_exception;
  239. return *this;
  240. }
  241. void value() const
  242. {
  243. throwIfFailure();
  244. }
  245. void operator * () const
  246. {
  247. return value();
  248. }
  249. bool isSuccess() const
  250. {
  251. return m_has_value;
  252. }
  253. bool isFailure() const
  254. {
  255. return !isSuccess();
  256. }
  257. std::exception_ptr& exception()
  258. {
  259. return m_exception;
  260. }
  261. const std::exception_ptr& exception() const
  262. {
  263. return m_exception;
  264. }
  265. template<typename Exc, typename F> bool recoverWith(F&& func) const
  266. {
  267. if (!isFailure())
  268. {
  269. return false;
  270. }
  271. try
  272. {
  273. std::rethrow_exception(exception());
  274. }
  275. catch (Exc& e)
  276. {
  277. func(e);
  278. return true;
  279. }
  280. catch (...)
  281. {
  282. }
  283. return false;
  284. }
  285. template <bool, typename R>
  286. R get()
  287. {
  288. return std::forward<R>(*this);
  289. }
  290. private:
  291. bool m_has_value;
  292. std::exception_ptr m_exception;
  293. void throwIfFailure() const
  294. {
  295. if (!m_has_value)
  296. {
  297. if (m_exception)
  298. {
  299. std::rethrow_exception(m_exception);
  300. }
  301. else
  302. {
  303. throw std::logic_error("Exception is not set");
  304. }
  305. }
  306. }
  307. };
  308. template<typename F>
  309. typename std::enable_if<
  310. !std::is_same<typename std::result_of<F()>::type, void>::value,
  311. Try<typename std::result_of<F()>::type>>::type
  312. make_try_with(F&& f)
  313. {
  314. using Result = typename std::result_of<F()>::type;
  315. try
  316. {
  317. return Try<Result>(f());
  318. }
  319. catch (...)
  320. {
  321. return Try<Result>(std::current_exception());
  322. }
  323. }
  324. template<typename F>
  325. typename std::enable_if<
  326. std::is_same<typename std::result_of<F()>::type, void>::value,
  327. Try<void>>::type
  328. make_try_with(F&& f)
  329. {
  330. try
  331. {
  332. f();
  333. return Try<void>();
  334. }
  335. catch (...)
  336. {
  337. return Try<void>(std::current_exception());
  338. }
  339. }
  340. namespace detail
  341. {
  342. template<typename... T> struct UnwrapHelper
  343. {
  344. // Build list of tuple arguments by recursive concatening them and unwrapping from Try at same time
  345. template<typename... TailList>
  346. static std::tuple<T...> unwrap(std::tuple<Try<T>...>&& t, TailList&&... tl)
  347. {
  348. return unwrap(std::move(t), std::forward<TailList>(tl)..., std::move(*std::get<sizeof...(TailList)>(t)));
  349. }
  350. // If that list is compatible with original T... , then we can finally make tuple from this list
  351. static std::tuple<T...> unwrap(std::tuple<Try<T>...>&&, T&&... args)
  352. {
  353. return std::make_tuple(std::forward<T>(args)...);
  354. }
  355. };
  356. }
  357. template<typename... T> std::tuple<T...> unwrap_tuple(std::tuple<Try<T>...>&& ts)
  358. {
  359. return detail::UnwrapHelper<T...>::unwrap(std::forward<std::tuple<Try<T>...>>(ts));
  360. }
  361. }