diff --git a/README.md b/README.md index 0451565..a69a74a 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ # Money -`io1::Money` is a header-only c++ 20 class that holds money amounts in the range -9,223,372,036,854,775,808 +9,223,372,036,854,775,807. Amounts are intended to be expressed in the lowest subdivision allowed by a currency (eg. cents for USD), hence the integer base type. Localized formatting is supported by the standard `moneypunct`facet. Arithmetic operations involving `io1::Money` instances and integers are exact (yet subject to overflow). Arithmetic operations with floats are rounded towards the nearest even integer (aka banker’s rounding). +`io1::money` is a header-only c++ 20 class that holds money amounts in the range -9,223,372,036,854,775,808 +9,223,372,036,854,775,807. Amounts are intended to be expressed in the lowest subdivision allowed by a currency (eg. cents for USD), hence the integer base type. Localized formatting is supported by the standard `moneypunct`facet. Arithmetic operations involving `io1::money` instances and integers are exact (yet subject to overflow). Arithmetic operations with floats are rounded towards the nearest even integer (aka banker’s rounding). # Rationale -An amount like \$0.10 cannot be represented by a float because of the involved loss of precision that would make the following operation unbalanced: \$1.00≠\$0.10+\$0.10… (ten times). Besides, not all currencies have a 1/100 subdivision[^dinar] or even a decimal subdivision[^ougiya]. As a result, money amounts are stored as plain integer values and formatting them with the correct currency / sub-currency format is left to the user of the class. For example, the value 12550 stored in a `io1::Money` instance may be formatted as $125.50, 12.550 DT or 2510 UM depending on the locale context. The `io1` namespace provides overloads to `std::put_money` and `std::get_money` into order to format `io1::Money` instances with the currently imbued `std::moneypunct` facet. +An amount like \$0.10 cannot be represented by a float because of the involved loss of precision that would make the following operation unbalanced: \$1.00≠\$0.10+\$0.10… (ten times). Besides, not all currencies have a 1/100 subdivision[^dinar] or even a decimal subdivision[^ougiya]. As a result, money amounts are stored as plain integer values and formatting them with the correct currency / sub-currency format is left to the user of the class. For example, the value 12550 stored in a `io1::money` instance may be formatted as $125.50, 12.550 DT or 2510 UM depending on the locale context. The `io1` namespace provides overloads to `std::put_money` and `std::get_money` into order to format `io1::money` instances with the currently imbued `std::moneypunct` facet. -To prevent any unexpected rounding issue, it is not possible to construct a `io1::Money` instance from a floating-point type. Multiplication and division by a floating-point type are provided and round to the nearest even integer. They are intended to support operations like percentages or loan interest rate calculations. +To prevent any unexpected rounding issue, it is not possible to construct a `io1::money` instance from a floating-point type. Multiplication and division by a floating-point type are provided and round to the nearest even integer. They are intended to support operations like percentages or loan interest rate calculations. -Integer and `io1::Money` arithmetic is exact but may overflow in the same conditions as any integers. Integer division may throw an exception if the result is not exact. +Integer and `io1::money` arithmetic is exact but may overflow in the same conditions as any integers. Integer division may throw an exception if the result is not exact. [^dinar]: In Tunisia 1 TND equals 1000 millimes. [^ougiya]: In Mauritania 1 MRU equals 5 khoums. # References -`io1::Money` is a trivial class (`std::is_trivial_v == true`) and has standard layout (`std::is_standard_layout_v == true`). +`io1::money` is a trivial class (`std::is_trivial_v == true`) and has standard layout (`std::is_standard_layout_v == true`). ## Traits ```cpp -io1::Money::value_type; +io1::money::value_type; ``` The underlying type used to store amounts. @@ -28,17 +28,17 @@ The underlying type used to store amounts. ## Constructors ```cpp -io1::Money::Money() noexcept; (1) -constexpr io1::Money::Money(io1::Money const & o) noexcept; (2) -template explicit constexpr Money(T amount) noexcept; (3) -template constexpr io1::Money operator ""_money() noexcept; (4) +io1::money::money() noexcept; (1) +constexpr io1::money::money(io1::money const & o) noexcept; (2) +template explicit constexpr io1::money(T amount) noexcept; (3) +template constexpr io1::money operator ""_money() noexcept; (4) ``` -(1) Create an uninitialized instance. Such instances are meant to be initialized by copy assignment. Before they are they can only be destructed. Any other use of a `void` constructed `io1::Money` has undefined behavior. +(1) Create an uninitialized instance. Such instances are meant to be initialized by copy assignment. Before they are they can only be destructed. Any other use of a `void` constructed `io1::money` has undefined behavior. (2) Create an instance which amount is the same as `o`. -(3) Create an instance which amount is `amount`. If `amount` overflows the capacity of `io1::Money` then it has undefined behavior. +(3) Create an instance which amount is `amount`. If `amount` overflows the capacity of `io1::money` then it has undefined behavior. (4) If provided with an integer number, it behaves like (3) except that overflows are diagnosed with a compilation failure. If provided with a float number, it deduces the lowest currency subdivision amount by ignoring the decimal separator (see the example below). @@ -46,7 +46,7 @@ If provided with a float number, it deduces the lowest currency subdivision amou ### Example ```cpp -static_assert(io1::Money(120) == 120_money, "Assuming lowest subdivision."); +static_assert(io1::money(120) == 120_money, "Assuming lowest subdivision."); static_assert(120_money == 1.20_money, "Assuming 1/100th subdivision."); static_assert(12_money == 1.2_money, "Assuming 1/10th subdivision."); static_assert(102_money == 1.02_money, "Assuming 1/100th subdivision."); @@ -56,7 +56,7 @@ static_assert(1022_money == 1.022_money, "Assuming 1/1000th subdivision."); ## Observers ```cpp -[[nodiscard]] constexpr io1::Money::value_type const & io1::Money::data() const noexcept; +[[nodiscard]] constexpr io1::money::value_type const & io1::money::data() const noexcept; ``` Returns the amount stored by the instance. @@ -66,10 +66,10 @@ Returns the amount stored by the instance. ### Increment / Decrement ```cpp -[[nodiscard]] constexpr io1::Money io1::Money::operator++(int ignored) noexcept; (1) -[[nodiscard]] constexpr io1::Money io1::Money::operator--(int ignored) noexcept; (2) -constexpr io1::Money & io1::Money::operator++() noexcept; (3) -constexpr io1::Money & io1::Money::operator--() noexcept; (4) +[[nodiscard]] constexpr io1::money io1::money::operator++(int ignored) noexcept; (1) +[[nodiscard]] constexpr io1::money io1::money::operator--(int ignored) noexcept; (2) +constexpr io1::money & io1::money::operator++() noexcept; (3) +constexpr io1::money & io1::money::operator--() noexcept; (4) ``` (1, 2) Post increment/decrement operator. The `int` argument is ignored. Overflow has undefined behavior. @@ -79,13 +79,13 @@ constexpr io1::Money & io1::Money::operator--() noexcept; (4) ### Assignment Operators ```cpp -constexpr io1::Money & io1::Money::operator=(io1::Money const & m) noexcept; (1) -constexpr io1::Money & io1::Money::operator+=(io1::Money m) noexcept; (2) -constexpr io1::Money & io1::Money::operator-=(io1::Money m) noexcept; (3) -template constexpr io1::Money & io1::Money::operator*=(T i) noexcept; (4) -template constexpr io1::Money & io1::Money::operator/=(T i); (5) -io1::Money & io1::Money::operator*=(long double f) noexcept; (6) -io1::Money & io1::Money::operator/=(long double f) noexcept; (7) +constexpr io1::money & io1::money::operator=(io1::money const & m) noexcept; (1) +constexpr io1::money & io1::money::operator+=(io1::money m) noexcept; (2) +constexpr io1::money & io1::money::operator-=(io1::money m) noexcept; (3) +template constexpr io1::money & io1::money::operator*=(T i) noexcept; (4) +template constexpr io1::money & io1::money::operator/=(T i); (5) +io1::money & io1::money::operator*=(long double f) noexcept; (6) +io1::money & io1::money::operator/=(long double f) noexcept; (7) ``` (1) Copy assignment. @@ -94,19 +94,19 @@ io1::Money & io1::Money::operator/=(long double f) noexcept; (7) (4) Multiplication by an integer assignment. Replace the amount with the result of the multiplication of the previous amount by `i`. Overflow has undefined behavior. -(5) Division by an integer assignment. Replace the amount with the result of the division of the previous amount by `i`. If the result is not exact, the operator throws an instance of `io1::Money::InexactDivision` and provides the strong exception guarantee (see example below). Dividing by zero has undefined behavior. +(5) Division by an integer assignment. Replace the amount with the result of the division of the previous amount by `i`. If the result is not exact, the operator throws an instance of `io1::money::InexactDivision` and provides the strong exception guarantee (see example below). Dividing by zero has undefined behavior. (6, 7) Multiplication and division by a float. Replace the amount with the result of the multiplication / division of the previous amount by `f`. Overflow and division by zero have undefined behavior. Inexact results are rounded to nearest even integer. These operators assert that the current floating-point rounding mode is still (or has been restored to) the default value of `FE_TONEAREST`. -Multiplication and division assignments from `io1::Money` instances are not provided because they would violate dimensional analysis and would hence allow flawed uses of money arithmetic. For example, consider the result of 5 USD multiplied by 10 USD. According to dimensional analysis, the result should be homogeneous to USD _squared_, hence 50 USD² and not 50 USD. Likewise, 10 USD divided by 5 USD equals 2 (no dimension) and not 2 USD. +Multiplication and division assignments from `io1::money` instances are not provided because they would violate dimensional analysis and would hence allow flawed uses of money arithmetic. For example, consider the result of 5 USD multiplied by 10 USD. According to dimensional analysis, the result should be homogeneous to USD _squared_, hence 50 USD² and not 50 USD. Likewise, 10 USD divided by 5 USD equals 2 (no dimension) and not 2 USD. ### Example ```cpp -io1::Money m = 10_money; +io1::money m = 10_money; m /= 2; // ok, the result is 5_money try { m /= 2; } -catch (io1::Money::InexactDivision const & e) +catch (io1::money::InexactDivision const & e) { std::cerr << "Dividing " << e.dividend << " by " << e.divisor << " is not exact.\n"; std::cout << "m still holds: " << m << '\n'; // 5_money @@ -116,7 +116,7 @@ catch (io1::Money::InexactDivision const & e) ### Arithmetic Operators ```cpp -[[nodiscard]] constexpr io1::Money io1::Money::operator-() const noexcept; (1) +[[nodiscard]] constexpr io1::money io1::money::operator-() const noexcept; (1) ``` (1) Return an instance with an opposite amount. Overflow has undefined behavior. @@ -124,13 +124,13 @@ catch (io1::Money::InexactDivision const & e) The following operators are built from the assignment operators of the previous section and thus have the same restrictions. ```cpp -[[nodiscard]] constexpr io1::Money io1::operator+(io1::Money lhs, io1::Money rhs) noexcept; -[[nodiscard]] constexpr io1::Money io1::operator-(io1::Money lhs, io1::Money rhs) noexcept; -template [[nodiscard]] constexpr io1::Money io1::operator*(io1::Money lhs, T rhs) noexcept; -template [[nodiscard]] constexpr io1::Money io1::operator*(T lhs, io1::Money rhs) noexcept; -template [[nodiscard]] constexpr io1::Money io1::operator/(io1::Money lhs, T rhs); -[[nodiscard]] io1::Money io1::operator*(long double lhs, io1::Money rhs) noexcept; -[[nodiscard]] io1::Money io1::operator/(io1::Money lhs, long double rhs) noexcept; +[[nodiscard]] constexpr io1::money io1::operator+(io1::money lhs, io1::money rhs) noexcept; +[[nodiscard]] constexpr io1::money io1::operator-(io1::money lhs, io1::money rhs) noexcept; +template [[nodiscard]] constexpr io1::money io1::operator*(io1::money lhs, T rhs) noexcept; +template [[nodiscard]] constexpr io1::money io1::operator*(T lhs, io1::money rhs) noexcept; +template [[nodiscard]] constexpr io1::money io1::operator/(io1::money lhs, T rhs); +[[nodiscard]] io1::money io1::operator*(long double lhs, io1::money rhs) noexcept; +[[nodiscard]] io1::money io1::operator/(io1::money lhs, long double rhs) noexcept; ``` Right multiplication by `long double` is not provided because it has more rounding issues than left multiplication. Consider the following example. @@ -145,58 +145,58 @@ auto const m1 = 0.1 * 10. * 2_money; (2) (2) Left to right evaluation gives the expected result of `2_money`. ```cpp -[[nodiscard]] io1::moneydiv_t io1::div(io1::Money m, io1::Money::value_type divisor) noexcept; +[[nodiscard]] io1::moneydiv_t io1::div(io1::money m, io1::money::value_type divisor) noexcept; ``` Compute both the quotient (`io1::moneydiv_t::quot`) and remainder (`io1::moneydiv_t::rem`) of `m` divided by `divisor`. It has undefined behavior if `0 == divisor`. Returned `quot` and `rem` are such that `divisor * quot + rem == m` and `std::abs(rem) < std::abs(m)`. Like `std::div_t`, `io1::moneydiv_t` may be implemented as ```cpp -struct moneydiv_t { io1::Money quot; io1::Money rem; }; +struct moneydiv_t { io1::money quot; io1::money rem; }; ``` or ```cpp -struct moneydiv_t { io1::Money rem; io1::Money quot; }; +struct moneydiv_t { io1::money rem; io1::money quot; }; ``` ### Comparison Operators ```cpp -[[nodiscard]] constexpr std::strong_ordering io1::operator<=>(io1::Money lhs, io1::Money rhs) noexcept; -[[nodiscard]] constexpr bool io1::operator==(io1::Money lhs, io1::Money rhs) noexcept; -[[nodiscard]] constexpr bool io1::operator!=(io1::Money lhs, io1::Money rhs) noexcept; -[[nodiscard]] constexpr bool io1::operator<(io1::Money lhs, io1::Money rhs) noexcept; -[[nodiscard]] constexpr bool io1::operator<=(io1::Money lhs, io1::Money rhs) noexcept; -[[nodiscard]] constexpr bool io1::operator>(io1::Money lhs, io1::Money rhs) noexcept; -[[nodiscard]] constexpr bool io1::operator>=(io1::Money lhs, io1::Money rhs) noexcept; +[[nodiscard]] constexpr std::strong_ordering io1::operator<=>(io1::money lhs, io1::money rhs) noexcept; +[[nodiscard]] constexpr bool io1::operator==(io1::money lhs, io1::money rhs) noexcept; +[[nodiscard]] constexpr bool io1::operator!=(io1::money lhs, io1::money rhs) noexcept; +[[nodiscard]] constexpr bool io1::operator<(io1::money lhs, io1::money rhs) noexcept; +[[nodiscard]] constexpr bool io1::operator<=(io1::money lhs, io1::money rhs) noexcept; +[[nodiscard]] constexpr bool io1::operator>(io1::money lhs, io1::money rhs) noexcept; +[[nodiscard]] constexpr bool io1::operator>=(io1::money lhs, io1::money rhs) noexcept; ``` ### Stream Operators ```cpp -std::ostream & operator<<(std::ostream & stream, io1::Money m) noexcept; (1) -std::istream & operator>>(std::istream & stream, io1::Money & m); (2) -[[nodiscard]] /*unspecified*/ io1::put_money(io1::Money const & m, bool intl = false) noexcept; (3) -[[nodiscard]] /*unspecified*/ io1::get_money(io1::Money & m, bool intl = false); (4) +std::ostream & operator<<(std::ostream & stream, io1::money m) noexcept; (1) +std::istream & operator>>(std::istream & stream, io1::money & m); (2) +[[nodiscard]] /*unspecified*/ io1::put_money(io1::money const & m, bool intl = false) noexcept; (3) +[[nodiscard]] /*unspecified*/ io1::get_money(io1::money & m, bool intl = false); (4) ``` (1) Write the amount held by `m` in `stream`. -(2) Parse an instance of `io1::Money` from `stream` and copy assign it to `m`. The implementation is equivalent to the following code. +(2) Parse an instance of `io1::money` from `stream` and copy assign it to `m`. The implementation is equivalent to the following code. ```cpp -std::istream & operator>>(std::istream & stream, io1::Money & m) +std::istream & operator>>(std::istream & stream, io1::money & m) { - io1::Money::value_type amount; - if (stream >> amount) m = io1::Money{amount}; + io1::money::value_type amount; + if (stream >> amount) m = io1::money{amount}; return stream; } ``` (3) Return an object of unspecified type that can be inserted into a `std::ostream` instance to format `m` according to its current `moneypunct` facet. The `intl` argument, if true, uses the international currency string instead of the currency symbol. -(4) Return an object of unspecified type that can extract an instance of `io1::Money` from a `std::istream` instance according to its current `moneypunct` facet. The `intl` argument, if true, expects to find a required international currency string instead of an optional currency symbol. +(4) Return an object of unspecified type that can extract an instance of `io1::money` from a `std::istream` instance according to its current `moneypunct` facet. The `intl` argument, if true, expects to find a required international currency string instead of an optional currency symbol. ### Standard Format Support @@ -247,10 +247,10 @@ USD 12.35 ## Exceptions ```cpp -struct [[nodiscard]] io1::Money::InexactDivision +struct [[nodiscard]] io1::money::InexactDivision { - io1::Money::value_type dividend; - io1::Money::value_type divisor; + io1::money::value_type dividend; + io1::money::value_type divisor; }; ``` @@ -281,7 +281,7 @@ private: pattern do_neg_format() const override { return {{symbol, sign, value}}; }; }; -[[nodiscard]] auto compute_installment_plan(io1::Money price, std::size_t count) noexcept +[[nodiscard]] auto compute_installment_plan(io1::money price, std::size_t count) noexcept { assert(0 != count && "Not doing a plan for no payment."); @@ -289,7 +289,7 @@ private: assert(div_result.rem >= 0_money && "Count was unsigned."); auto const m = static_cast(div_result.rem.data()); - std::vector plan(count, div_result.quot); + std::vector plan(count, div_result.quot); assert(m < plan.size() && "As per the io1::div documentation since plan.size() is count."); for (std::size_t i = 0; i < m; ++i) ++plan[i]; diff --git a/include/io1/money.hpp b/include/io1/money.hpp index 9b75e0e..59ecaf1 100644 --- a/include/io1/money.hpp +++ b/include/io1/money.hpp @@ -17,15 +17,15 @@ namespace io1 { - class Money; + class money; } template -constexpr io1::Money operator""_money(void) noexcept; +constexpr io1::money operator""_money(void) noexcept; namespace io1 { - class Money + class money { public: using value_type = std::int64_t; @@ -36,58 +36,58 @@ namespace io1 "Type too short to hold the advertised value range."); public: - Money(void) noexcept = default; + money(void) noexcept = default; - constexpr Money(Money const &) noexcept = default; - constexpr Money & operator=(Money const &) noexcept = default; + constexpr money(money const &) noexcept = default; + constexpr money & operator=(money const &) noexcept = default; template - explicit constexpr Money(T amount) noexcept : amount_(static_cast(amount)) + explicit constexpr money(T amount) noexcept : amount_(static_cast(amount)) { } template - explicit constexpr Money(T amount) noexcept = delete; + explicit constexpr money(T amount) noexcept = delete; template - friend constexpr Money(::operator""_money)(void) noexcept; + friend constexpr money(::operator""_money)(void) noexcept; [[nodiscard]] constexpr value_type const & data(void) const noexcept { return amount_; } - [[nodiscard]] constexpr Money operator++(int) noexcept { return Money{amount_++}; } - [[nodiscard]] constexpr Money operator--(int) noexcept { return Money{amount_--}; } + [[nodiscard]] constexpr money operator++(int) noexcept { return money{amount_++}; } + [[nodiscard]] constexpr money operator--(int) noexcept { return money{amount_--}; } - constexpr Money & operator++(void) noexcept + constexpr money & operator++(void) noexcept { ++amount_; return *this; } - constexpr Money & operator--(void) noexcept + constexpr money & operator--(void) noexcept { --amount_; return *this; } - constexpr Money & operator+=(Money m) noexcept + constexpr money & operator+=(money m) noexcept { amount_ += m.amount_; return *this; } - constexpr Money & operator-=(Money m) noexcept + constexpr money & operator-=(money m) noexcept { amount_ -= m.amount_; return *this; } template - constexpr Money & operator*=(T i) noexcept + constexpr money & operator*=(T i) noexcept { amount_ *= static_cast(i); return *this; } template - Money & operator*=(T v) noexcept + money & operator*=(T v) noexcept { assert((std::fegetround() == FE_TONEAREST) && "Make sure the default rounding mode is active before entering this function."); @@ -99,13 +99,13 @@ namespace io1 } template - constexpr Money & operator/=(T i); + constexpr money & operator/=(T i); template - Money & operator/=(T v) noexcept; + money & operator/=(T v) noexcept; - [[nodiscard]] constexpr Money operator-(void) const noexcept { return Money{-amount_}; } - [[nodiscard]] friend constexpr std::strong_ordering operator<=>(Money lhs, Money rhs) noexcept = default; + [[nodiscard]] constexpr money operator-(void) const noexcept { return money{-amount_}; } + [[nodiscard]] friend constexpr std::strong_ordering operator<=>(money lhs, money rhs) noexcept = default; public: struct [[nodiscard]] InexactDivision : public std::runtime_error @@ -124,22 +124,22 @@ namespace io1 struct PutMoney; struct GetMoney; - friend io1::Money::PutMoney put_money(io1::Money, bool) noexcept; - friend io1::Money::GetMoney get_money(io1::Money &, bool); + friend io1::money::PutMoney put_money(io1::money, bool) noexcept; + friend io1::money::GetMoney get_money(io1::money &, bool); private: value_type amount_; }; - static_assert(std::is_trivial_v && std::is_standard_layout_v, + static_assert(std::is_trivial_v && std::is_standard_layout_v, "You have changed io1::Money in a way that removed its POD nature!"); template - constexpr Money & Money::operator/=(T i) + constexpr money & money::operator/=(T i) { assert(0 != i && "Dividing by zero is undefined behavior."); auto const divisor = static_cast(i); - if (amount_ % divisor) throw io1::Money::InexactDivision{amount_, divisor}; + if (amount_ % divisor) throw io1::money::InexactDivision{amount_, divisor}; // strong guarantee amount_ /= divisor; @@ -148,7 +148,7 @@ namespace io1 } template - inline Money & Money::operator/=(T v) noexcept + inline money & money::operator/=(T v) noexcept { assert(0. != v && "Dividing by zero is undefined behavior."); assert((std::fegetround() == FE_TONEAREST) && @@ -160,56 +160,56 @@ namespace io1 return *this; } - [[nodiscard]] inline constexpr Money operator+(Money lhs, Money rhs) noexcept + [[nodiscard]] inline constexpr money operator+(money lhs, money rhs) noexcept { return lhs += rhs; } - [[nodiscard]] inline constexpr Money operator-(Money lhs, Money rhs) noexcept + [[nodiscard]] inline constexpr money operator-(money lhs, money rhs) noexcept { return lhs -= rhs; } template - constexpr Money operator*(Money lhs, T rhs) = delete; + constexpr money operator*(money lhs, T rhs) = delete; template - [[nodiscard]] constexpr Money operator*(Money lhs, T rhs) noexcept + [[nodiscard]] constexpr money operator*(money lhs, T rhs) noexcept { return lhs *= rhs; } template - [[nodiscard]] constexpr Money operator*(T lhs, Money rhs) noexcept + [[nodiscard]] constexpr money operator*(T lhs, money rhs) noexcept { return rhs *= lhs; } template - [[nodiscard]] inline Money operator*(T lhs, Money rhs) noexcept + [[nodiscard]] inline money operator*(T lhs, money rhs) noexcept { return rhs *= static_cast(lhs); } template - [[nodiscard]] constexpr Money operator/(Money lhs, T rhs) + [[nodiscard]] constexpr money operator/(money lhs, T rhs) { return lhs /= rhs; } template - [[nodiscard]] inline Money operator/(Money lhs, T rhs) noexcept + [[nodiscard]] inline money operator/(money lhs, T rhs) noexcept { return lhs /= static_cast(rhs); } // Helper structure to build a io1::Money object from a user-defined string litteral - struct Money::StringLitteralDecoder + struct money::StringLitteralDecoder { public: template - [[nodiscard]] constexpr static Money apply(void) noexcept + [[nodiscard]] constexpr static money apply(void) noexcept { - return Money{parse_mantissa<0, STR...>()}; + return money{parse_mantissa<0, STR...>()}; } private: @@ -250,21 +250,21 @@ namespace io1 } }; - inline std::ostream & operator<<(std::ostream & stream, io1::Money m) noexcept + inline std::ostream & operator<<(std::ostream & stream, io1::money m) noexcept { return stream << m.data(); } - inline std::istream & operator>>(std::istream & stream, io1::Money & m) + inline std::istream & operator>>(std::istream & stream, io1::money & m) { - io1::Money::value_type amount; + io1::money::value_type amount; stream >> amount; - if (stream) m = io1::Money(amount); // strong guarantee + if (stream) m = io1::money(amount); // strong guarantee return stream; } - struct Money::GetMoney + struct money::GetMoney { - explicit GetMoney(Money & m, bool intl) noexcept : intl_(intl), amount_(m.amount_){}; + explicit GetMoney(money & m, bool intl) noexcept : intl_(intl), amount_(m.amount_){}; GetMoney(GetMoney const &) = delete; GetMoney & operator=(GetMoney const &) = delete; @@ -290,12 +290,12 @@ namespace io1 } bool intl_; - Money::value_type & amount_; + money::value_type & amount_; }; - struct Money::PutMoney + struct money::PutMoney { - explicit PutMoney(Money m, bool intl) noexcept : intl_(intl), amount_(std::to_string(m.data())) {} + explicit PutMoney(money m, bool intl) noexcept : intl_(intl), amount_(std::to_string(m.data())) {} PutMoney(PutMoney const &) = delete; PutMoney & operator=(PutMoney const &) = delete; @@ -308,52 +308,52 @@ namespace io1 std::string amount_; }; - [[nodiscard]] inline io1::Money::PutMoney put_money(io1::Money m, bool intl = false) noexcept + [[nodiscard]] inline io1::money::PutMoney put_money(io1::money m, bool intl = false) noexcept { - return io1::Money::PutMoney(m, intl); + return io1::money::PutMoney(m, intl); } - [[nodiscard]] inline io1::Money::GetMoney get_money(io1::Money & m, bool intl = false) + [[nodiscard]] inline io1::money::GetMoney get_money(io1::money & m, bool intl = false) { - return io1::Money::GetMoney(m, intl); + return io1::money::GetMoney(m, intl); } namespace detail { struct moneydiv_quotrem_t { - Money quot; - Money rem; + money quot; + money rem; }; struct moneydiv_remquot_t { - Money rem; - Money quot; + money rem; + money quot; }; constexpr bool is_quot_rem_v = 10 == std::div_t{10, 5}.quot; } // namespace detail using moneydiv_t = std::conditional_t; - [[nodiscard]] inline moneydiv_t div(Money m, Money::value_type divisor) noexcept + [[nodiscard]] inline moneydiv_t div(money m, money::value_type divisor) noexcept { assert(0 != divisor && "Division by zero is undefined behavior."); auto const result = std::div(m.data(), divisor); - return {.quot = Money(result.quot), .rem = Money(result.rem)}; + return {.quot = money(result.quot), .rem = money(result.rem)}; } } // namespace io1 template -constexpr io1::Money operator""_money(void) noexcept +constexpr io1::money operator""_money(void) noexcept { - return io1::Money::StringLitteralDecoder::apply(); + return io1::money::StringLitteralDecoder::apply(); } template -struct std::formatter +struct std::formatter { template constexpr auto parse(FormatParseContext & ctx) @@ -387,7 +387,7 @@ struct std::formatter } template - auto format(io1::Money const & m, FormatContext & ctx) const + auto format(io1::money const & m, FormatContext & ctx) const { if (!locale_) return int_.format(m.data(), ctx); else @@ -402,6 +402,6 @@ struct std::formatter bool locale_ : 1 {false}; bool showbase_ : 1 {false}; bool intl_ : 1 {false}; - std::formatter int_; + std::formatter int_; std::formatter, CharT> string_; }; diff --git a/test/test_money.cpp b/test/test_money.cpp index 26bd0b7..bb3cc02 100644 --- a/test/test_money.cpp +++ b/test/test_money.cpp @@ -18,10 +18,10 @@ TEST_CASE("Value bounds") TEST_CASE("Constexpr functions") { { - [[maybe_unused]] constexpr io1::Money m(1_money); + [[maybe_unused]] constexpr io1::money m(1_money); } { - [[maybe_unused]] constexpr io1::Money m = 1_money; + [[maybe_unused]] constexpr io1::money m = 1_money; } { [[maybe_unused]] constexpr auto m = 1_money ++; @@ -102,9 +102,9 @@ TEST_CASE("Constexpr functions") TEST_CASE("Constructors") { - CHECK_EQ(0, io1::Money(0).data()); - CHECK_EQ(20, io1::Money(20).data()); - CHECK_EQ(-20, io1::Money(-20).data()); + CHECK_EQ(0, io1::money(0).data()); + CHECK_EQ(20, io1::money(20).data()); + CHECK_EQ(-20, io1::money(-20).data()); } TEST_CASE("Literal constructions") @@ -191,7 +191,7 @@ TEST_CASE("Arithmetic") CHECK_EQ(-2_money, 4_money /= -2); CHECK_EQ(0_money, 0_money /= -2); - CHECK_THROWS_AS(4_money /= 3, io1::Money::InexactDivision); + CHECK_THROWS_AS(4_money /= 3, io1::money::InexactDivision); { auto m = 4_money; @@ -199,7 +199,7 @@ TEST_CASE("Arithmetic") { m /= 3; } - catch (io1::Money::InexactDivision const & e) + catch (io1::money::InexactDivision const & e) { CHECK_EQ(4, e.dividend); CHECK_EQ(3, e.divisor); @@ -207,7 +207,7 @@ TEST_CASE("Arithmetic") } } - CHECK_THROWS_AS([[maybe_unused]] auto const m = 4_money / 3, io1::Money::InexactDivision); + CHECK_THROWS_AS([[maybe_unused]] auto const m = 4_money / 3, io1::money::InexactDivision); { auto m = 4_money; @@ -215,7 +215,7 @@ TEST_CASE("Arithmetic") { [[maybe_unused]] auto const m2 = m / 3; } - catch (io1::Money::InexactDivision const & e) + catch (io1::money::InexactDivision const & e) { CHECK_EQ(4, e.dividend); CHECK_EQ(3, e.divisor); @@ -708,7 +708,7 @@ TEST_CASE("Parse classic") std::stringstream stream; stream << "0 1 -1 15 -15 20745 -20745 90000000000000000000 $9"; - io1::Money m; + io1::money m; CHECK_NOTHROW(stream >> m); CHECK_EQ(0_money, m); CHECK_NOTHROW(stream >> m); @@ -741,7 +741,7 @@ TEST_CASE("Parse") stream.imbue(std::locale(stream.getloc(), new moneypunct_facet)); - io1::Money m; + io1::money m; CHECK_NOTHROW(stream >> get_money(m)); CHECK_EQ(0_money, m); CHECK_NOTHROW(stream >> get_money(m)); diff --git a/test/tutorial.cpp b/test/tutorial.cpp index a980b5f..32a9212 100644 --- a/test/tutorial.cpp +++ b/test/tutorial.cpp @@ -20,7 +20,7 @@ class american_moneypunct_facet : public std::moneypunct pattern do_neg_format() const override { return {symbol, sign, value}; }; }; -[[nodiscard]] auto compute_installment_plan(io1::Money price, std::size_t count) noexcept +[[nodiscard]] auto compute_installment_plan(io1::money price, std::size_t count) noexcept { assert(0 != count && "Not doing a plan for no payment."); @@ -28,7 +28,7 @@ class american_moneypunct_facet : public std::moneypunct assert(div_result.rem >= 0_money && "Count was unsigned."); auto const m = static_cast(div_result.rem.data()); - std::vector plan(count, div_result.quot); + std::vector plan(count, div_result.quot); assert(m < plan.size() && "As per the io1::div documentation since plan.size() is count."); for (std::size_t i = 0; i < m; ++i) ++plan[i];