userver: userver/utils/numeric_cast.hpp Source File
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
numeric_cast.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/numeric_cast.hpp
4/// @brief @copybrief utils::numeric_cast
5
6#include <limits>
7#include <stdexcept>
8
9#include <fmt/format.h>
10
11#include <userver/compiler/demangle.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace utils {
16
17namespace impl {
18
19template <typename T>
20using PrintableValue = std::conditional_t<(sizeof(T) > 1), T, int>;
21
22}
23
24/// Detects loss of range when a numeric type is converted, and throws an
25/// exception if the range cannot be preserved
26///
27/// ## Example usage:
28///
29/// @snippet utils/numeric_cast_test.cpp Sample utils::numeric_cast usage
30template <typename To, typename From>
31constexpr To numeric_cast(From input) {
32 static_assert(std::is_integral_v<From>);
33 static_assert(std::is_integral_v<To>);
34 using FromLimits = std::numeric_limits<From>;
35 using ToLimits = std::numeric_limits<To>;
36
37 std::string_view overflow_type{};
38
39 if constexpr (ToLimits::digits < FromLimits::digits) {
40 if (input > static_cast<From>(ToLimits::max())) {
41 overflow_type = "positive";
42 }
43 }
44
45 // signed -> signed: loss if narrowing
46 // signed -> unsigned: loss
47 if constexpr (FromLimits::is_signed && (!ToLimits::is_signed || ToLimits::digits < FromLimits::digits)) {
48 if (input < static_cast<From>(ToLimits::lowest())) {
49 overflow_type = "negative";
50 }
51 }
52
53 if (!overflow_type.empty()) {
54 throw std::runtime_error(fmt::format(
55 "Failed to convert {} {} into {} due to {} integer overflow",
56 compiler::GetTypeName<From>(),
57 static_cast<impl::PrintableValue<From>>(input),
58 compiler::GetTypeName<To>(),
59 overflow_type
60 ));
61 }
62
63 return static_cast<To>(input);
64}
65
66} // namespace utils
67
68USERVER_NAMESPACE_END