rippled
Loading...
Searching...
No Matches
STAmount.h
1#pragma once
2
3#include <xrpl/basics/CountedObject.h>
4#include <xrpl/basics/LocalValue.h>
5#include <xrpl/basics/Number.h>
6#include <xrpl/beast/utility/instrumentation.h>
7#include <xrpl/protocol/Asset.h>
8#include <xrpl/protocol/IOUAmount.h>
9#include <xrpl/protocol/Issue.h>
10#include <xrpl/protocol/MPTAmount.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/STBase.h>
13#include <xrpl/protocol/Serializer.h>
14#include <xrpl/protocol/XRPAmount.h>
15#include <xrpl/protocol/json_get_or_throw.h>
16
17namespace xrpl {
18
19// Internal form:
20// 1: If amount is zero, then value is zero and offset is -100
21// 2: Otherwise:
22// legal offset range is -96 to +80 inclusive
23// value range is 10^15 to (10^16 - 1) inclusive
24// amount = value * [10 ^ offset]
25
26// Wire form:
27// High 8 bits are (offset+142), legal range is, 80 to 22 inclusive
28// Low 56 bits are value, legal range is 10^15 to (10^16 - 1) inclusive
29class STAmount final : public STBase, public CountedObject<STAmount>
30{
31public:
33 using exponent_type = int;
35
36private:
41
42public:
44
45 static int const cMinOffset = -96;
46 static int const cMaxOffset = 80;
47
48 // Maximum native value supported by the code
49 constexpr static std::uint64_t cMinValue = 1'000'000'000'000'000ull;
50 static_assert(isPowerOfTen(cMinValue));
51 constexpr static std::uint64_t cMaxValue = cMinValue * 10 - 1;
52 static_assert(cMaxValue == 9'999'999'999'999'999ull);
53 constexpr static std::uint64_t cMaxNative = 9'000'000'000'000'000'000ull;
54
55 // Max native value on network.
56 constexpr static std::uint64_t cMaxNativeN = 100'000'000'000'000'000ull;
57 constexpr static std::uint64_t cIssuedCurrency = 0x8'000'000'000'000'000ull;
58 constexpr static std::uint64_t cPositive = 0x4'000'000'000'000'000ull;
59 constexpr static std::uint64_t cMPToken = 0x2'000'000'000'000'000ull;
60 constexpr static std::uint64_t cValueMask = ~(cPositive | cMPToken);
61
62 static std::uint64_t const uRateOne;
63
64 //--------------------------------------------------------------------------
65 STAmount(SerialIter& sit, SField const& name);
66
67 struct unchecked
68 {
69 explicit unchecked() = default;
70 };
71
72 // Do not call canonicalize
73 template <AssetType A>
75 SField const& name,
76 A const& asset,
79 bool negative,
80 unchecked);
81
82 template <AssetType A>
84
85 // Call canonicalize
86 template <AssetType A>
88 SField const& name,
89 A const& asset,
92 bool negative = false);
93
95
96 STAmount(SField const& name, std::uint64_t mantissa = 0, bool negative = false);
97
98 explicit STAmount(std::uint64_t mantissa = 0, bool negative = false);
99
100 explicit STAmount(SField const& name, STAmount const& amt);
101
102 template <AssetType A>
103 STAmount(A const& asset, std::uint64_t mantissa = 0, int exponent = 0, bool negative = false)
105 {
106 canonicalize();
107 }
108
109 // VFALCO Is this needed when we have the previous signature?
110 template <AssetType A>
111 STAmount(A const& asset, std::uint32_t mantissa, int exponent = 0, bool negative = false);
112
113 template <AssetType A>
114 STAmount(A const& asset, std::int64_t mantissa, int exponent = 0);
115
116 template <AssetType A>
117 STAmount(A const& asset, int mantissa, int exponent = 0);
118
119 template <AssetType A>
120 STAmount(A const& asset, Number const& number) : STAmount(fromNumber(asset, number))
121 {
122 }
123
124 // Legacy support for new-style amounts
125 STAmount(IOUAmount const& amount, Issue const& issue);
126 STAmount(XRPAmount const& amount);
127 STAmount(MPTAmount const& amount, MPTIssue const& mptIssue);
128 operator Number() const;
129
130 //--------------------------------------------------------------------------
131 //
132 // Observers
133 //
134 //--------------------------------------------------------------------------
135
136 int
137 exponent() const noexcept;
138
139 bool
140 integral() const noexcept;
141
142 bool
143 native() const noexcept;
144
145 template <ValidIssueType TIss>
146 constexpr bool
147 holds() const noexcept;
148
149 bool
150 negative() const noexcept;
151
152 std::uint64_t
153 mantissa() const noexcept;
154
155 Asset const&
156 asset() const;
157
158 template <ValidIssueType TIss>
159 constexpr TIss const&
160 get() const;
161
162 Issue const&
163 issue() const;
164
165 // These three are deprecated
166 Currency const&
167 getCurrency() const;
168
169 AccountID const&
170 getIssuer() const;
171
172 int
173 signum() const noexcept;
174
177 zeroed() const;
178
179 void
180 setJson(Json::Value&) const;
181
182 STAmount const&
183 value() const noexcept;
184
185 //--------------------------------------------------------------------------
186 //
187 // Operators
188 //
189 //--------------------------------------------------------------------------
190
191 explicit
192 operator bool() const noexcept;
193
194 STAmount&
195 operator+=(STAmount const&);
196 STAmount&
197 operator-=(STAmount const&);
198
199 STAmount& operator=(beast::Zero);
200
201 STAmount&
202 operator=(XRPAmount const& amount);
203
204 STAmount&
205 operator=(Number const&);
206
207 //--------------------------------------------------------------------------
208 //
209 // Modification
210 //
211 //--------------------------------------------------------------------------
212
213 void
214 negate();
215
216 void
217 clear();
218
219 // Zero while copying currency and issuer.
220 void
221 clear(Asset const& asset);
222
223 void
224 setIssuer(AccountID const& uIssuer);
225
227 void
228 setIssue(Asset const& asset);
229
230 //--------------------------------------------------------------------------
231 //
232 // STBase
233 //
234 //--------------------------------------------------------------------------
235
237 getSType() const override;
238
239 std::string
240 getFullText() const override;
241
242 std::string
243 getText() const override;
244
245 Json::Value getJson(JsonOptions = JsonOptions::none) const override;
246
247 void
248 add(Serializer& s) const override;
249
250 bool
251 isEquivalent(STBase const& t) const override;
252
253 bool
254 isDefault() const override;
255
257 xrp() const;
259 iou() const;
261 mpt() const;
262
263private:
264 template <AssetType A>
265 static STAmount
266 fromNumber(A const& asset, Number const& number);
267
268 static std::unique_ptr<STAmount>
269 construct(SerialIter&, SField const& name);
270
271 void
272 set(std::int64_t v);
273 void
274 canonicalize();
275
276 STBase*
277 copy(std::size_t n, void* buf) const override;
278 STBase*
279 move(std::size_t n, void* buf) override;
280
281 STAmount&
282 operator=(IOUAmount const& iou);
283
284 friend class detail::STVar;
285
286 friend STAmount
287 operator+(STAmount const& v1, STAmount const& v2);
288};
289
290template <AssetType A>
292 SField const& name,
293 A const& asset,
296 bool negative,
297 unchecked)
299{
300}
301
302template <AssetType A>
307
308template <AssetType A>
309STAmount::STAmount(SField const& name, A const& asset, std::uint64_t mantissa, int exponent, bool negative)
310 : STBase(name), mAsset(asset), mValue(mantissa), mOffset(exponent), mIsNegative(negative)
311{
312 // mValue is uint64, but needs to fit in the range of int64
314 {
315 XRPL_ASSERT(
317 "xrpl::STAmount::STAmount(SField, A, std::uint64_t, int, bool) : "
318 "maximum mantissa input");
319 }
320 else
321 {
323 throw std::overflow_error("STAmount mantissa is too large " + std::to_string(mantissa));
324 }
325 canonicalize();
326}
327
328template <AssetType A>
329STAmount::STAmount(A const& asset, std::int64_t mantissa, int exponent) : mAsset(asset), mOffset(exponent)
330{
331 set(mantissa);
332 canonicalize();
333}
334
335template <AssetType A>
336STAmount::STAmount(A const& asset, std::uint32_t mantissa, int exponent, bool negative)
337 : STAmount(asset, safe_cast<std::uint64_t>(mantissa), exponent, negative)
338{
339}
340
341template <AssetType A>
342STAmount::STAmount(A const& asset, int mantissa, int exponent)
343 : STAmount(asset, safe_cast<std::int64_t>(mantissa), exponent)
344{
345}
346
347// Legacy support for new-style amounts
348inline STAmount::STAmount(IOUAmount const& amount, Issue const& issue)
349 : mAsset(issue), mOffset(amount.exponent()), mIsNegative(amount < beast::zero)
350{
351 if (mIsNegative)
352 mValue = unsafe_cast<std::uint64_t>(-amount.mantissa());
353 else
354 mValue = unsafe_cast<std::uint64_t>(amount.mantissa());
355
356 canonicalize();
357}
358
359inline STAmount::STAmount(MPTAmount const& amount, MPTIssue const& mptIssue)
360 : mAsset(mptIssue), mOffset(0), mIsNegative(amount < beast::zero)
361{
362 if (mIsNegative)
363 mValue = unsafe_cast<std::uint64_t>(-amount.value());
364 else
365 mValue = unsafe_cast<std::uint64_t>(amount.value());
366
367 canonicalize();
368}
369
370//------------------------------------------------------------------------------
371//
372// Creation
373//
374//------------------------------------------------------------------------------
375
376// VFALCO TODO The parameter type should be Quality not uint64_t
379
381amountFromString(Asset const& asset, std::string const& amount);
382
384amountFromJson(SField const& name, Json::Value const& v);
385
386bool
387amountFromJsonNoThrow(STAmount& result, Json::Value const& jvSource);
388
389// IOUAmount and XRPAmount define toSTAmount, defining this
390// trivial conversion here makes writing generic code easier
391inline STAmount const&
393{
394 return a;
395}
396
397//------------------------------------------------------------------------------
398//
399// Observers
400//
401//------------------------------------------------------------------------------
402
403inline int
404STAmount::exponent() const noexcept
405{
406 return mOffset;
407}
408
409inline bool
410STAmount::integral() const noexcept
411{
412 return mAsset.integral();
413}
414
415inline bool
416STAmount::native() const noexcept
417{
418 return mAsset.native();
419}
420
421template <ValidIssueType TIss>
422constexpr bool
423STAmount::holds() const noexcept
424{
425 return mAsset.holds<TIss>();
426}
427
428inline bool
429STAmount::negative() const noexcept
430{
431 return mIsNegative;
432}
433
434inline std::uint64_t
435STAmount::mantissa() const noexcept
436{
437 return mValue;
438}
439
440inline Asset const&
442{
443 return mAsset;
444}
445
446template <ValidIssueType TIss>
447constexpr TIss const&
449{
450 return mAsset.get<TIss>();
451}
452
453inline Issue const&
455{
456 return get<Issue>();
457}
458
459inline Currency const&
461{
462 return mAsset.get<Issue>().currency;
463}
464
465inline AccountID const&
467{
468 return mAsset.getIssuer();
469}
470
471inline int
472STAmount::signum() const noexcept
473{
474 return mValue ? (mIsNegative ? -1 : 1) : 0;
475}
476
477inline STAmount
479{
480 return STAmount(mAsset);
481}
482
483inline STAmount::operator bool() const noexcept
484{
485 return *this != beast::zero;
486}
487
488inline STAmount::operator Number() const
489{
490 if (native())
491 return xrp();
492 if (mAsset.holds<MPTIssue>())
493 return mpt();
494 return iou();
495}
496
497inline STAmount&
499{
500 clear();
501 return *this;
502}
503
504inline STAmount&
506{
507 *this = STAmount(amount);
508 return *this;
509}
510
511template <AssetType A>
512inline STAmount
513STAmount::fromNumber(A const& a, Number const& number)
514{
515 bool const negative = number.mantissa() < 0;
516 Number const working{negative ? -number : number};
517 Asset asset{a};
518 if (asset.integral())
519 {
520 std::uint64_t const intValue = static_cast<std::int64_t>(working);
521 return STAmount{asset, intValue, 0, negative};
522 }
523
524 auto const [mantissa, exponent] = working.normalizeToRange(cMinValue, cMaxValue);
525
527}
528
529inline void
531{
532 if (*this != beast::zero)
534}
535
536inline void
538{
539 // The -100 is used to allow 0 to sort less than a small positive values
540 // which have a negative exponent.
541 mOffset = integral() ? 0 : -100;
542 mValue = 0;
543 mIsNegative = false;
544}
545
546inline void
548{
550 clear();
551}
552
553inline void
555{
556 mAsset.get<Issue>().account = uIssuer;
557}
558
559inline STAmount const&
560STAmount::value() const noexcept
561{
562 return *this;
563}
564
565inline bool
566isLegalNet(STAmount const& value)
567{
568 return !value.native() || (value.mantissa() <= STAmount::cMaxNativeN);
569}
570
571//------------------------------------------------------------------------------
572//
573// Operators
574//
575//------------------------------------------------------------------------------
576
577bool
578operator==(STAmount const& lhs, STAmount const& rhs);
579bool
580operator<(STAmount const& lhs, STAmount const& rhs);
581
582inline bool
583operator!=(STAmount const& lhs, STAmount const& rhs)
584{
585 return !(lhs == rhs);
586}
587
588inline bool
589operator>(STAmount const& lhs, STAmount const& rhs)
590{
591 return rhs < lhs;
592}
593
594inline bool
595operator<=(STAmount const& lhs, STAmount const& rhs)
596{
597 return !(rhs < lhs);
598}
599
600inline bool
601operator>=(STAmount const& lhs, STAmount const& rhs)
602{
603 return !(lhs < rhs);
604}
605
606STAmount
607operator-(STAmount const& value);
608
609//------------------------------------------------------------------------------
610//
611// Arithmetic
612//
613//------------------------------------------------------------------------------
614
615STAmount
616operator+(STAmount const& v1, STAmount const& v2);
617STAmount
618operator-(STAmount const& v1, STAmount const& v2);
619
620STAmount
621divide(STAmount const& v1, STAmount const& v2, Asset const& asset);
622
623STAmount
624multiply(STAmount const& v1, STAmount const& v2, Asset const& asset);
625
626// multiply rounding result in specified direction
627STAmount
628mulRound(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
629
630// multiply following the rounding directions more precisely.
631STAmount
632mulRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
633
634// divide rounding result in specified direction
635STAmount
636divRound(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
637
638// divide following the rounding directions more precisely.
639STAmount
640divRoundStrict(STAmount const& v1, STAmount const& v2, Asset const& asset, bool roundUp);
641
642// Someone is offering X for Y, what is the rate?
643// Rate: smaller is better, the taker wants the most out: in/out
644// VFALCO TODO Return a Quality object
646getRate(STAmount const& offerOut, STAmount const& offerIn);
647
660[[nodiscard]] STAmount
661roundToScale(STAmount const& value, std::int32_t scale, Number::rounding_mode rounding = Number::getround());
662
672template <AssetType A>
673void
674roundToAsset(A const& asset, Number& value)
675{
676 value = STAmount{asset, value};
677}
678
690template <AssetType A>
691[[nodiscard]] Number
693 A const& asset,
694 Number const& value,
695 std::int32_t scale,
697{
698 NumberRoundModeGuard mg(rounding);
699 STAmount const ret{asset, value};
700 if (ret.integral())
701 return ret;
702 // Note that the ctor will round integral types (XRP, MPT) via canonicalize,
703 // so no extra work is needed for those.
704 return roundToScale(ret, scale);
705}
706
707//------------------------------------------------------------------------------
708
709inline bool
710isXRP(STAmount const& amount)
711{
712 return amount.native();
713}
714
715bool
716canAdd(STAmount const& amt1, STAmount const& amt2);
717
718bool
719canSubtract(STAmount const& amt1, STAmount const& amt2);
720
721} // namespace xrpl
722
723//------------------------------------------------------------------------------
724namespace Json {
725template <>
726inline xrpl::STAmount
727getOrThrow(Json::Value const& v, xrpl::SField const& field)
728{
729 using namespace xrpl;
730 Json::StaticString const& key = field.getJsonName();
731 if (!v.isMember(key))
732 Throw<JsonMissingKeyError>(key);
733 Json::Value const& inner = v[key];
734 return amountFromJson(field, inner);
735}
736} // namespace Json
Lightweight wrapper to tag static string.
Definition json_value.h:44
Represents a JSON value.
Definition json_value.h:130
bool isMember(char const *key) const
Return true if the object has a member named key.
constexpr TIss const & get() const
AccountID const & getIssuer() const
Definition Asset.cpp:17
bool integral() const
Definition Asset.h:92
bool native() const
Definition Asset.h:79
constexpr bool holds() const
Definition Asset.h:130
Tracks the number of instances of an object.
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:25
A currency issued by an account.
Definition Issue.h:13
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
static rounding_mode getround()
Definition Number.cpp:33
constexpr rep mantissa() const noexcept
Returns the mantissa of the external view of the Number.
Definition Number.h:542
static MantissaRange::mantissa_scale getMantissaScale()
Returns which mantissa scale is currently in use for normalization.
Definition Number.cpp:45
Identifies fields.
Definition SField.h:126
void set(std::int64_t v)
Definition STAmount.cpp:910
constexpr bool holds() const noexcept
Definition STAmount.h:423
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:721
constexpr TIss const & get() const
void setIssue(Asset const &asset)
Set the Issue for this amount.
Definition STAmount.cpp:419
std::string getFullText() const override
Definition STAmount.cpp:629
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
Issue const & issue() const
Definition STAmount.h:454
static std::uint64_t const uRateOne
Definition STAmount.h:62
static STAmount fromNumber(A const &asset, Number const &number)
Definition STAmount.h:513
void add(Serializer &s) const override
Definition STAmount.cpp:729
void setIssuer(AccountID const &uIssuer)
Definition STAmount.h:554
void negate()
Definition STAmount.h:530
void canonicalize()
Definition STAmount.cpp:794
std::uint64_t mantissa() const noexcept
Definition STAmount.h:435
int signum() const noexcept
Definition STAmount.h:472
friend class detail::STVar
Definition STAmount.h:284
bool isEquivalent(STBase const &t) const override
Definition STAmount.cpp:763
STBase * copy(std::size_t n, void *buf) const override
Definition STAmount.cpp:232
exponent_type mOffset
Definition STAmount.h:39
static constexpr std::uint64_t cValueMask
Definition STAmount.h:60
std::string getText() const override
Definition STAmount.cpp:639
bool mIsNegative
Definition STAmount.h:40
void setJson(Json::Value &) const
Definition STAmount.cpp:599
SerializedTypeID getSType() const override
Definition STAmount.cpp:623
IOUAmount iou() const
Definition STAmount.cpp:264
bool negative() const noexcept
Definition STAmount.h:429
STAmount zeroed() const
Returns a zero value with the same issuer and currency.
Definition STAmount.h:478
static int const cMaxOffset
Definition STAmount.h:46
bool isDefault() const override
Definition STAmount.cpp:770
bool integral() const noexcept
Definition STAmount.h:410
mantissa_type mValue
Definition STAmount.h:38
STAmount & operator=(beast::Zero)
Definition STAmount.h:498
Currency const & getCurrency() const
Definition STAmount.h:460
static std::unique_ptr< STAmount > construct(SerialIter &, SField const &name)
Definition STAmount.cpp:226
static constexpr std::uint64_t cIssuedCurrency
Definition STAmount.h:57
bool native() const noexcept
Definition STAmount.h:416
static constexpr std::uint64_t cMinValue
Definition STAmount.h:49
Asset const & asset() const
Definition STAmount.h:441
static int const cMinOffset
Definition STAmount.h:45
static constexpr std::uint64_t cMaxNative
Definition STAmount.h:53
int exponent_type
Definition STAmount.h:33
STAmount(A const &asset, Number const &number)
Definition STAmount.h:120
MPTAmount mpt() const
Definition STAmount.cpp:279
static constexpr std::uint64_t cMPToken
Definition STAmount.h:59
int exponent() const noexcept
Definition STAmount.h:404
AccountID const & getIssuer() const
Definition STAmount.h:466
static constexpr std::uint64_t cMaxNativeN
Definition STAmount.h:56
STBase * move(std::size_t n, void *buf) override
Definition STAmount.cpp:238
XRPAmount xrp() const
Definition STAmount.cpp:249
static constexpr std::uint64_t cPositive
Definition STAmount.h:58
STAmount const & value() const noexcept
Definition STAmount.h:560
STAmount(SerialIter &sit, SField const &name)
Definition STAmount.cpp:100
STAmount(A const &asset, std::uint64_t mantissa=0, int exponent=0, bool negative=false)
Definition STAmount.h:103
A type which can be exported to a well known binary format.
Definition STBase.h:115
JSON (JavaScript Object Notation).
Definition json_errors.h:5
xrpl::AccountID getOrThrow(Json::Value const &v, xrpl::SField const &field)
Definition AccountID.h:111
STL namespace.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
Definition Slice.h:198
bool operator>=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:601
bool isXRP(AccountID const &c)
Definition AccountID.h:70
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
Definition base_uint.h:589
Number operator-(Number const &x, Number const &y)
Definition Number.h:638
STAmount amountFromJson(SField const &name, Json::Value const &v)
Definition STAmount.cpp:948
STAmount mulRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount toSTAmount(IOUAmount const &iou, Issue const &iss)
bool isLegalNet(STAmount const &value)
Definition STAmount.h:566
STAmount amountFromString(Asset const &asset, std::string const &amount)
Definition STAmount.cpp:939
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
STAmount roundToScale(STAmount const &value, std::int32_t scale, Number::rounding_mode rounding=Number::getround())
Round an arbitrary precision Amount to the precision of an STAmount that has a given exponent.
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:552
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
Definition STAmount.cpp:474
STAmount amountFromQuality(std::uint64_t rate)
Definition STAmount.cpp:927
SerializedTypeID
Definition SField.h:90
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:434
constexpr bool isPowerOfTen(T value)
Definition Number.h:36
bool operator!=(Buffer const &lhs, Buffer const &rhs) noexcept
Definition Buffer.h:209
void roundToAsset(A const &asset, Number &value)
Round an arbitrary precision Number IN PLACE to the precision of a given Asset.
Definition STAmount.h:674
bool operator<=(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:595
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:545
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
bool operator>(STAmount const &lhs, STAmount const &rhs)
Definition STAmount.h:589
constexpr std::enable_if_t< std::is_integral_v< Dest > &&std::is_integral_v< Src >, Dest > safe_cast(Src s) noexcept
Definition safe_cast.h:19
Zero allows classes to offer efficient comparisons to zero.
Definition Zero.h:25
Note, should be treated as flags that can be | and &.
Definition STBase.h:17
T to_string(T... args)