From 27259f8ebad5e3372db5c4d8cb35b522e43fe28f Mon Sep 17 00:00:00 2001
From: Hudd <haddayn@gmail.com>
Date: Mon, 11 Mar 2024 21:35:51 +0400
Subject: [PATCH] types: add narrow_cast

---
 types/include/aw/types/support/narrow.h | 54 +++++++++++++++++++++++++
 1 file changed, 54 insertions(+)
 create mode 100644 types/include/aw/types/support/narrow.h

diff --git a/types/include/aw/types/support/narrow.h b/types/include/aw/types/support/narrow.h
new file mode 100644
index 00000000..7ca61b29
--- /dev/null
+++ b/types/include/aw/types/support/narrow.h
@@ -0,0 +1,54 @@
+#ifndef aw_types_support_narrow_h
+#define aw_types_support_narrow_h
+
+#include <type_traits>
+#include <utility>
+#include <cassert>
+
+namespace aw {
+/*!
+ * Narrowing cast.
+ * Identical to static_cast but makes it easier to grep the code.
+ */
+template <typename To, typename From>
+constexpr To narrow_cast(From&& from) noexcept
+{
+	return static_cast<To>(std::forward<From>(from));
+}
+
+template <typename T, typename U>
+constexpr bool same_sign(const T& t, const U& u)
+{
+	return (t < T{}) == (u < U{});
+}
+
+/*!
+ * Checked narrowing cast. Inspired by GSL.
+ */
+template <class To, class From>
+	requires std::is_arithmetic_v<To>
+constexpr To narrow(From from)
+{
+	auto to = narrow_cast<To>(from);
+
+	assert(static_cast<From>(to) == from);
+
+	if constexpr(std::is_signed_v<To> != std::is_signed_v<From>)
+		assert(same_sign(to, from));
+
+	return to;
+}
+
+template <class To, class From>
+	requires (!std::is_arithmetic_v<To>)
+constexpr To narrow(From from)
+{
+	auto to = narrow_cast<To>(from);
+
+	assert(static_cast<From>(to) == from);
+
+	return to;
+}
+} // namespace aw
+
+#endif // aw_types_support_narrow_h