Lagrange
Loading...
Searching...
No Matches
safe_cast.h
1/*
2 * Copyright 2019 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#include <lagrange/Logger.h>
15#include <lagrange/utils/Error.h>
16#include <lagrange/utils/warning.h>
17
18#include <cmath>
19#include <limits>
20
21namespace lagrange {
22
25
50template <typename TargetType, typename SourceType>
51constexpr auto safe_cast(SourceType value)
52 -> std::enable_if_t<!std::is_same<SourceType, TargetType>::value, TargetType>
53{
54 if constexpr (std::is_integral_v<TargetType> && std::is_floating_point_v<SourceType>) {
55 SourceType float_max = std::nextafter(
56 static_cast<SourceType>(std::numeric_limits<TargetType>::max()),
57 SourceType(0));
58 SourceType float_min = std::nextafter(
59 static_cast<SourceType>(std::numeric_limits<TargetType>::min()),
60 SourceType(0));
61 if (value > float_max || value < float_min) {
62 logger().error("Casting failed: float cast overflow for float {}", value);
63 throw BadCastError();
64 }
65 }
66
67 TargetType value_2 = static_cast<TargetType>(value);
68
69 if constexpr (std::is_integral_v<SourceType> && std::is_floating_point_v<TargetType>) {
70 TargetType float_max = std::nextafter(
71 static_cast<TargetType>(std::numeric_limits<SourceType>::max()),
72 TargetType(0));
73 TargetType float_min = std::nextafter(
74 static_cast<TargetType>(std::numeric_limits<SourceType>::min()),
75 TargetType(0));
76 if (value_2 > float_max || value_2 < float_min) {
77 logger().error("Casting failed: float cast overflow for integer {}", value);
78 throw BadCastError();
79 }
80 }
81
82 SourceType value_3 = static_cast<SourceType>(value_2);
83
84 if ((value_2 >= 0) != (value >= 0)) {
85 // Sign changed. Not good.
86 logger().error("Casting failed: from {} to {} causes a sign change...", value, value_2);
87 throw BadCastError();
88 } else if (value_3 == value) {
89 // Lossless cast. :D
90 return value_2;
91 } else {
92 // Lossy cast... Check for casting error.
93 constexpr SourceType EPS =
94 static_cast<SourceType>(std::numeric_limits<TargetType>::epsilon());
95
96 // Generates warning C4146: "unary minus operator applied to unsigned type, result still
97 // unsigned" this cannot happen, as we check for unsigned types above.
98 LA_DISABLE_WARNING_BEGIN
99 LA_DISABLE_WARNING_MSVC(4146)
100 const SourceType value_abs = value_3 > 0 ? value_3 : -value_3;
101 LA_DISABLE_WARNING_END
102
103 const SourceType scaled_eps = value_abs >= SourceType(1) ? EPS * value_abs : EPS;
104 if (value_3 > value && value_3 < value + scaled_eps) {
105 return value_2;
106 } else if (value_3 < value && value_3 + scaled_eps > value) {
107 return value_2;
108 } else {
109 // Cast is likely not valid...
110 logger().error(
111 "Casting failed: from {} to {} will incur error ({}) larger than {}",
112 value,
113 value_2,
114 value - static_cast<SourceType>(value_2),
115 scaled_eps);
116 throw BadCastError();
117 }
118 }
119}
120
130template <typename T>
131constexpr T safe_cast(T value)
132{
133 return value;
134}
135
145template <typename TargetType>
146constexpr TargetType safe_cast(bool value)
147{
148 // do we really want to allow this?
149 return static_cast<TargetType>(value);
150}
151
163template <typename T, typename U>
164constexpr T safe_cast_enum(const U u)
165{
166 static_assert(
167 std::is_enum_v<T> || std::is_enum_v<U>,
168 "At least one of the types should be an enum");
169 static_assert(
170 (std::is_enum_v<T> && std::is_enum_v<U>) == std::is_same_v<T, U>,
171 "Casting one enum to another is prohibited");
172
173 using underlying_t = std::underlying_type_t<std::conditional_t<std::is_enum_v<T>, T, U>>;
174 using enum_t = std::conditional_t<std::is_enum_v<T>, T, U>;
175 using other_t = std::conditional_t<std::is_enum_v<T>, U, T>;
176
177 if constexpr (std::is_enum_v<T>) {
178 return static_cast<enum_t>(safe_cast<underlying_t>(u));
179 } else {
180 return safe_cast<other_t>(static_cast<underlying_t>(u));
181 }
182}
183
185
186} // namespace lagrange
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
constexpr T safe_cast_enum(const U u)
Casting an enum to scalar and vice versa.
Definition safe_cast.h:164
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:51
Main namespace for Lagrange.
An exception of this type is thrown when a lagrange::safe_cast<> fails.
Definition Error.h:36