value-preserving-literals 0.0.1
value-preserving literals for C++
Loading...
Searching...
No Matches
val.h
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-3.0-or-later
2// Copyright © 2026 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH
3// Matthias Kretz <m.kretz@gsi.de>
4
14
15#ifndef INCLUDE_VAL_H_
16#define INCLUDE_VAL_H_
17
18#if __cpp_concepts >= 202002L && __cpp_deleted_function >= 202403L \
19 && __cpp_constexpr_exceptions >= 202411L
20
21#include <concepts>
22#include <exception>
23#include <limits>
24#include <source_location>
25#include <string_view>
26
32#define vir_lib_val_literal 202601L
33
40namespace vir
41{
42 using std::integral;
43 using std::signed_integral;
44 using std::unsigned_integral;
45 using std::floating_point;
46 using std::source_location;
47 using std::type_identity_t;
48 using std::numeric_limits;
49 using std::u8string_view;
50
56 template <typename _Tp>
57 concept __arithmetic = integral<_Tp> || floating_point<_Tp>;
58
59 struct constinteger;
60
61 struct constreal;
62
72 class bad_value_preserving_cast : public std::exception
73 {
74 private:
75#if __cpp_impl_reflection >= 202506L
77 decltype(^^int) _M_poison;
78#endif
80 source_location _M_where;
81
82 public:
88 consteval
89 bad_value_preserving_cast(source_location __where = source_location::current()) noexcept
90 : _M_where{__where} {}
91
96
101
107#if __cpp_impl_reflection >= 202506L
108 consteval
109#else
110 constexpr
111#endif
112 const char*
113 what() const noexcept override
114 { return "conversion is not value-preserving"; }
115
121 consteval u8string_view u8what() const noexcept
122 { return u8"conversion is not value-preserving"; }
123
129 consteval source_location where() const noexcept { return _M_where; }
130 };
131
138 struct _ConstBinaryOps
139 {
149 template <__arithmetic _Tp>
150 struct _ConvertTo
151 {
152 const _Tp _M_value;
153
157 consteval
158 _ConvertTo(const constinteger& __x)
159 : _M_value(__x)
160 {}
161
165 consteval
166 _ConvertTo(const constreal& __x)
167 : _M_value(__x)
168 {}
169 };
170
174#define _GLIBCXX_CONVERTTO_OP(constraint, op) \
175 template <constraint _Tp> \
176 friend constexpr _Tp& \
177 operator op##=(_Tp& __a, _ConvertTo<type_identity_t<_Tp>> __b) noexcept \
178 { return __a += __b._M_value; } \
179 \
180 template <constraint _Tp> \
181 friend constexpr _Tp \
182 operator op(_Tp __a, _ConvertTo<type_identity_t<_Tp>> __b) noexcept \
183 { return __a op __b._M_value; } \
184 \
185 template <constraint _Tp> \
186 friend constexpr _Tp \
187 operator op(_ConvertTo<type_identity_t<_Tp>> __a, _Tp __b) noexcept \
188 { return __a._M_value op __b; }
189
190 _GLIBCXX_CONVERTTO_OP(__arithmetic, +)
191 _GLIBCXX_CONVERTTO_OP(__arithmetic, -)
192 _GLIBCXX_CONVERTTO_OP(__arithmetic, *)
193 _GLIBCXX_CONVERTTO_OP(__arithmetic, /)
194 _GLIBCXX_CONVERTTO_OP(integral, %)
195 _GLIBCXX_CONVERTTO_OP(integral, &)
196 _GLIBCXX_CONVERTTO_OP(integral, |)
197 _GLIBCXX_CONVERTTO_OP(integral, ^)
198
199#undef _GLIBCXX_CONVERTTO_OP
200
204#define _GLIBCXX_CONVERTTO_CMP(op) \
205 template <__arithmetic _Tp> \
206 friend constexpr bool \
207 operator op(_Tp __a, _ConvertTo<type_identity_t<_Tp>> __b) noexcept \
208 { return __a op __b._M_value; } \
209 \
210 template <__arithmetic _Tp> \
211 friend constexpr bool \
212 operator op(_ConvertTo<type_identity_t<_Tp>> __a, _Tp __b) noexcept \
213 { return __a._M_value op __b; }
214
215 _GLIBCXX_CONVERTTO_CMP(==)
216 _GLIBCXX_CONVERTTO_CMP(!=)
217 _GLIBCXX_CONVERTTO_CMP(<=)
218 _GLIBCXX_CONVERTTO_CMP(>=)
219 _GLIBCXX_CONVERTTO_CMP(<)
220 _GLIBCXX_CONVERTTO_CMP(>)
221
222#undef _GLIBCXX_CONVERTTO_CMP
223 };
224
233 struct constinteger : _ConstBinaryOps
234 {
236 unsigned long long _M_value;
237
239 bool _M_negative = false;
240
247 friend consteval constinteger
249 { return constinteger{{}, __v._M_value, !__v._M_negative}; }
250
256 friend consteval constinteger
258 { return __v; }
259
265 friend consteval constinteger
267 = delete("complement cannot be applied to value of unspecified width");
268
274 friend consteval constinteger
275 operator!(constinteger) = delete("explicitly write 1 or 0 instead");
276
288 template <__arithmetic _Up>
289 consteval
290 operator _Up() const
291 {
292 using L = numeric_limits<_Up>;
293 if constexpr (floating_point<_Up>)
294 {
295 if (static_cast<unsigned long long>(static_cast<_Up>(_M_value)) != _M_value)
297 _Up r = static_cast<_Up>(_M_value);
298 return _M_negative ? -r : r;
299 }
300 else
301 {
302 if ((_M_negative && _M_value > -static_cast<unsigned long long>(L::lowest()))
303 || (!_M_negative && _M_value > L::max()))
305 return static_cast<_Up>(_M_negative ? -_M_value : _M_value);
306 }
307 }
308 };
309
318 consteval constinteger
319 operator""_val(unsigned long long __x) noexcept
320 { return constinteger{{}, __x}; }
321
328 consteval constinteger
329 val(unsigned_integral auto __x) noexcept
330 { return constinteger{{}, __x}; }
331
338 consteval constinteger
339 val(signed_integral auto __x) noexcept
340 {
341 if (__x >= 0)
342 return constinteger{{}, static_cast<unsigned long long>(__x)};
343 else
344 return constinteger{{}, -static_cast<unsigned long long>(__x), true};
345 }
346
355 struct constreal : _ConstBinaryOps
356 {
358 long double _M_value;
359
366 friend consteval constreal
367 operator-(constreal __v) noexcept
368 { return constreal{{}, -__v._M_value}; }
369
375 friend consteval constreal
376 operator+(constreal __v) noexcept
377 { return __v; }
378
384 friend consteval constreal
386
392 friend consteval constreal
393 operator!(constreal) = delete("explicitly write 1 or 0 instead");
394
398 template <__arithmetic _Up>
399 consteval
400 operator _Up() const
401 {
402 using L = numeric_limits<_Up>;
403 if (_M_value > L::max() || _M_value < L::lowest())
405 if (static_cast<long double>(static_cast<_Up>(_M_value)) != _M_value)
407 return static_cast<_Up>(_M_value);
408 }
409 };
410
419 consteval constreal
420 operator""_val(long double __x) noexcept
421 { return constreal{{}, __x}; }
422
429 consteval constreal
430 val(long double __x) noexcept
431 { return constreal{{}, __x}; }
432}
433
434#endif
435
436#endif // INCLUDE_VAL_H_
437
438// vim: ft=cpp
Exception thrown when conversion to arithmetic type would change value.
Definition val.h:73
consteval bad_value_preserving_cast & operator=(const bad_value_preserving_cast &)=default
Defaulted copy assignment.
consteval u8string_view u8what() const noexcept
Get UTF-8 error description.
Definition val.h:121
consteval const char * what() const noexcept override
Get error description.
Definition val.h:113
consteval bad_value_preserving_cast(source_location __where=source_location::current()) noexcept
Construct with source location.
Definition val.h:89
consteval bad_value_preserving_cast & operator=(bad_value_preserving_cast &&)=default
Defaulted move assignment.
consteval bad_value_preserving_cast(bad_value_preserving_cast &&)=default
Defaulted move constructor.
consteval bad_value_preserving_cast(const bad_value_preserving_cast &)=default
Defaulted copy constructor.
consteval source_location where() const noexcept
Get source location of the failed conversion.
Definition val.h:129
consteval constinteger val(unsigned_integral auto __x) noexcept
Create untyped constant from typed value / constant expression.
Definition val.h:329
Untyped integer literal type.
Definition val.h:234
friend consteval constinteger operator-(constinteger __v) noexcept
Unary negation operator.
Definition val.h:248
friend consteval constinteger operator~(constinteger)=delete("complement cannot be applied to value of unspecified width")
Bitwise complement operator (deleted)
friend consteval constinteger operator+(constinteger __v) noexcept
Unary plus operator (identity)
Definition val.h:257
friend consteval constinteger operator!(constinteger)=delete("explicitly write 1 or 0 instead")
Logical NOT operator (deleted)
Untyped real literal type.
Definition val.h:356
friend consteval constreal operator-(constreal __v) noexcept
Unary negation operator.
Definition val.h:367
friend consteval constreal operator+(constreal __v) noexcept
Unary plus operator (identity)
Definition val.h:376
friend consteval constreal operator~(constreal)=delete
Bitwise complement operator (deleted)
friend consteval constreal operator!(constreal)=delete("explicitly write 1 or 0 instead")
Logical NOT operator (deleted)