Lagrange
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 <limits>
19
20namespace lagrange {
21
24
49template <typename TargetType, typename SourceType>
50constexpr auto safe_cast(SourceType value)
51 -> std::enable_if_t<!std::is_same<SourceType, TargetType>::value, TargetType>
52{
53 if constexpr (std::is_integral_v<TargetType> && std::is_floating_point_v<SourceType>) {
54 SourceType float_max = std::nextafter(
55 static_cast<SourceType>(std::numeric_limits<TargetType>::max()),
56 SourceType(0));
57 SourceType float_min = std::nextafter(
58 static_cast<SourceType>(std::numeric_limits<TargetType>::min()),
59 SourceType(0));
60 if (value > float_max || value < float_min) {
61 logger().error("Casting failed: float cast overflow for float {}", value);
62 throw BadCastError();
63 }
64 }
65
66 TargetType value_2 = static_cast<TargetType>(value);
67
68 if constexpr (std::is_integral_v<SourceType> && std::is_floating_point_v<TargetType>) {
69 TargetType float_max = std::nextafter(
70 static_cast<TargetType>(std::numeric_limits<SourceType>::max()),
71 TargetType(0));
72 TargetType float_min = std::nextafter(
73 static_cast<TargetType>(std::numeric_limits<SourceType>::min()),
74 TargetType(0));
75 if (value_2 > float_max || value_2 < float_min) {
76 logger().error("Casting failed: float cast overflow for integer {}", value);
77 throw BadCastError();
78 }
79 }
80
81 SourceType value_3 = static_cast<SourceType>(value_2);
82
83 if ((value_2 >= 0) != (value >= 0)) {
84 // Sign changed. Not good.
85 logger().error("Casting failed: from {} to {} causes a sign change...", value, value_2);
86 throw BadCastError();
87 } else if (value_3 == value) {
88 // Lossless cast. :D
89 return value_2;
90 } else {
91 // Lossy cast... Check for casting error.
92 constexpr SourceType EPS =
93 static_cast<SourceType>(std::numeric_limits<TargetType>::epsilon());
94
95 // Generates warning C4146: "unary minus operator applied to unsigned type, result still
96 // unsigned" this cannot happen, as we check for unsigned types above.
97 LA_DISABLE_WARNING_BEGIN
98 LA_DISABLE_WARNING_MSVC(4146)
99 const SourceType value_abs = value_3 > 0 ? value_3 : -value_3;
100 LA_DISABLE_WARNING_END
101
102 const SourceType scaled_eps = value_abs >= SourceType(1) ? EPS * value_abs : EPS;
103 if (value_3 > value && value_3 < value + scaled_eps) {
104 return value_2;
105 } else if (value_3 < value && value_3 + scaled_eps > value) {
106 return value_2;
107 } else {
108 // Cast is likely not valid...
109 logger().error(
110 "Casting failed: from {} to {} will incur error ({}) larger than {}",
111 value,
112 value_2,
113 value - static_cast<SourceType>(value_2),
114 scaled_eps);
115 throw BadCastError();
116 }
117 }
118}
119
129template <typename T>
130constexpr T safe_cast(T value)
131{
132 return value;
133}
134
144template <typename TargetType>
145constexpr TargetType safe_cast(bool value)
146{
147 // do we really want to allow this?
148 return static_cast<TargetType>(value);
149}
150
162template <typename T, typename U>
163constexpr T safe_cast_enum(const U u)
164{
165 static_assert(
166 std::is_enum_v<T> || std::is_enum_v<U>,
167 "At least one of the types should be an enum");
168 static_assert(
169 (std::is_enum_v<T> && std::is_enum_v<U>) == std::is_same_v<T, U>,
170 "Casting one enum to another is prohibited");
171
172 using underlying_t = std::underlying_type_t<std::conditional_t<std::is_enum_v<T>, T, U>>;
173 using enum_t = std::conditional_t<std::is_enum_v<T>, T, U>;
174 using other_t = std::conditional_t<std::is_enum_v<T>, U, T>;
175
176 if constexpr (std::is_enum_v<T>) {
177 return static_cast<enum_t>(safe_cast<underlying_t>(u));
178 } else {
179 return safe_cast<other_t>(static_cast<underlying_t>(u));
180 }
181}
182
184
185} // 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:163
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:50
Main namespace for Lagrange.
Definition: AABBIGL.h:30
An exception of this type is thrown when a lagrange::safe_cast<> fails.
Definition: Error.h:37