├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── appveyor.yml ├── catch ├── catch.hpp ├── catch_reporter_automake.hpp ├── catch_reporter_tap.hpp └── catch_reporter_teamcity.hpp ├── conanfile.py ├── include ├── country.h ├── country_currency.h ├── currency.h ├── ext │ └── boost.h ├── money.h └── rounding.h ├── lgtm.yml ├── test ├── CMakeLists.txt ├── main.cpp ├── test_boost.cpp ├── test_country.cpp ├── test_country_currency.cpp ├── test_currency.cpp ├── test_money.cpp └── test_rounding.cpp └── test_package ├── CMakeLists.txt ├── conanfile.py └── example.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | /build 35 | /.vs 36 | /out/build/x64-Debug 37 | /CMakeSettings.json 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: gcc 3 | dist: trusty 4 | 5 | before_install: 6 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 7 | - sudo apt-get update -qq 8 | 9 | install: 10 | # install gcc 8 11 | - sudo apt-get install -qq g++-8 12 | - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 13 | # install cppcheck 14 | - sudo apt-get install -qq cppcheck 15 | 16 | before_script: 17 | - cd ${TRAVIS_BUILD_DIR} 18 | - cmake -H. -BBuild -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DCOUNTRY_AND_CURRENCY_DB=ON -Wdev 19 | - cd Build 20 | 21 | script: 22 | # run tests 23 | - make -j 2 24 | - ctest -V -j 2 -C Release 25 | # run cppcheck 26 | - cppcheck --quiet --error-exitcode=1 . 27 | 28 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.0) 2 | project(moneycpp) 3 | 4 | if(WIN32) 5 | message(status "Setting MSVC flags") 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHc /std:c++latest") 7 | add_definitions(-D_SCL_SECURE_NO_WARNINGS) 8 | elseif(APPLE) 9 | message(status "Setting Clang flags") 10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1z -fexceptions -g -Wall") 11 | else() 12 | include_directories(${LIBUUID_INCLUDE_DIR}) 13 | message(status "Setting GCC flags") 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1z -fexceptions -g -Wall") 15 | endif() 16 | 17 | message(status "** CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") 18 | 19 | if(BUILD_TESTS) 20 | include(CTest) 21 | endif() 22 | 23 | add_subdirectory(test) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Marius Bancila 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # moneycpp 2 | A C++ 17 header-only, cross-platform library for handling monetary values, currencies, rounding and other related features. 3 | 4 | [![Build Status](https://travis-ci.org/mariusbancila/moneycpp.svg?branch=master)](https://travis-ci.org/mariusbancila/moneycpp) 5 | [![Tests status](https://ci.appveyor.com/api/projects/status/k8guis2uxc4673j8?svg=true&pendingText=tests%20-%20pending&failingText=tests%20-%20FAILED&passingText=tests%20-%20OK)](https://ci.appveyor.com/project/mariusbancila/moneycpp) 6 | 7 | ## Core requirements 8 | The library is intended for being used in a variety of types of application including ERP systems, banking, finance, insurance, games, and others. 9 | 10 | The following is a list of its core requirements: 11 | * Provide an API for handling and calculating with monetary amounts. 12 | * Support different numeric capabilities. 13 | * Provide a default set of rounding algorithms and policies and support additional user-defined ones. 14 | * Support the entire [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) list of currencies. 15 | * Support the entire [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) list of countries. 16 | * It should be possible for users to add new (virtual) currencies and countries. 17 | 18 | ## Overview 19 | The library is built around several core components: 20 | * `money` that holds a monetary value 21 | * `currency_unit` that contains currency information for a monetary value as per [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) 22 | * `country_unit` that contains country information in relation to currencies, as per [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) 23 | * rounding algorithms - that specify how values are rounded, and policies - that specify how monetary values are rounded using a rounding algorithm 24 | 25 | ## Library API 26 | ### Monetary values 27 | 28 | A monetary value has two dimensions: the actual amount and the currency that it represents. A monetary value is represented by the `money` class. 29 | The following are examples for working with monetary values: 30 | 31 | ```cpp 32 | // create and operate with money values 33 | auto m = make_money(20.0, currency::USD); 34 | m += make_money(10.5, currency::USD); 35 | m *= 2.5; 36 | 37 | // round money values 38 | m = rounding_policy_standard(round_ceiling())(m); 39 | 40 | // convert between currencies 41 | auto ex = exchange_money( 42 | m, 43 | currency::EUR, 0.86, 44 | rounding_policy_standard(round_ceiling())); 45 | ``` 46 | 47 | The examples above use the type `double` for numerical values. This is a floating point type and can only represent exact decimal values for numbers that are a sum of inverse powers of two. That means floating point types can exactly represent values such as 0.5, 1.25, or 42.90625 but cannot do the same for values such as 0.10 or 19.99. Therefore, floating point types are not appropriate for monetary values because they cannot exactly represent most real numbers. This can be an important aspect in financial applications or, in general, in applications that deal with monetary transactions because over time, or over a large number of transactions, the small differences can add up to important values. Because of this, the library supports 3rd party libraries that provide better representations of real numbers, such as `boost::multiprecision`. All the rounding algorithms are specialized for the `boost::multiprecision::cpp_dec_float`, aliased as `decimal`, as shown below. 48 | 49 | ```cpp 50 | using decimal = boost::multiprecision::number>; 51 | 52 | inline decimal operator""_dec(char const * str, std::size_t) 53 | { return decimal(str); } 54 | 55 | auto m = make_money("20.99"_dec, currency::USD); 56 | 57 | auto ex = exchange_money( 58 | m, 59 | currency::EUR, "0.8649"_dec, 60 | rounding_policy_to_currency_digits(round_half_even())); 61 | ``` 62 | 63 | ### Countries and Currencies 64 | The library provides a full database of ISO recognized countries and currencies and functions to look them up. Information about a country is represented by the `country_unit` class and information about a currency by the `currency_unit` class. Below are several examples for searching these lists: 65 | 66 | ```cpp 67 | // finding a currency 68 | auto cu1 = find_currency("EUR"); 69 | auto cu2 = find_currency(978); 70 | assert(cu1 == cu2); 71 | assert(cu1 == currency::EUR); 72 | assert(cu1.value().code == "EUR"); 73 | ``` 74 | 75 | ```cpp 76 | // finding a country 77 | auto cu1 = find_country("US"); 78 | auto cu2 = find_country(840); 79 | assert(cu1 == cu2); 80 | assert(cu1 == country::US); 81 | assert(cu1.value().alpha2 == "US") 82 | ``` 83 | 84 | ```cpp 85 | // finding the (main) currency of a country 86 | auto cu1 = country::find_country_currency(country::RO); 87 | assert(cu1 == currency::RON); 88 | 89 | auto cu2 = country::find_country_currency(country::US); 90 | assert(cu2 == currency::USD); 91 | ``` 92 | 93 | ```cpp 94 | // finding all the currencies from a country as a set 95 | auto s = country::find_country_currencies(country::US); 96 | assert(s.size() == 2); 97 | assert(*s.begin() == currency::USD); 98 | assert(*std::next(s.begin()) == currency::USN); 99 | ``` 100 | 101 | ```cpp 102 | // finding all the currencies from a country as a range 103 | auto r = country::country_currency_equal_range( 104 | country::currencies, 105 | country::US); 106 | assert(std::distance(r.first, r.second) == 2); 107 | assert(r.first->second == currency::USD); 108 | assert(std::next(r.first)->second == currency::USN); 109 | ``` 110 | 111 | The built-in databases for countries, currencies, and country currencies (available when the `HAS_COUNTRY_AND_CURRENCY_DB` macro is defined) can be extended with additional units. In this case, you can use overloaded versions of these functions that use iterators to define the range to search. The following example shows how to do so with the currencies database, but the same apply for countries (`find_country()` overload) and country currencies (`find_country_currencies()` and `country_currency_equal_range()` overloads): 112 | ```cpp 113 | std::vector my_currencies{ currency::currencies }; 114 | my_currencies.emplace_back(currency_unit{ "VIR", 1001, 2, "Virtual Currency" }); 115 | 116 | auto cu1 = find_currency(std::cbegin(my_currencies), std::cend(my_currencies), "VIR"); 117 | auto cu2 = find_currency(std::cbegin(my_currencies), std::cend(my_currencies), 1001); 118 | 119 | assert(cu1 != std::cend(my_currencies)); 120 | assert(cu1 == cu2); 121 | assert(cu1->alpha2 == "VIR"); 122 | ``` 123 | 124 | ## Rounding 125 | Several rounding algorithms are provided with the library. These algorithms transform a numerical value from a greater precision (e.g. 19.99128) to a lesser precision (e.g. 19.99). In addition to these, any user-defined rounding algorithm can be used with the library. The rounding algorithms, implemented as functors, are as follows: 126 | 127 | | Name | Description | Functor | 128 | | --- | --- | --- | 129 | | None | no rounding | `round_none` | 130 | | Up | rounds away from zero | `round_up` | 131 | | Down | rounds towards zero | `round_down` | 132 | | Ceiling | rounds towards positive infinity | `round_ceiling` | 133 | | Floor | rounds towards negative infinity | `round_floor` | 134 | | Half up | rounds towards "nearest neighbour" unless both neighbours are equidistant, in which case round up | `round_half_up` | 135 | | Half down | rounds towards "nearest neighbour" unless both neighbours are equidistant, in which case round down | `round_half_down` | 136 | | Half even | rounds towards the "nearest neighbour" unless both neighbours are equidistant, in which case, round towards the even neighbour | `round_half_even` | 137 | | Half odd | rounds towards the "nearest neighbour" unless both neighbours are equidistant, in which case, round towards the odd neighbour | `round_half_odd` | 138 | 139 | The following is a table with numerical examples for each rounding algorithm: 140 | 141 | | Algorithm / Value | -5.5 | -2.5 | -1.6 | -1.1 | -1.0 | 1.0 | 1.1 | 1.6 | 2.5 | 5.5 | 142 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 143 | | Up | -6.0 | -3.0 | -2.0 | -2.0 | -1.0 | 1.0 | 2.0 | 2.0 | 3.0 | 6.0 | 144 | | Down | -5.0 | -2.0 | -1.0 | -1.0 | -1.0 | 1.0 | 1.0 | 1.0 | 2.0 | 5.0 | 145 | | Ceiling | -5.0 | -2.0 | -1.0 | -1.0 | -1.0 | 1.0 | 2.0 | 2.0 | 3.0 | 6.0 | 146 | | Floor | -6.0 | -3.0 | -2.0 | -2.0 | -1.0 | 1.0 | 1.0 | 1.0 | 2.0 | 5.0 | 147 | | Half up | -6.0 | -3.0 | -2.0 | -1.0 | -1.0 | 1.0 | 1.0 | 2.0 | 3.0 | 6.0 | 148 | | Half down | -5.0 | -2.0 | -2.0 | -1.0 | -1.0 | 1.0 | 1.0 | 2.0 | 2.0 | 5.0 | 149 | | Half even | -6.0 | -2.0 | -2.0 | -1.0 | -1.0 | 1.0 | 1.0 | 2.0 | 2.0 | 6.0 | 150 | | Half odd | -5.0 | -3.0 | -2.0 | -1.0 | -1.0 | 1.0 | 1.0 | 2.0 | 3.0 | 5.0 | 151 | 152 | More about these rounding algorithms can be found in the article [Rounding Algorithms 101 Redux](https://www.eetimes.com/document.asp?doc_id=1274515). 153 | 154 | Apart from the rounding algorithms, the library provides several rounding policies that define how a `money` value should be rounded. The available policies are: 155 | 156 | | Type name | Description | 157 | | --- | --- | 158 | | `rounding_policy_none`| No rounding is performed | 159 | | `rounding_policy_standard` | Rounding to 4 decimal digits | 160 | | `rounding_policy_to_currency_digits` | Rounding to the number of digits (i.e. minor unit) as defined for the currency | 161 | 162 | Any additional user-defined policy can be used instead of the ones supplied with the library. 163 | 164 | ## Using the library 165 | The library is composed of several headers and uses C++ 17 features (such as string_view, optional, structured bindings). You need a compiler that supports these features. 166 | 167 | The library works with: 168 | * the built-in floating point types, `float`, `double`, and `long double` (NOT ADVICED!) 169 | * `boost::multiprecision` library, with particular specializations for `boost::multiprecision::cpp_dec_float<50>`, aliased as `decimal` 170 | * any 3rd library provided that you specialize the rounding function object templates 171 | 172 | To include the full library of ISO specified currencies and countries you must define the macro `HAS_COUNTRY_AND_CURRENCY_DB`. 173 | 174 | In order to use `boost::multiprecision` you must: 175 | * define the macro `HAS_BOOST_MULTIPRECISION` 176 | * make the path to the `boost` headers available in the include search path 177 | 178 | In order to use `boost::optional` instead of `std::optional` you must: 179 | * define the macro `HAS_BOOST_OPTIONAL` 180 | * make the path to the `boost` headers available in the include search path 181 | * make the path to the `boost` library files available for the libraries search path 182 | 183 | The library is accompanied by unit tests (built with Catch2). CMake is used for creating projects to build and run the unit tests. You can do the following to build it with support for `boost::multiprecision`: 184 | * clone or download and unzip the `moneycpp` library 185 | * create a `build` folder 186 | * download and unzip [Boost](https://www.boost.org/) 187 | * run CMake from the `build` folder 188 | * open the project in the IDE (such as Visual Studio or Xcode), build the project, and run it 189 | 190 | Here is an example for creating a project for VS2017 with `boost` available at `C:\libraries\boost_1_68_0\` (make sure to include the trailing `\`). 191 | ``` 192 | mkdir build 193 | cd build 194 | cmake .. -G "Visual Studio 15 2017" -DCOUNTRY_AND_CURRENCY_DB=ON -DBOOST_MULTIPRECISION=ON -DBOOST_INCLUDE_DIR=C:\libraries\boost_1_68_0\ 195 | ``` 196 | 197 | ## Related projects 198 | The following libraries are used: 199 | * [Catch2](https://github.com/catchorg/Catch2/) - unit testing framework 200 | * [Boost](https://www.boost.org/) - optional (but recommended) library 201 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | 3 | environment: 4 | matrix: 5 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 6 | platform: x86 7 | FLAGS: "" 8 | GENERATOR: Visual Studio 15 2017 9 | 10 | - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 11 | platform: x64 12 | FLAGS: "" 13 | GENERATOR: Visual Studio 15 2017 14 | 15 | init: 16 | - cmake --version 17 | - msbuild /version 18 | 19 | before_build: 20 | - mkdir build 21 | - cd build 22 | - cmake .. -G "%GENERATOR%" -DCMAKE_CXX_FLAGS="%FLAGS%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" -DCOUNTRY_AND_CURRENCY_DB=ON -DBUILD_TESTS=ON 23 | 24 | build_script: 25 | - cmake --build . --config Release 26 | 27 | test_script: 28 | - ctest -C Release -V -j 29 | -------------------------------------------------------------------------------- /catch/catch_reporter_automake.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Justin R. Wilson on 2/19/2017. 3 | * Copyright 2017 Justin R. Wilson. All rights reserved. 4 | * 5 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | */ 8 | #ifndef TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED 9 | #define TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED 10 | 11 | // Don't #include any Catch headers here - we can assume they are already 12 | // included before this header. 13 | // This is not good practice in general but is necessary in this case so this 14 | // file can be distributed as a single header that works with the main 15 | // Catch single header. 16 | 17 | namespace Catch { 18 | 19 | struct AutomakeReporter : StreamingReporterBase { 20 | AutomakeReporter( ReporterConfig const& _config ) 21 | : StreamingReporterBase( _config ) 22 | {} 23 | 24 | ~AutomakeReporter() override; 25 | 26 | static std::string getDescription() { 27 | return "Reports test results in the format of Automake .trs files"; 28 | } 29 | 30 | void assertionStarting( AssertionInfo const& ) override {} 31 | 32 | bool assertionEnded( AssertionStats const& /*_assertionStats*/ ) override { return true; } 33 | 34 | void testCaseEnded( TestCaseStats const& _testCaseStats ) override { 35 | // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. 36 | stream << ":test-result: "; 37 | if (_testCaseStats.totals.assertions.allPassed()) { 38 | stream << "PASS"; 39 | } else if (_testCaseStats.totals.assertions.allOk()) { 40 | stream << "XFAIL"; 41 | } else { 42 | stream << "FAIL"; 43 | } 44 | stream << ' ' << _testCaseStats.testInfo.name << '\n'; 45 | StreamingReporterBase::testCaseEnded( _testCaseStats ); 46 | } 47 | 48 | void skipTest( TestCaseInfo const& testInfo ) override { 49 | stream << ":test-result: SKIP " << testInfo.name << '\n'; 50 | } 51 | 52 | }; 53 | 54 | #ifdef CATCH_IMPL 55 | AutomakeReporter::~AutomakeReporter() {} 56 | #endif 57 | 58 | CATCH_REGISTER_REPORTER( "automake", AutomakeReporter) 59 | 60 | } // end namespace Catch 61 | 62 | #endif // TWOBLUECUBES_CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED 63 | -------------------------------------------------------------------------------- /catch/catch_reporter_tap.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Colton Wolkins on 2015-08-15. 3 | * Copyright 2015 Martin Moene. All rights reserved. 4 | * 5 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | */ 8 | #ifndef TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 9 | #define TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 10 | 11 | 12 | // Don't #include any Catch headers here - we can assume they are already 13 | // included before this header. 14 | // This is not good practice in general but is necessary in this case so this 15 | // file can be distributed as a single header that works with the main 16 | // Catch single header. 17 | 18 | #include 19 | 20 | namespace Catch { 21 | 22 | struct TAPReporter : StreamingReporterBase { 23 | 24 | using StreamingReporterBase::StreamingReporterBase; 25 | 26 | ~TAPReporter() override; 27 | 28 | static std::string getDescription() { 29 | return "Reports test results in TAP format, suitable for test harnesses"; 30 | } 31 | 32 | ReporterPreferences getPreferences() const override { 33 | ReporterPreferences prefs; 34 | prefs.shouldRedirectStdOut = false; 35 | return prefs; 36 | } 37 | 38 | void noMatchingTestCases( std::string const& spec ) override { 39 | stream << "# No test cases matched '" << spec << "'" << std::endl; 40 | } 41 | 42 | void assertionStarting( AssertionInfo const& ) override {} 43 | 44 | bool assertionEnded( AssertionStats const& _assertionStats ) override { 45 | ++counter; 46 | 47 | AssertionPrinter printer( stream, _assertionStats, counter ); 48 | printer.print(); 49 | stream << " # " << currentTestCaseInfo->name ; 50 | 51 | stream << std::endl; 52 | return true; 53 | } 54 | 55 | void testRunEnded( TestRunStats const& _testRunStats ) override { 56 | printTotals( _testRunStats.totals ); 57 | stream << "\n" << std::endl; 58 | StreamingReporterBase::testRunEnded( _testRunStats ); 59 | } 60 | 61 | private: 62 | std::size_t counter = 0; 63 | class AssertionPrinter { 64 | public: 65 | AssertionPrinter& operator= ( AssertionPrinter const& ) = delete; 66 | AssertionPrinter( AssertionPrinter const& ) = delete; 67 | AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter ) 68 | : stream( _stream ) 69 | , result( _stats.assertionResult ) 70 | , messages( _stats.infoMessages ) 71 | , itMessage( _stats.infoMessages.begin() ) 72 | , printInfoMessages( true ) 73 | , counter(_counter) 74 | {} 75 | 76 | void print() { 77 | itMessage = messages.begin(); 78 | 79 | switch( result.getResultType() ) { 80 | case ResultWas::Ok: 81 | printResultType( passedString() ); 82 | printOriginalExpression(); 83 | printReconstructedExpression(); 84 | if ( ! result.hasExpression() ) 85 | printRemainingMessages( Colour::None ); 86 | else 87 | printRemainingMessages(); 88 | break; 89 | case ResultWas::ExpressionFailed: 90 | if (result.isOk()) { 91 | printResultType(passedString()); 92 | } else { 93 | printResultType(failedString()); 94 | } 95 | printOriginalExpression(); 96 | printReconstructedExpression(); 97 | if (result.isOk()) { 98 | printIssue(" # TODO"); 99 | } 100 | printRemainingMessages(); 101 | break; 102 | case ResultWas::ThrewException: 103 | printResultType( failedString() ); 104 | printIssue( "unexpected exception with message:" ); 105 | printMessage(); 106 | printExpressionWas(); 107 | printRemainingMessages(); 108 | break; 109 | case ResultWas::FatalErrorCondition: 110 | printResultType( failedString() ); 111 | printIssue( "fatal error condition with message:" ); 112 | printMessage(); 113 | printExpressionWas(); 114 | printRemainingMessages(); 115 | break; 116 | case ResultWas::DidntThrowException: 117 | printResultType( failedString() ); 118 | printIssue( "expected exception, got none" ); 119 | printExpressionWas(); 120 | printRemainingMessages(); 121 | break; 122 | case ResultWas::Info: 123 | printResultType( "info" ); 124 | printMessage(); 125 | printRemainingMessages(); 126 | break; 127 | case ResultWas::Warning: 128 | printResultType( "warning" ); 129 | printMessage(); 130 | printRemainingMessages(); 131 | break; 132 | case ResultWas::ExplicitFailure: 133 | printResultType( failedString() ); 134 | printIssue( "explicitly" ); 135 | printRemainingMessages( Colour::None ); 136 | break; 137 | // These cases are here to prevent compiler warnings 138 | case ResultWas::Unknown: 139 | case ResultWas::FailureBit: 140 | case ResultWas::Exception: 141 | printResultType( "** internal error **" ); 142 | break; 143 | } 144 | } 145 | 146 | private: 147 | static Colour::Code dimColour() { return Colour::FileName; } 148 | 149 | static const char* failedString() { return "not ok"; } 150 | static const char* passedString() { return "ok"; } 151 | 152 | void printSourceInfo() const { 153 | Colour colourGuard( dimColour() ); 154 | stream << result.getSourceInfo() << ":"; 155 | } 156 | 157 | void printResultType( std::string const& passOrFail ) const { 158 | if( !passOrFail.empty() ) { 159 | stream << passOrFail << ' ' << counter << " -"; 160 | } 161 | } 162 | 163 | void printIssue( std::string const& issue ) const { 164 | stream << " " << issue; 165 | } 166 | 167 | void printExpressionWas() { 168 | if( result.hasExpression() ) { 169 | stream << ";"; 170 | { 171 | Colour colour( dimColour() ); 172 | stream << " expression was:"; 173 | } 174 | printOriginalExpression(); 175 | } 176 | } 177 | 178 | void printOriginalExpression() const { 179 | if( result.hasExpression() ) { 180 | stream << " " << result.getExpression(); 181 | } 182 | } 183 | 184 | void printReconstructedExpression() const { 185 | if( result.hasExpandedExpression() ) { 186 | { 187 | Colour colour( dimColour() ); 188 | stream << " for: "; 189 | } 190 | std::string expr = result.getExpandedExpression(); 191 | std::replace( expr.begin(), expr.end(), '\n', ' '); 192 | stream << expr; 193 | } 194 | } 195 | 196 | void printMessage() { 197 | if ( itMessage != messages.end() ) { 198 | stream << " '" << itMessage->message << "'"; 199 | ++itMessage; 200 | } 201 | } 202 | 203 | void printRemainingMessages( Colour::Code colour = dimColour() ) { 204 | if (itMessage == messages.end()) { 205 | return; 206 | } 207 | 208 | // using messages.end() directly (or auto) yields compilation error: 209 | std::vector::const_iterator itEnd = messages.end(); 210 | const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); 211 | 212 | { 213 | Colour colourGuard( colour ); 214 | stream << " with " << pluralise( N, "message" ) << ":"; 215 | } 216 | 217 | for(; itMessage != itEnd; ) { 218 | // If this assertion is a warning ignore any INFO messages 219 | if( printInfoMessages || itMessage->type != ResultWas::Info ) { 220 | stream << " '" << itMessage->message << "'"; 221 | if ( ++itMessage != itEnd ) { 222 | Colour colourGuard( dimColour() ); 223 | stream << " and"; 224 | } 225 | } 226 | } 227 | } 228 | 229 | private: 230 | std::ostream& stream; 231 | AssertionResult const& result; 232 | std::vector messages; 233 | std::vector::const_iterator itMessage; 234 | bool printInfoMessages; 235 | std::size_t counter; 236 | }; 237 | 238 | void printTotals( const Totals& totals ) const { 239 | if( totals.testCases.total() == 0 ) { 240 | stream << "1..0 # Skipped: No tests ran."; 241 | } else { 242 | stream << "1.." << counter; 243 | } 244 | } 245 | }; 246 | 247 | #ifdef CATCH_IMPL 248 | TAPReporter::~TAPReporter() {} 249 | #endif 250 | 251 | CATCH_REGISTER_REPORTER( "tap", TAPReporter ) 252 | 253 | } // end namespace Catch 254 | 255 | #endif // TWOBLUECUBES_CATCH_REPORTER_TAP_HPP_INCLUDED 256 | -------------------------------------------------------------------------------- /catch/catch_reporter_teamcity.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Phil Nash on 19th December 2014 3 | * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. 4 | * 5 | * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | */ 8 | #ifndef TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED 9 | #define TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED 10 | 11 | // Don't #include any Catch headers here - we can assume they are already 12 | // included before this header. 13 | // This is not good practice in general but is necessary in this case so this 14 | // file can be distributed as a single header that works with the main 15 | // Catch single header. 16 | 17 | #include 18 | 19 | #ifdef __clang__ 20 | # pragma clang diagnostic push 21 | # pragma clang diagnostic ignored "-Wpadded" 22 | #endif 23 | 24 | namespace Catch { 25 | 26 | struct TeamCityReporter : StreamingReporterBase { 27 | TeamCityReporter( ReporterConfig const& _config ) 28 | : StreamingReporterBase( _config ) 29 | { 30 | m_reporterPrefs.shouldRedirectStdOut = true; 31 | } 32 | 33 | static std::string escape( std::string const& str ) { 34 | std::string escaped = str; 35 | replaceInPlace( escaped, "|", "||" ); 36 | replaceInPlace( escaped, "'", "|'" ); 37 | replaceInPlace( escaped, "\n", "|n" ); 38 | replaceInPlace( escaped, "\r", "|r" ); 39 | replaceInPlace( escaped, "[", "|[" ); 40 | replaceInPlace( escaped, "]", "|]" ); 41 | return escaped; 42 | } 43 | ~TeamCityReporter() override; 44 | 45 | static std::string getDescription() { 46 | return "Reports test results as TeamCity service messages"; 47 | } 48 | 49 | void skipTest( TestCaseInfo const& /* testInfo */ ) override { 50 | } 51 | 52 | void noMatchingTestCases( std::string const& /* spec */ ) override {} 53 | 54 | void testGroupStarting( GroupInfo const& groupInfo ) override { 55 | StreamingReporterBase::testGroupStarting( groupInfo ); 56 | stream << "##teamcity[testSuiteStarted name='" 57 | << escape( groupInfo.name ) << "']\n"; 58 | } 59 | void testGroupEnded( TestGroupStats const& testGroupStats ) override { 60 | StreamingReporterBase::testGroupEnded( testGroupStats ); 61 | stream << "##teamcity[testSuiteFinished name='" 62 | << escape( testGroupStats.groupInfo.name ) << "']\n"; 63 | } 64 | 65 | 66 | void assertionStarting( AssertionInfo const& ) override {} 67 | 68 | bool assertionEnded( AssertionStats const& assertionStats ) override { 69 | AssertionResult const& result = assertionStats.assertionResult; 70 | if( !result.isOk() ) { 71 | 72 | ReusableStringStream msg; 73 | if( !m_headerPrintedForThisSection ) 74 | printSectionHeader( msg.get() ); 75 | m_headerPrintedForThisSection = true; 76 | 77 | msg << result.getSourceInfo() << "\n"; 78 | 79 | switch( result.getResultType() ) { 80 | case ResultWas::ExpressionFailed: 81 | msg << "expression failed"; 82 | break; 83 | case ResultWas::ThrewException: 84 | msg << "unexpected exception"; 85 | break; 86 | case ResultWas::FatalErrorCondition: 87 | msg << "fatal error condition"; 88 | break; 89 | case ResultWas::DidntThrowException: 90 | msg << "no exception was thrown where one was expected"; 91 | break; 92 | case ResultWas::ExplicitFailure: 93 | msg << "explicit failure"; 94 | break; 95 | 96 | // We shouldn't get here because of the isOk() test 97 | case ResultWas::Ok: 98 | case ResultWas::Info: 99 | case ResultWas::Warning: 100 | throw std::domain_error( "Internal error in TeamCity reporter" ); 101 | // These cases are here to prevent compiler warnings 102 | case ResultWas::Unknown: 103 | case ResultWas::FailureBit: 104 | case ResultWas::Exception: 105 | throw std::domain_error( "Not implemented" ); 106 | } 107 | if( assertionStats.infoMessages.size() == 1 ) 108 | msg << " with message:"; 109 | if( assertionStats.infoMessages.size() > 1 ) 110 | msg << " with messages:"; 111 | for( auto const& messageInfo : assertionStats.infoMessages ) 112 | msg << "\n \"" << messageInfo.message << "\""; 113 | 114 | 115 | if( result.hasExpression() ) { 116 | msg << 117 | "\n " << result.getExpressionInMacro() << "\n" 118 | "with expansion:\n" << 119 | " " << result.getExpandedExpression() << "\n"; 120 | } 121 | 122 | if( currentTestCaseInfo->okToFail() ) { 123 | msg << "- failure ignore as test marked as 'ok to fail'\n"; 124 | stream << "##teamcity[testIgnored" 125 | << " name='" << escape( currentTestCaseInfo->name )<< "'" 126 | << " message='" << escape( msg.str() ) << "'" 127 | << "]\n"; 128 | } 129 | else { 130 | stream << "##teamcity[testFailed" 131 | << " name='" << escape( currentTestCaseInfo->name )<< "'" 132 | << " message='" << escape( msg.str() ) << "'" 133 | << "]\n"; 134 | } 135 | } 136 | stream.flush(); 137 | return true; 138 | } 139 | 140 | void sectionStarting( SectionInfo const& sectionInfo ) override { 141 | m_headerPrintedForThisSection = false; 142 | StreamingReporterBase::sectionStarting( sectionInfo ); 143 | } 144 | 145 | void testCaseStarting( TestCaseInfo const& testInfo ) override { 146 | m_testTimer.start(); 147 | StreamingReporterBase::testCaseStarting( testInfo ); 148 | stream << "##teamcity[testStarted name='" 149 | << escape( testInfo.name ) << "']\n"; 150 | stream.flush(); 151 | } 152 | 153 | void testCaseEnded( TestCaseStats const& testCaseStats ) override { 154 | StreamingReporterBase::testCaseEnded( testCaseStats ); 155 | if( !testCaseStats.stdOut.empty() ) 156 | stream << "##teamcity[testStdOut name='" 157 | << escape( testCaseStats.testInfo.name ) 158 | << "' out='" << escape( testCaseStats.stdOut ) << "']\n"; 159 | if( !testCaseStats.stdErr.empty() ) 160 | stream << "##teamcity[testStdErr name='" 161 | << escape( testCaseStats.testInfo.name ) 162 | << "' out='" << escape( testCaseStats.stdErr ) << "']\n"; 163 | stream << "##teamcity[testFinished name='" 164 | << escape( testCaseStats.testInfo.name ) << "' duration='" 165 | << m_testTimer.getElapsedMilliseconds() << "']\n"; 166 | stream.flush(); 167 | } 168 | 169 | private: 170 | void printSectionHeader( std::ostream& os ) { 171 | assert( !m_sectionStack.empty() ); 172 | 173 | if( m_sectionStack.size() > 1 ) { 174 | os << getLineOfChars<'-'>() << "\n"; 175 | 176 | std::vector::const_iterator 177 | it = m_sectionStack.begin()+1, // Skip first section (test case) 178 | itEnd = m_sectionStack.end(); 179 | for( ; it != itEnd; ++it ) 180 | printHeaderString( os, it->name ); 181 | os << getLineOfChars<'-'>() << "\n"; 182 | } 183 | 184 | SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; 185 | 186 | if( !lineInfo.empty() ) 187 | os << lineInfo << "\n"; 188 | os << getLineOfChars<'.'>() << "\n\n"; 189 | } 190 | 191 | // if string has a : in first line will set indent to follow it on 192 | // subsequent lines 193 | static void printHeaderString( std::ostream& os, std::string const& _string, std::size_t indent = 0 ) { 194 | std::size_t i = _string.find( ": " ); 195 | if( i != std::string::npos ) 196 | i+=2; 197 | else 198 | i = 0; 199 | os << Column( _string ) 200 | .indent( indent+i) 201 | .initialIndent( indent ) << "\n"; 202 | } 203 | private: 204 | bool m_headerPrintedForThisSection = false; 205 | Timer m_testTimer; 206 | }; 207 | 208 | #ifdef CATCH_IMPL 209 | TeamCityReporter::~TeamCityReporter() {} 210 | #endif 211 | 212 | CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter ) 213 | 214 | } // end namespace Catch 215 | 216 | #ifdef __clang__ 217 | # pragma clang diagnostic pop 218 | #endif 219 | 220 | #endif // TWOBLUECUBES_CATCH_REPORTER_TEAMCITY_HPP_INCLUDED 221 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake, tools 2 | 3 | 4 | class MoneycppConan(ConanFile): 5 | name = "moneycpp" 6 | version = "2021-04-24" 7 | license = "MIT License" 8 | author = "Marius Bancila https://github.com/mariusbancila" 9 | url = "https://github.com/mariusbancila/moneycpp" 10 | description = "A C++ 17 header-only, cross-platform library for handling monetary values, currencies, rounding " \ 11 | "and other related features. " 12 | topics = ("monetary", "currency", "rounding") 13 | settings = "os", "compiler", "build_type", "arch" 14 | options = {"shared": [True, False], "fPIC": [True, False]} 15 | default_options = {"shared": False, "fPIC": True} 16 | generators = "cmake" 17 | exports_sources = "include/*" 18 | 19 | def config_options(self): 20 | if self.settings.os == "Windows": 21 | del self.options.fPIC 22 | 23 | def package(self): 24 | self.copy("*.h", src="include", dst="include/moneycpp") 25 | 26 | def build(self): 27 | # header only, nothing to build, add method to prevent warning from conan 28 | yield 29 | -------------------------------------------------------------------------------- /include/country.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef HAS_BOOST_OPTIONAL 7 | # include 8 | using boost::optional; 9 | #else 10 | # include 11 | using std::optional; 12 | #endif 13 | 14 | namespace moneycpp 15 | { 16 | struct country_unit 17 | { 18 | int const code; 19 | std::string_view const alpha2; 20 | std::string_view const alpha3; 21 | std::string_view const name; 22 | bool const independent; 23 | }; 24 | 25 | inline constexpr bool operator==(country_unit const & lhs, country_unit const & rhs) noexcept 26 | { 27 | return 28 | lhs.code == rhs.code && 29 | lhs.alpha2 == rhs.alpha2 && 30 | lhs.alpha3 == rhs.alpha3; 31 | } 32 | 33 | inline constexpr bool operator!=(country_unit const & lhs, country_unit const & rhs) noexcept 34 | { 35 | return !(lhs == rhs); 36 | } 37 | 38 | inline constexpr bool operator<(country_unit const & lhs, country_unit const & rhs) noexcept 39 | { 40 | return lhs.code < rhs.code; 41 | } 42 | 43 | template 44 | inline Iter find_country(Iter first, Iter last, int const code) 45 | { 46 | for (auto it = first; it != last; ++it) 47 | { 48 | if (it->code == code) 49 | return it; 50 | } 51 | 52 | return last; 53 | } 54 | 55 | template 56 | inline Iter find_country(Iter first, Iter last, std::string_view alpha2) 57 | { 58 | for (auto it = first; it != last; ++it) 59 | { 60 | if (it->alpha2 == alpha2) 61 | return it; 62 | } 63 | 64 | return last; 65 | } 66 | 67 | #ifdef HAS_COUNTRY_AND_CURRENCY_DB 68 | 69 | namespace country 70 | { 71 | // ISO 3166-1 country codes 72 | constexpr country_unit AF { 4, "AF", "AFG", "Afganistan", true }; 73 | constexpr country_unit AX { 248, "AX", "ALA", "Aland Islands", false }; 74 | constexpr country_unit AL { 8, "AL", "ALB", "Albania", true }; 75 | constexpr country_unit DZ { 12, "DL", "DZA", "Algeria", true }; 76 | constexpr country_unit AS { 16, "AS", "ASM", "American Samoa", false }; 77 | constexpr country_unit AD { 20, "AD", "AND", "Andorra", true }; 78 | constexpr country_unit AO { 24, "AO", "AGO", "Angola", true }; 79 | constexpr country_unit AI { 660, "AI", "AIA", "Anguilla", false }; 80 | constexpr country_unit AQ { 10, "AQ", "ATA", "Antartica", false }; 81 | constexpr country_unit AG { 28, "AG", "ATG", "Antigua and Barbuda", true }; 82 | constexpr country_unit AR { 32, "AR", "ARG", "Argentina", true }; 83 | constexpr country_unit AM { 51, "AM", "ARM", "Armenia", true }; 84 | constexpr country_unit AW { 533, "AW", "ABW", "Aruba", false }; 85 | constexpr country_unit AU { 36, "AU", "AUS", "Australia", true }; 86 | constexpr country_unit AT { 40, "AT", "AUT", "Austria", true }; 87 | constexpr country_unit AZ { 31, "AZ", "AZE", "Azerbaijan", true }; 88 | constexpr country_unit BS { 44, "BS", "BHS", "Bahamas", true }; 89 | constexpr country_unit BH { 48, "BH", "BHR", "Bahrain", true }; 90 | constexpr country_unit BD { 50, "BD", "BGD", "Bangladesh", true }; 91 | constexpr country_unit BB { 52, "BB", "BRB", "Barbados", true }; 92 | constexpr country_unit BY { 112, "BY", "BLR", "Belarus", true }; 93 | constexpr country_unit BE { 56, "BE", "BEL", "Belgium", true }; 94 | constexpr country_unit BZ { 84, "BZ", "BLZ", "Belize", true }; 95 | constexpr country_unit BJ { 204, "BJ", "BEN", "Benin", true }; 96 | constexpr country_unit BM { 60, "BM", "BMU", "Bermuda", false }; 97 | constexpr country_unit BT { 64, "BT", "BTN", "Bhutan", true }; 98 | constexpr country_unit BO { 68, "BO", "BOL", "Bolivia", true }; 99 | constexpr country_unit BQ { 535, "BQ", "BES", "Bonaire, Sint Eustatius and Saba", false }; 100 | constexpr country_unit BA { 70, "BA", "BIH", "Bosnia and Herzegovina", true }; 101 | constexpr country_unit BW { 72, "BW", "BWA", "Botswana", true }; 102 | constexpr country_unit BV { 74, "BV", "BVT", "Bouvet Island", false }; 103 | constexpr country_unit BR { 76, "BR", "BRA", "Brazil", true }; 104 | constexpr country_unit IO { 86, "IO", "IOT", "British Indian Ocean Teritory", false }; 105 | constexpr country_unit BN { 96, "BN", "BRN", "Brunei Darussalam", true }; 106 | constexpr country_unit BG { 100, "BG", "BGR", "Bulgaria", true }; 107 | constexpr country_unit BF { 854, "BF", "BFA", "Burkina Faso", true }; 108 | constexpr country_unit BI { 108, "BI", "BDI", "Burundi", true }; 109 | constexpr country_unit CV { 132, "CV", "CPV", "Cabo Verde", true }; 110 | constexpr country_unit KH { 116, "KH", "KHM", "Cambodia", true }; 111 | constexpr country_unit CM { 120, "CM", "CMR", "Cameroon", true }; 112 | constexpr country_unit CA { 124, "CA", "CAN", "Canada", true }; 113 | constexpr country_unit KY { 136, "KY", "CYM", "Cayman Islands", false }; 114 | constexpr country_unit CF { 140, "CF", "CAF", "Central African Republic", true }; 115 | constexpr country_unit TD { 148, "TD", "TDC", "Chad", true }; 116 | constexpr country_unit CL { 152, "CL", "CHL", "Chile", true }; 117 | constexpr country_unit CN { 156, "CN", "CHN", "China", true }; 118 | constexpr country_unit CX { 162, "CX", "CXR", "Christmas Island", false }; 119 | constexpr country_unit CC { 166, "CC", "CCK", "Cocos Islands", false }; 120 | constexpr country_unit CO { 170, "CO", "COL", "Colombia", true }; 121 | constexpr country_unit KM { 174, "KM", "COM", "Comoros", true }; 122 | constexpr country_unit CG { 178, "CG", "COG", "Congo", true }; 123 | constexpr country_unit CD { 180, "CD", "COD", "Congo (Democratic Republic of the)", true }; 124 | constexpr country_unit CK { 184, "CK", "COK", "Cook Islands", false }; 125 | constexpr country_unit CR { 188, "CR", "CRI", "Costa Rica", true }; 126 | constexpr country_unit CI { 384, "CI", "CIV", "Côte d'Ivoire", true }; 127 | constexpr country_unit HR { 191, "HR", "HRV", "Croatia", true }; 128 | constexpr country_unit CU { 192, "CU", "CUB", "Cuba", true }; 129 | constexpr country_unit CW { 531, "CW", "CUW", "Curaçao", false }; 130 | constexpr country_unit CY { 196, "CY", "CYP", "Cyprus", true }; 131 | constexpr country_unit CZ { 203, "CZ", "CZE", "Czechia", true }; 132 | constexpr country_unit DK { 208, "DK", "DNK", "Demark", true }; 133 | constexpr country_unit DJ { 262, "DJ", "DJI", "Djibouti", true }; 134 | constexpr country_unit DM { 212, "DM", "DMA", "Dominica", true }; 135 | constexpr country_unit DO { 214, "DO", "DOM", "Dominican Republic", true }; 136 | constexpr country_unit EC { 218, "EC", "ECU", "Ecuador", true }; 137 | constexpr country_unit EG { 818, "EG", "EGY", "Egypt", true }; 138 | constexpr country_unit SV { 222, "SV", "SLV", "El Salvador", true }; 139 | constexpr country_unit GQ { 226, "GQ", "GNQ", "Equatorial Guinea", true }; 140 | constexpr country_unit ER { 232, "ER", "ERI", "Eritrea", true }; 141 | constexpr country_unit EE { 233, "EE", "EST", "Estonia", true }; 142 | constexpr country_unit SZ { 748, "SZ", "SWZ", "Eswatini", true }; 143 | constexpr country_unit ET { 231, "ET", "ETH", "Ethiopia", true }; 144 | constexpr country_unit FK { 238, "FK", "FLK", "Falkland Islands", false }; 145 | constexpr country_unit FO { 234, "FO", "FRO", "Faroe Islands", false }; 146 | constexpr country_unit FJ { 242, "FJ", "FJI", "Fiji", true }; 147 | constexpr country_unit FI { 246, "FI", "FIN", "Finland", true }; 148 | constexpr country_unit FR { 250, "FR", "FRA", "France", true }; 149 | constexpr country_unit GF { 254, "GF", "GUF", "French Guiana", false }; 150 | constexpr country_unit PF { 258, "PF", "PYF", "French Polynesia", false }; 151 | constexpr country_unit TF { 260, "TF", "ATF", "French Southern Territories", false }; 152 | constexpr country_unit GA { 266, "GA", "GAB", "Gabon", true }; 153 | constexpr country_unit GM { 270, "GM", "GMB", "Gambia", true }; 154 | constexpr country_unit GE { 268, "GE", "GEO", "Georgia", true }; 155 | constexpr country_unit DE { 276, "DE", "DEU", "Germany", true }; 156 | constexpr country_unit GH { 288, "GH", "GHA", "Ghana", true }; 157 | constexpr country_unit GI { 292, "GI", "GIB", "Gibraltar", false }; 158 | constexpr country_unit GR { 300, "GR", "GRC", "Greece", true }; 159 | constexpr country_unit GL { 304, "GL", "GRL", "Greenland", false }; 160 | constexpr country_unit GD { 308, "GD", "GRD", "Grenada", true }; 161 | constexpr country_unit GP { 312, "GP", "GLP", "Guadeloupe", false }; 162 | constexpr country_unit GU { 316, "GU", "GUM", "Guam", false }; 163 | constexpr country_unit GT { 320, "GT", "GTM", "Guatemala", true }; 164 | constexpr country_unit GG { 831, "GG", "GGY", "Guernsey", false }; 165 | constexpr country_unit GN { 324, "GN", "GIN", "Guinea", true }; 166 | constexpr country_unit GW { 624, "GW", "GNB", "Guinea-Bissau", true }; 167 | constexpr country_unit GY { 328, "GY", "GUY", "Guyana", true }; 168 | constexpr country_unit HT { 332, "HT", "HTI", "Haiti", true }; 169 | constexpr country_unit HM { 334, "HM", "HMD", "Heard Island and McDonald Islands", false }; 170 | constexpr country_unit VA { 336, "VA", "VAT", "Vatican", true }; 171 | constexpr country_unit HN { 340, "HN", "HND", "Honduras", true }; 172 | constexpr country_unit HK { 344, "HK", "HKG", "Hong Kong", false }; 173 | constexpr country_unit HU { 348, "HU", "HUN", "Hungary", true }; 174 | constexpr country_unit IS { 352, "IS", "ISL", "Iceland", true }; 175 | constexpr country_unit IN { 356, "IN", "IND", "India", true }; 176 | constexpr country_unit ID { 360, "ID", "IDN", "Indonesia", true }; 177 | constexpr country_unit IR { 364, "IR", "IRN", "Iran", true }; 178 | constexpr country_unit IQ { 368, "IQ", "IRQ", "Iraq", true }; 179 | constexpr country_unit IE { 372, "IE", "IRL", "Ireland", true }; 180 | constexpr country_unit IM { 833, "IM", "IMN", "Isle of Man", false }; 181 | constexpr country_unit IL { 376, "IL", "ISR", "Israel", true }; 182 | constexpr country_unit IT { 380, "IT", "ITA", "Italy", true }; 183 | constexpr country_unit JM { 388, "JM", "JAM", "Jamaica", true }; 184 | constexpr country_unit JP { 392, "JP", "JPN", "Japan", true }; 185 | constexpr country_unit JE { 832, "JE", "JEY", "Jersey", false }; 186 | constexpr country_unit JO { 400, "JO", "JOR", "Jordan", true }; 187 | constexpr country_unit KZ { 398, "KZ", "KAZ", "Kazakhstan", true }; 188 | constexpr country_unit KE { 404, "KE", "KEN", "Kenya", true }; 189 | constexpr country_unit KI { 296, "KI", "KIR", "Kiribati", true }; 190 | constexpr country_unit KP { 408, "KP", "PRK", "Democratic People's Republic of Korea", true }; 191 | constexpr country_unit KR { 410, "KR", "KOR", "Republic of Korea", true }; 192 | constexpr country_unit KW { 414, "KW", "KWT", "Kuwait", true }; 193 | constexpr country_unit KG { 417, "KG", "KGZ", "Kyrgystan", true }; 194 | constexpr country_unit LA { 418, "LA", "LAO", "Lao", true }; 195 | constexpr country_unit LV { 428, "LV", "LVA", "Latvia", true }; 196 | constexpr country_unit LB { 422, "LB", "LBN", "Lebanon", true }; 197 | constexpr country_unit LS { 426, "LS", "LSO", "Lesotho", true }; 198 | constexpr country_unit LR { 430, "LR", "LBR", "Liberia", true }; 199 | constexpr country_unit LY { 434, "LY", "LBY", "Libya", true }; 200 | constexpr country_unit LI { 438, "LI", "LIE", "Liechtenstein", true }; 201 | constexpr country_unit LT { 440, "LT", "LTU", "Lithuania", true }; 202 | constexpr country_unit LU { 442, "LU", "LUX", "Luxembourg", true }; 203 | constexpr country_unit MO { 446, "MO", "MAC", "Macao", false }; 204 | constexpr country_unit MK { 807, "MK", "MKD", "Macedonia", true }; 205 | constexpr country_unit MG { 450, "MG", "MDG", "Madagascar", true }; 206 | constexpr country_unit MW { 454, "MW", "MWI", "Malawi", true }; 207 | constexpr country_unit MY { 458, "MY", "MYS", "Malaysia", true }; 208 | constexpr country_unit MV { 462, "MV", "MDV", "Maldives", true }; 209 | constexpr country_unit ML { 466, "ML", "MLI", "Mali", true }; 210 | constexpr country_unit MT { 470, "MT", "MLT", "Malta", true }; 211 | constexpr country_unit MH { 584, "MH", "MHL", "Marshall Islands", true }; 212 | constexpr country_unit MQ { 474, "MQ", "MTQ", "Martinique", false }; 213 | constexpr country_unit MR { 478, "MR", "MRT", "Mauritania", true }; 214 | constexpr country_unit MU { 480, "MU", "MUS", "Mauritius", true }; 215 | constexpr country_unit YT { 175, "YT", "MYT", "Mayotte", false }; 216 | constexpr country_unit MX { 484, "MX", "MEX", "Mexico", true }; 217 | constexpr country_unit FM { 583, "FM", "FSM", "Micronesia", true }; 218 | constexpr country_unit MD { 498, "MD", "MDA", "Moldova", true }; 219 | constexpr country_unit MC { 492, "MC", "MCO", "Monaco", true }; 220 | constexpr country_unit MN { 496, "MN", "MNG", "Mongolia", true }; 221 | constexpr country_unit ME { 499, "ME", "MNE", "Montenegro", true }; 222 | constexpr country_unit MS { 500, "MS", "MSR", "Montserrat", false }; 223 | constexpr country_unit MA { 504, "MA", "MAR", "Morocco", true }; 224 | constexpr country_unit MZ { 508, "MZ", "MOZ", "Mozambique", true }; 225 | constexpr country_unit MM { 104, "MM", "MMR", "Myanmar", true }; 226 | constexpr country_unit NA { 516, "NA", "NAM", "Namibia", true }; 227 | constexpr country_unit NR { 520, "NR", "NRU", "Nauru", true }; 228 | constexpr country_unit NP { 524, "NP", "NPL", "Nepal", true }; 229 | constexpr country_unit NL { 528, "NL", "NLD", "Netherlands", true }; 230 | constexpr country_unit NC { 540, "NC", "NCL", "New Caledonia", false }; 231 | constexpr country_unit NZ { 554, "NZ", "NZL", "New Zealand", true }; 232 | constexpr country_unit NI { 558, "NI", "NIC", "Nicaragua", true }; 233 | constexpr country_unit NE { 562, "NE", "NER", "Niger", true }; 234 | constexpr country_unit NG { 566, "NG", "NGA", "Nigeria", true }; 235 | constexpr country_unit NU { 570, "NU", "NIU", "Niue", false }; 236 | constexpr country_unit NF { 574, "NF", "NFK", "Norfolk Island", false }; 237 | constexpr country_unit MP { 580, "MP", "MNP", "Northern Mariana Islands", false }; 238 | constexpr country_unit NO { 578, "NO", "NOR", "Norway", true }; 239 | constexpr country_unit OM { 512, "OM", "OMN", "Oman", true }; 240 | constexpr country_unit PK { 586, "PK", "PAK", "Pakistan", true }; 241 | constexpr country_unit PW { 585, "PW", "PLW", "Palau", true }; 242 | constexpr country_unit PS { 275, "PS", "PSE", "Palestine", false }; 243 | constexpr country_unit PA { 591, "PA", "PAN", "Panama", true }; 244 | constexpr country_unit PG { 598, "PG", "PNG", "Papua New Guinea", true }; 245 | constexpr country_unit PY { 600, "PY", "PRY", "Paraguay", true }; 246 | constexpr country_unit PE { 604, "PE", "PER", "Peru", true }; 247 | constexpr country_unit PH { 608, "PH", "PHL", "Philippines", true }; 248 | constexpr country_unit PN { 612, "PN", "PCN", "Pitcairn", false }; 249 | constexpr country_unit PL { 616, "PL", "POL", "Poland", true }; 250 | constexpr country_unit PT { 620, "PT", "PRT", "Portugal", true }; 251 | constexpr country_unit PR { 630, "PR", "PRI", "Puerto Rico", false }; 252 | constexpr country_unit QA { 634, "QA", "QAT", "Qatar", true }; 253 | constexpr country_unit RE { 638, "RE", "REU", "Reunion", false }; 254 | constexpr country_unit RO { 642, "RO", "ROU", "Romania", true }; 255 | constexpr country_unit RU { 643, "RU", "RUS", "Russian Federation", true }; 256 | constexpr country_unit RW { 646, "RW", "RWA", "Rwanda", true }; 257 | constexpr country_unit BL { 652, "BL", "BLM", "Saint Barthelemy", false }; 258 | constexpr country_unit SH { 654, "SH", "SHN", "Saint Helena, Ascension and Tristan da Cunha", false }; 259 | constexpr country_unit KN { 659, "KN", "KNA", "Saint Kitts and Nevis", true }; 260 | constexpr country_unit LC { 662, "LC", "LCA", "Saint Lucia", true }; 261 | constexpr country_unit MF { 663, "MF", "MAF", "Saint Martin", false }; 262 | constexpr country_unit PM { 666, "PM", "SPM", "Saint Pierre and Miquelon", false }; 263 | constexpr country_unit VC { 670, "VC", "VCT", "Saint Vincent and the Grenadines", true }; 264 | constexpr country_unit WS { 882, "WS", "WSM", "Samoa", true }; 265 | constexpr country_unit SM { 674, "SM", "SMR", "San Marino", true }; 266 | constexpr country_unit ST { 678, "ST", "STP", "San Tome and Principe", true }; 267 | constexpr country_unit SA { 682, "SA", "SAU", "Saudi Arabia", true }; 268 | constexpr country_unit SN { 686, "SN", "Sen", "Senegal", true }; 269 | constexpr country_unit RS { 688, "RS", "SRB", "Serbia", true }; 270 | constexpr country_unit SC { 690, "SC", "Syc", "Seychelles", true }; 271 | constexpr country_unit SL { 694, "SL", "SLE", "Sierra Leone", true }; 272 | constexpr country_unit SG { 702, "SG", "SGP", "Singapore", true }; 273 | constexpr country_unit SX { 534, "SX", "SXM", "Sint Maarten", false }; 274 | constexpr country_unit SK { 703, "SK", "SVK", "Slovakia", true }; 275 | constexpr country_unit SI { 705, "SI", "SVN", "Slovenia", true }; 276 | constexpr country_unit SB { 90, "SB", "SLB", "Solomon Islands", true }; 277 | constexpr country_unit SO { 706, "SO", "SOM", "Somalia", true }; 278 | constexpr country_unit ZA { 710, "ZA", "ZAF", "South Africa", true }; 279 | constexpr country_unit GS { 239, "GS", "SGS", "South Georgia and the South Sandwich Islands", false }; 280 | constexpr country_unit SS { 728, "SS", "SSD", "South Sudan", true }; 281 | constexpr country_unit ES { 724, "ES", "ESP", "Spain", true }; 282 | constexpr country_unit LK { 144, "LK", "LKA", "Sri Lanka", true }; 283 | constexpr country_unit SD { 729, "SD", "SDN", "Sudan", true }; 284 | constexpr country_unit SR { 740, "SR", "SUR", "Suriname", true }; 285 | constexpr country_unit SJ { 744, "SJ", "SJM", "Svalbard and Jan Mayen", false }; 286 | constexpr country_unit SE { 752, "SE", "SWE", "Sweden", true }; 287 | constexpr country_unit CH { 756, "CH", "CHE", "Switzerland", true }; 288 | constexpr country_unit SY { 760, "SY", "SYR", "Syrian Arab Republic", true }; 289 | constexpr country_unit TW { 158, "TW", "TWN", "Taiwan", false }; 290 | constexpr country_unit TJ { 762, "TJ", "TJK", "Tajikistan", true }; 291 | constexpr country_unit TZ { 834, "TZ", "TZA", "Tanzania", true }; 292 | constexpr country_unit TH { 764, "TH", "THA", "Thailand", true }; 293 | constexpr country_unit TL { 626, "TL", "TLS", "Timor-Leste", true }; 294 | constexpr country_unit TG { 768, "TG", "TGO", "Togo", true }; 295 | constexpr country_unit TK { 772, "TK", "TKL", "Tokelau", false }; 296 | constexpr country_unit TO { 776, "TO", "TON", "Tonga", true }; 297 | constexpr country_unit TT { 780, "TT", "TTO", "Trinidad and Tobago", true }; 298 | constexpr country_unit TN { 788, "TN", "TUN", "Tunisia", true }; 299 | constexpr country_unit TR { 792, "TR", "TUR", "Turkey", true }; 300 | constexpr country_unit TM { 795, "TM", "TKM", "Turkmenistan", true }; 301 | constexpr country_unit TC { 796, "TC", "TCA", "Turks and Caicos Islands", false }; 302 | constexpr country_unit TV { 798, "TV", "TUV", "Tuvalu", true }; 303 | constexpr country_unit UG { 800, "UG", "UGA", "Uganda", true }; 304 | constexpr country_unit UA { 804, "UA", "UKR", "Ukraine", true }; 305 | constexpr country_unit AE { 784, "AE", "ARE", "United Arab Emirates", true }; 306 | constexpr country_unit GB { 826, "GB", "GBR", "United Kingdom of Great Britain and Northern Ireland", true }; 307 | constexpr country_unit US { 840, "US", "USA", "United States of America", true }; 308 | constexpr country_unit UM { 581, "UM", "UMI", "United States Minor Outlying Islands", false }; 309 | constexpr country_unit UY { 858, "UY", "URY", "Uruguay", true }; 310 | constexpr country_unit UZ { 860, "UZ", "UZB", "Uzbekistan", true }; 311 | constexpr country_unit VU { 548, "VU", "VUT", "Vanuatu", true }; 312 | constexpr country_unit VE { 862, "VE", "VEN", "Venezuela", true }; 313 | constexpr country_unit VN { 704, "VN", "VNM", "Viet Nam", true }; 314 | constexpr country_unit VG { 92, "VG", "VGB", "Virgin Islands (British)", false }; 315 | constexpr country_unit VI { 850, "VI", "VIR", "Virgin Islands (U.S.)", false }; 316 | constexpr country_unit WF { 876, "WF", "WLF", "Wallis and Furtuna", false }; 317 | constexpr country_unit EH { 732, "EH", "ESH", "Western Sahara", false }; 318 | constexpr country_unit YE { 887, "YE", "YEM", "Yemen", true }; 319 | constexpr country_unit ZM { 894, "ZM", "ZMB", "Zambia", true }; 320 | constexpr country_unit ZW { 716, "ZW", "ZWE", "Zimbabwe", true }; 321 | 322 | static const std::initializer_list countries 323 | { 324 | AF, AX, AL, AS, AD, AO, AI, AQ, AG, AR, AM, AW, AU, AT, AZ, AE, 325 | BS, BH, BD, BB, BY, BE, BZ, BJ, BM, BT, BO, BQ, BA, BW, BV, BR, BN, BG, BF, BI, BL, 326 | CV, CM, CA, CF, CL, CN, CX, CC, CO, CG, CD, CK, CR, CI, CU, CW, CY, CZ, CH, 327 | DZ, DK, DJ, DM, DO, DE, 328 | EC, EG, ER, EE, ET, ES, EH, 329 | FK, FO, FJ, FI, FR, FM, 330 | GQ, GF, GA, GM, GE, GH, GI, GR, GL, GD, GP, GU, GT, GG, GN, GW, GY, GS, GB, 331 | HR, HT, HM, HN, HK, HU, 332 | IO, IS, IN, ID, IR, IQ, IE, IM, IL, IT, 333 | JM, JP, JE, JO, 334 | KH, KY, KM, KZ, KE, KI, KP, KR, KW, KG, KN, 335 | LA, LV, LB, LS, LR, LY, LI, LT, LU, LC, LK, 336 | MO, MK, MG, MW, MY, MV, ML, MT, MH, MQ, MR, MU, MX, MD, MC, MN, ME, MS, MA, MZ, MM, MP, MF, 337 | NA, NR, NP, NL, NC, NZ, NI, NE, NG, NU, NF, NO, 338 | OM, 339 | PF, PK, PW, PS, PA, PG, PY, PE, PH, PN, PL, PT, PR, PM, 340 | QA, 341 | RE, RO, RU, RW, RS, 342 | SV, SZ, SH, SM, ST, SA, SN, SC, SL, SG, SX, SK, SI, SB, SO, SS, SD, SR, SJ, SE, SY, 343 | TD, TF, TW, TJ, TZ, TH, TL, TG, TK, TO, TT, TN, TR, TM, TC, TV, 344 | UA, UG, US, UM, UY, UZ, 345 | VA, VC, VU, VE, VN, VG, VI, 346 | YT, YE, 347 | WS, WF, 348 | ZA, ZM, ZW, 349 | }; 350 | } 351 | 352 | inline optional find_country(int const code) 353 | { 354 | #ifdef HAS_COUNTRY_AND_CURRENCY_DB 355 | auto it = find_country( 356 | std::cbegin(country::countries), 357 | std::cend(country::countries), 358 | code); 359 | 360 | if (it != std::cend(country::countries)) 361 | return optional{ *it }; 362 | #endif 363 | 364 | return {}; 365 | } 366 | 367 | inline optional find_country(std::string_view alpha2) 368 | { 369 | #ifdef HAS_COUNTRY_AND_CURRENCY_DB 370 | auto it = find_country( 371 | std::cbegin(country::countries), 372 | std::cend(country::countries), 373 | alpha2); 374 | 375 | if (it != std::cend(country::countries)) 376 | return optional{ *it }; 377 | #endif 378 | return {}; 379 | } 380 | 381 | #endif 382 | } -------------------------------------------------------------------------------- /include/country_currency.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "country.h" 4 | #include "currency.h" 5 | #include 6 | #include 7 | 8 | namespace moneycpp 9 | { 10 | using country_currency_map = std::multimap; 11 | 12 | namespace country 13 | { 14 | using namespace currency; 15 | 16 | template 17 | inline std::set find_country_currencies( 18 | Map const & map, 19 | country_unit const & cu) 20 | { 21 | std::set result; 22 | auto range = map.equal_range(cu); 23 | for (auto it = range.first; it != range.second; ++it) 24 | { 25 | result.insert(it->second); 26 | } 27 | return result; 28 | } 29 | 30 | template 31 | inline std::pair country_currency_equal_range( 32 | Map const & map, 33 | country_unit const & cu) 34 | { 35 | return map.equal_range(cu); 36 | } 37 | 38 | #ifdef HAS_COUNTRY_AND_CURRENCY_DB 39 | 40 | // compiled from the following sources 41 | // https://www.iban.com/currency-codes 42 | // https://www.countries-ofthe-world.com/world-currencies.html 43 | static const country_currency_map currencies 44 | { 45 | // A 46 | {AF, AFN}, // Afghanistan 47 | {AX, EUR}, // Aland Islands (Finland) 48 | {AL, ALL}, // Albania 49 | {DZ, DZD}, // Algeria 50 | {AS, USD}, // American Samoa (USA) 51 | {AD, EUR}, // Andorra 52 | {AO, AOA}, // Angola 53 | {AI, XCD}, // Anguilla (UK) 54 | {AG, XCD}, // Antigua and Barbuda 55 | {AR, ARS}, // Argentina 56 | {AM, AMD}, // Armenia 57 | {AW, AWG}, // Aruba (Netherlands) 58 | {AU, AUD}, // Australia 59 | {AT, EUR}, // Austria 60 | {AZ, AZN}, // Azerbaijan 61 | 62 | // B 63 | {BS, BSD}, // Bahamas 64 | {BH, BHD}, // Bahrain 65 | {BD, BDT}, // Bangladesh 66 | {BB, BBD}, // Barbados 67 | {BY, BYN}, // Belarus 68 | {BE, EUR}, // Belgium 69 | {BZ, BZD}, // Belize 70 | {BJ, XOF}, // Benin 71 | {BM, BMD}, // Bermuda (UK) 72 | {BT, BTN}, // Bhutan 73 | {BT, INR}, // Bhutan 74 | {BO, BOB}, // Bolivia 75 | {BO, BOV}, // Bolivia 76 | {BQ, USD}, // Bonaire, Sint Eustatius and Saba (Netherlands) 77 | {BA, BAM}, // Bosnia and Herzegovina 78 | {BW, BWP}, // Botswana 79 | {BV, NOK}, // Bouvet Island (NO) 80 | {BR, BRL}, // Brazil 81 | {IO, USD}, // British Indian Ocean Territory (UK) 82 | {VG, USD}, // British Virgin Islands (UK) 83 | {BN, BND}, // Brunei 84 | {BG, BGN}, // Bulgaria 85 | {BF, XOF}, // Burkina Faso 86 | {BI, BIF}, // Burundi 87 | 88 | // C 89 | {CV, CVE}, // Cabo Verde 90 | {KH, KHR}, // Cambodia 91 | {CM, XAF}, // Cameroon 92 | {CA, CAD}, // Canada 93 | {KY, KYD}, // Cayman Islands (UK) 94 | {CF, XAF}, // Central African Republic 95 | {TD, XAF}, // Chad 96 | {CL, CLP}, // Chile 97 | {CL, CLF}, // Chile 98 | {CN, CNY}, // China 99 | {CX, AUD}, // Christmas Island (Australia) 100 | {CC, AUD}, // Cocos (Keeling) Islands (Australia) 101 | {CO, COP}, // Colombia 102 | {CO, COU}, // Colombia 103 | {KM, KMF}, // Comoros 104 | {CD, CDF}, // Congo, Democratic Republic of the 105 | {CG, XAF}, // Congo, Republic of the 106 | {CK, NZD}, // Cook Islands (New Zealand) 107 | {CR, CRC}, // Costa Rica 108 | {CI, XOF}, // Cote d'Ivoire 109 | {HR, HRK}, // Croatia 110 | {CU, CUP}, // Cuba 111 | {CU, CUC}, // Cuba 112 | {CW, ANG}, // Curacao (Netherlands) 113 | {CY, EUR}, // Cyprus 114 | {CZ, CZK}, // Czech Republic 115 | 116 | // D 117 | {DK, DKK}, // Denmark 118 | {DJ, DJF}, // Djibouti 119 | {DM, XCD}, // Dominica 120 | {DO, DOP}, // Dominican Republic 121 | 122 | // E 123 | {EC, USD}, // Ecuador 124 | {EG, EGP}, // Egypt 125 | {SV, SVC}, // El Salvador 126 | {SV, USD}, // El Salvador 127 | {GQ, XAF}, // Equatorial Guinea 128 | {ER, ERN}, // Eritrea 129 | {EE, EUR}, // Estonia 130 | {SZ, SZL}, // Eswatini (formerly Swaziland) 131 | {ET, ETB}, // Ethiopia 132 | 133 | // F 134 | {FK, FKP}, // Falkland Islands (UK) 135 | {FO, DKK}, // Faroe Islands (Denmark) 136 | {FJ, FJD}, // Fiji 137 | {FI, EUR}, // Finland 138 | {FR, EUR}, // France 139 | {GF, EUR}, // French Guiana (France) 140 | {PF, XPF}, // French Polynesia (France) 141 | {TF, EUR}, // French Southern Territories (France) 142 | 143 | // G 144 | {GA, XAF}, // Gabon 145 | {GM, GMD}, // Gambia 146 | {GE, GEL}, // Georgia 147 | {DE, EUR}, // Germany 148 | {GH, GHS}, // Ghana 149 | {GI, GIP}, // Gibraltar (UK) 150 | {GR, EUR}, // Greece 151 | {GL, DKK}, // Greenland (Denmark) 152 | {GD, XCD}, // Grenada 153 | {GP, EUR}, // Guadeloupe (France) 154 | {GU, USD}, // Guam (USA) 155 | {GT, GTQ}, // Guatemala 156 | {GG, GBP}, // Guernsey (UK) 157 | {GG, GGP}, // Guernsey (UK) 158 | {GN, GNF}, // Guinea 159 | {GW, XOF}, // Guinea-Bissau 160 | {GY, GYD}, // Guyana 161 | 162 | // H 163 | {HT, HTG}, // Haiti 164 | {HT, USD}, // Haiti 165 | {HM, AUD}, // Heard Island and McDonald Islands 166 | {VA, EUR}, // Holy See 167 | {HN, HNL}, // Honduras 168 | {HK, HKD}, // Hong Kong (China) 169 | {HU, HUF}, // Hungary 170 | 171 | // I 172 | {IS, ISK}, // Iceland 173 | {IN, INR}, // India 174 | {ID, IDR}, // Indonesia 175 | {IR, IRR}, // Iran 176 | {IQ, IQD}, // Iraq 177 | {IE, EUR}, // Ireland 178 | {IM, GBP}, // Isle of Man (UK) 179 | {IM, IMP}, // Isle of Man (UK) 180 | {IL, ILS}, // Israel 181 | {IT, EUR}, // Italy 182 | 183 | // J 184 | {JM, JMD}, // Jamaica 185 | {JP, JPY}, // Japan 186 | {JE, GBP}, // Jersey (UK) 187 | {JE, JEP}, // Jersey (UK) 188 | {JO, JOD}, // Jordan 189 | 190 | // K 191 | {KZ, KZT}, // Kazakhstan 192 | {KE, KES}, // Kenya 193 | {KI, AUD}, // Kiribati 194 | {KP, KPW}, // Democratic People's Republic of Korea 195 | {KR, KRW}, // Republic of Korea 196 | {KW, KWD}, // Kuwait 197 | {KG, KGS}, // Kyrgyzstan 198 | 199 | // L 200 | {LA, LAK}, // Laos 201 | {LV, EUR}, // Latvia 202 | {LB, LBP}, // Lebanon 203 | {LS, LSL}, // Lesotho 204 | {LS, ZAR}, // Lesotho 205 | {LR, LRD}, // Liberia 206 | {LY, LYD}, // Libya 207 | {LI, CHF}, // Liechtenstein 208 | {LT, EUR}, // Lithuania 209 | {LU, EUR}, // Luxembourg 210 | 211 | // M 212 | {MO, MOP}, // Macau (China) 213 | {MK, MKD}, // Macedonia (FYROM) 214 | {MG, MGA}, // Madagascar 215 | {MW, MWK}, // Malawi 216 | {MY, MYR}, // Malaysia 217 | {MV, MVR}, // Maldives 218 | {ML, XOF}, // Mali 219 | {MT, EUR}, // Malta 220 | {MH, USD}, // Marshall Islands 221 | {MQ, EUR}, // Martinique (France) 222 | {MR, MRU}, // Mauritania 223 | {MU, MUR}, // Mauritius 224 | {YT, EUR}, // Mayotte (France) 225 | {MX, MXN}, // Mexico 226 | {MX, MXV}, // Mexico 227 | {FM, USD}, // Micronesia 228 | {MD, MDL}, // Moldova 229 | {MC, EUR}, // Monaco 230 | {MN, MNT}, // Mongolia 231 | {ME, EUR}, // Montenegro 232 | {MS, XCD}, // Montserrat (UK) 233 | {MA, MAD}, // Morocco 234 | {MZ, MXN}, // Mozambique 235 | {MM, MMK}, // Myanmar 236 | 237 | // N 238 | {NA, NAD}, // Namibia 239 | {NA, ZAR}, // Namibia 240 | {NR, AUD}, // Nauru 241 | {NP, NPR}, // Nepal 242 | {NL, EUR}, // Netherlands 243 | {NC, XPF}, // New Caledonia (France) 244 | {NZ, NZD}, // New Zealand 245 | {NI, NIO}, // Nicaragua 246 | {NE, XOF}, // Niger 247 | {NG, NGN}, // Nigeria 248 | {NU, NZD}, // Niue (New Zealand) 249 | {NF, AUD}, // Norfolk Island (Australia) 250 | {MP, USD}, // Northern Mariana Islands (USA) 251 | {NO, NOK}, // Norway 252 | 253 | // O 254 | {OM, OMR}, // Oman 255 | 256 | {PK, PKR}, // Pakistan 257 | {PW, USD}, // Palau 258 | {PS, ILS}, // Palestine 259 | {PA, PAB}, // Panama 260 | {PA, USD}, // Panama 261 | {PG, PGK}, // Papua New Guinea 262 | {PY, PYG}, // Paraguay 263 | {PE, PEN}, // Peru 264 | {PH, PHP}, // Philippines 265 | {PN, NZD}, // Pitcairn Islands (UK) 266 | {PL, PLN}, // Poland 267 | {PT, EUR}, // Portugal 268 | {PR, USD}, // Puerto Rico (USA) 269 | 270 | // Q 271 | {QA, QAR}, // Qatar 272 | 273 | // R 274 | {RE, EUR}, // Reunion (France) 275 | {RO, RON}, // Romania 276 | {RU, RUB}, // Russia 277 | {RW, RWF}, // Rwanda 278 | 279 | // S 280 | {BL, EUR}, // Saint Barthelemy (France) 281 | {SH, SHP}, // Saint Helena, Ascension and Tristan da Cunha (UK) 282 | {KN, XCD}, // Saint Kitts and Nevis 283 | {LC, XCD}, // Saint Lucia 284 | {MF, EUR}, // Saint Martin (France) 285 | {PM, EUR}, // Saint Pierre and Miquelon (France) 286 | {VC, XCD}, // Saint Vincent and the Grenadines 287 | {WS, WST}, // Samoa 288 | {SM, EUR}, // San Marino 289 | {ST, STN}, // Sao Tome and Principe 290 | {SA, SAR}, // Saudi Arabia 291 | {SN, XOF}, // Senegal 292 | {RS, RSD}, // Serbia 293 | {SC, SCR}, // Seychelles 294 | {SL, SLL}, // Sierra Leone 295 | {SG, SGD}, // Singapore 296 | {SX, ANG}, // Sint Maarten (Netherlands) 297 | {SK, EUR}, // Slovakia 298 | {SI, EUR}, // Slovenia 299 | {SB, SBD}, // Solomon Islands 300 | {SO, SOS}, // Somalia 301 | {ZA, ZAR}, // South Africa 302 | {GS, GBP}, // South Georgia Island (UK) 303 | {SS, SSP}, // South Sudan 304 | {ES, EUR}, // Spain 305 | {LK, LKR}, // Sri Lanka 306 | {SD, SDG}, // Sudan 307 | {SR, SRD}, // Suriname 308 | {SJ, NOK}, // Svalbard and Jan Mayen (Norway) 309 | {SE, SEK}, // Sweden 310 | {CH, CHF}, // Switzerland 311 | {CH, CHE}, // Switzerland 312 | {CH, CHW}, // Switzerland 313 | {SY, SYP}, // Syria 314 | 315 | // T 316 | {TW, TWD}, // Taiwan 317 | {TJ, TJS}, // Tajikistan 318 | {TZ, TZS}, // Tanzania 319 | {TH, THB}, // Thailand 320 | {TL, USD}, // Timor-Leste 321 | {TG, XOF}, // Togo 322 | {TK, NZD}, // Tokelau (New Zealand) 323 | {TO, TOP}, // Tonga 324 | {TT, TTD}, // Trinidad and Tobago 325 | {TN, TND}, // Tunisia 326 | {TR, TRY}, // Turkey 327 | {TM, TMT}, // Turkmenistan 328 | {TC, USD}, // Turks and Caicos Islands (UK) 329 | {TV, AUD}, // Tuvalu 330 | 331 | // U 332 | {UG, UGX}, // Uganda 333 | {UA, UAH}, // Ukraine 334 | {AE, AED}, // United Arab Emirates 335 | {GB, GBP}, // United Kingdom 336 | {US, USD}, // United States of America 337 | {US, USN}, // United States of America 338 | {UM, USD}, // United States Minor Outlying Islands 339 | {VI, USD}, // US Virgin Islands (USA) 340 | {UY, UYU}, // Uruguay 341 | {UY, UYI}, // Uruguay 342 | {UZ, UZS}, // Uzbekistan 343 | 344 | // V 345 | {VU, VUV}, // Vanuatu 346 | {VA, EUR}, // Vatican City (Holy See) 347 | {VE, VES}, // Venezuela 348 | {VN, VND}, // Vietnam 349 | 350 | // W 351 | {WF, XPF}, // Wallis and Futuna (France) 352 | {EH, MAD}, // Western Sahara 353 | 354 | // Y 355 | {YE, YER}, // Yemen 356 | 357 | // X 358 | {ZM, ZMW}, // Zambia 359 | {ZW, USD}, // Zimbabwe 360 | }; 361 | 362 | inline currency_unit find_country_currency(country_unit const & cu) 363 | { 364 | auto range = currencies.equal_range(cu); 365 | if (range.first == range.second) 366 | throw currency_not_found{}; 367 | 368 | return range.first->second; 369 | } 370 | 371 | inline std::set find_country_currencies( 372 | country_unit const & cu) 373 | { 374 | return find_country_currencies(currencies, cu); 375 | } 376 | 377 | inline std::pair country_currency_equal_range( 378 | country_unit const & cu) 379 | { 380 | return country_currency_equal_range(currencies, cu); 381 | } 382 | 383 | #endif 384 | } 385 | } -------------------------------------------------------------------------------- /include/currency.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef HAS_BOOST_OPTIONAL 8 | # include 9 | using boost::optional; 10 | #else 11 | # include 12 | using std::optional; 13 | #endif 14 | 15 | namespace moneycpp 16 | { 17 | struct currency_unit 18 | { 19 | std::string_view const code; 20 | int const number; 21 | int const minor_unit; 22 | std::string_view const name; 23 | }; 24 | 25 | inline constexpr bool operator==(currency_unit const & lhs, currency_unit const & rhs) noexcept 26 | { 27 | return 28 | lhs.number == rhs.number && 29 | lhs.code == rhs.code && 30 | lhs.minor_unit == rhs.minor_unit && 31 | lhs.name == rhs.name; 32 | } 33 | 34 | inline constexpr bool operator!=(currency_unit const & lhs, currency_unit const & rhs) noexcept 35 | { 36 | return !(lhs == rhs); 37 | } 38 | 39 | inline constexpr bool operator<(currency_unit const & lhs, currency_unit const & rhs) noexcept 40 | { 41 | return lhs.number < rhs.number; 42 | } 43 | 44 | template 45 | inline Iter find_currency(Iter first, Iter last, std::string_view code) 46 | { 47 | for (auto it = first; it != last; ++it) 48 | { 49 | if (it->code == code) 50 | return it; 51 | } 52 | 53 | return last; 54 | } 55 | 56 | 57 | template 58 | inline Iter find_currency(Iter first, Iter last, int const number) 59 | { 60 | for (auto it = first; it != last; ++it) 61 | { 62 | if (it->number == number) 63 | return it; 64 | } 65 | 66 | return last; 67 | } 68 | 69 | #ifdef HAS_COUNTRY_AND_CURRENCY_DB 70 | 71 | namespace currency 72 | { 73 | // active ISO 4217 currency codes 74 | constexpr currency_unit AED { "AED", 784, 2, "United Arab Emirates dirham" }; 75 | constexpr currency_unit AFN { "AFN", 971, 2, "Afghan afghani" }; 76 | constexpr currency_unit ALL { "ALL", 8, 2, "Albanian lek" }; 77 | constexpr currency_unit AMD { "AMD", 51, 2, "Armenian dram" }; 78 | constexpr currency_unit ANG { "ANG", 532, 2, "Netherlands Antillean guilder" }; 79 | constexpr currency_unit AOA { "AOA", 973, 2, "Angolan kwanza" }; 80 | constexpr currency_unit ARS { "ARS", 32, 2, "Argentine peso" }; 81 | constexpr currency_unit AUD { "AUD", 36, 2, "Australian dollar" }; 82 | constexpr currency_unit AWG { "AWG", 533, 2, "Aruban florin" }; 83 | constexpr currency_unit AZN { "AZN", 944, 2, "Azerbaijani manat" }; 84 | constexpr currency_unit BAM { "BAM", 977, 2, "Bosnia and Herzegovina convertible mark" }; 85 | constexpr currency_unit BBD { "BBD", 52, 2, "Barbados dollar" }; 86 | constexpr currency_unit BDT { "BDT", 50, 2, "Bangladeshi taka" }; 87 | constexpr currency_unit BGN { "BGN", 975, 2, "Bulgarian lev" }; 88 | constexpr currency_unit BHD { "BHD", 48, 3, "Bahraini dinar" }; 89 | constexpr currency_unit BIF { "BIF", 108, 0, "Burundian franc" }; 90 | constexpr currency_unit BMD { "BMD", 60, 2, "Bermudian dollar" }; 91 | constexpr currency_unit BND { "BND", 96, 2, "Brunei dollar" }; 92 | constexpr currency_unit BOB { "BOB", 68, 2, "Boliviano" }; 93 | constexpr currency_unit BOV { "BOV", 984, 2, "Bolivian Mvdol" }; 94 | constexpr currency_unit BRL { "BRL", 986, 2, "Brazilian real" }; 95 | constexpr currency_unit BSD { "BSD", 44, 2, "Bahamian dollar" }; 96 | constexpr currency_unit BTN { "BTN", 64, 2, "Bhutanese ngultrum" }; 97 | constexpr currency_unit BWP { "BWP", 72, 2, "Botswana pula" }; 98 | constexpr currency_unit BYN { "BYN", 933, 2, "Belarusian ruble" }; 99 | constexpr currency_unit BZD { "BZD", 84, 2, "Belize dollar" }; 100 | constexpr currency_unit CAD { "CAD", 124, 2, "Canadian dollar" }; 101 | constexpr currency_unit CDF { "CDF", 976, 2, "Congolese franc" }; 102 | constexpr currency_unit CHE { "CHE", 947, 2, "WIR Euro" }; 103 | constexpr currency_unit CHF { "CHF", 756, 2, "Swiss franc" }; 104 | constexpr currency_unit CHW { "CHW", 948, 2, "WIR Franc" }; 105 | constexpr currency_unit CLF { "CLF", 990, 4, "Unidad de Fomento" }; 106 | constexpr currency_unit CLP { "CLP", 152, 0, "Chilean peso" }; 107 | constexpr currency_unit CNY { "CNY", 156, 2, "Renminbi" }; 108 | constexpr currency_unit COP { "COP", 170, 2, "Colombian peso" }; 109 | constexpr currency_unit COU { "COU", 970, 2, "Unidad de Valor Real" }; 110 | constexpr currency_unit CRC { "CRC", 188, 2, "Costa Rican colon" }; 111 | constexpr currency_unit CUC { "CUC", 931, 2, "Cuban convertible peso" }; 112 | constexpr currency_unit CUP { "CUP", 192, 2, "Cuban peso" }; 113 | constexpr currency_unit CVE { "CVE", 132, 0, "Cape Verde escudo" }; 114 | constexpr currency_unit CZK { "CZK", 203, 2, "Czech koruna" }; 115 | constexpr currency_unit DJF { "DJF", 262, 0, "Djiboutian franc" }; 116 | constexpr currency_unit DKK { "DKK", 208, 2, "Danish krone" }; 117 | constexpr currency_unit DOP { "DOP", 214, 2, "Dominican peso" }; 118 | constexpr currency_unit DZD { "DZD", 12, 2, "Algerian dinar" }; 119 | constexpr currency_unit EGP { "EGP", 818, 2, "Egyptian pound" }; 120 | constexpr currency_unit ERN { "ERN", 232, 2, "Eritrean nakfa" }; 121 | constexpr currency_unit ETB { "ETB", 230, 2, "Ethiopian birr" }; 122 | constexpr currency_unit EUR { "EUR", 978, 2, "Euro" }; 123 | constexpr currency_unit FJD { "FJD", 242, 2, "Fiji dollar" }; 124 | constexpr currency_unit FKP { "FKP", 238, 2, "Falkland Islands pound" }; 125 | constexpr currency_unit GBP { "GBP", 826, 2, "Pound sterling" }; 126 | constexpr currency_unit GEL { "GEL", 981, 2, "Georgian lari" }; 127 | constexpr currency_unit GHS { "GHS", 936, 2, "Ghanaian cedi" }; 128 | constexpr currency_unit GIP { "GIP", 292, 2, "Gibraltar pound" }; 129 | constexpr currency_unit GMD { "GMD", 270, 2, "Gambian dalasi" }; 130 | constexpr currency_unit GNF { "GNF", 324, 0, "Guinean franc" }; 131 | constexpr currency_unit GTQ { "GTQ", 320, 2, "Guatemalan quetzal" }; 132 | constexpr currency_unit GYD { "GYD", 328, 2, "Guyanese dollar" }; 133 | constexpr currency_unit HKD { "HKD", 344, 2, "Hong Kong dollar" }; 134 | constexpr currency_unit HNL { "HNL", 340, 2, "Honduran lempira" }; 135 | constexpr currency_unit HRK { "HRK", 191, 2, "Croatian kuna" }; 136 | constexpr currency_unit HTG { "HTG", 332, 2, "Haitian gourde" }; 137 | constexpr currency_unit HUF { "HUF", 348, 2, "Hungarian forint" }; 138 | constexpr currency_unit IDR { "IDR", 360, 2, "Indonesian rupiah" }; 139 | constexpr currency_unit ILS { "ILS", 376, 2, "Israeli new shekel" }; 140 | constexpr currency_unit INR { "INR", 356, 2, "Indian rupee" }; 141 | constexpr currency_unit IQD { "IQD", 368, 2, "Iraqi dinar" }; 142 | constexpr currency_unit IRR { "IRR", 364, 2, "Iranian rial" }; 143 | constexpr currency_unit ISK { "ISK", 352, 2, "Icelandic krona" }; 144 | constexpr currency_unit JMD { "JMD", 388, 2, "Jamaican dollar" }; 145 | constexpr currency_unit JOD { "JOD", 400, 3, "Jordanian dinar" }; 146 | constexpr currency_unit JPY { "JPY", 392, 0, "Japanese yen" }; 147 | constexpr currency_unit KES { "KES", 404, 2, "Kenyan shilling" }; 148 | constexpr currency_unit KGS { "KGS", 417, 2, "Kyrgyzstani som" }; 149 | constexpr currency_unit KHR { "KHR", 116, 2, "Cambodian riel" }; 150 | constexpr currency_unit KMF { "KMF", 174, 0, "Comoro franc" }; 151 | constexpr currency_unit KPW { "KPW", 408, 2, "North Korean won" }; 152 | constexpr currency_unit KRW { "KRW", 410, 0, "South Korean won" }; 153 | constexpr currency_unit KWD { "KWD", 414, 3, "Kuwaiti dinar" }; 154 | constexpr currency_unit KYD { "KYD", 136, 2, "Cayman Islands dollar" }; 155 | constexpr currency_unit KZT { "KZT", 398, 2, "Kazakhstani tenge" }; 156 | constexpr currency_unit LAK { "LAK", 418, 2, "Lao kip" }; 157 | constexpr currency_unit LBP { "LBP", 422, 2, "Lebanese pound" }; 158 | constexpr currency_unit LKR { "LKR", 144, 2, "Sri Lankan rupee" }; 159 | constexpr currency_unit LRD { "LRD", 430, 2, "Liberian dollar" }; 160 | constexpr currency_unit LSL { "LSL", 426, 2, "Lesotho loti" }; 161 | constexpr currency_unit LYD { "LYD", 434, 3, "Libyan dinar" }; 162 | constexpr currency_unit MAD { "MAD", 504, 2, "Moroccan dirham" }; 163 | constexpr currency_unit MDL { "MDL", 498, 2, "Moldovan leu" }; 164 | constexpr currency_unit MGA { "MGA", 969, 1, "Malagasy ariary" }; 165 | constexpr currency_unit MKD { "MKD", 807, 2, "Macedonian denar" }; 166 | constexpr currency_unit MMK { "MMK", 104, 2, "Myanmar kyat" }; 167 | constexpr currency_unit MNT { "MNT", 496, 2, "Mongolian tögrög" }; 168 | constexpr currency_unit MOP { "MOP", 446, 2, "Macanese pataca" }; 169 | constexpr currency_unit MRU { "MRU", 929, 1, "Mauritanian ouguiya" }; 170 | constexpr currency_unit MUR { "MUR", 480, 2, "Mauritian rupee" }; 171 | constexpr currency_unit MVR { "MVR", 462, 2, "Maldivian rufiyaa" }; 172 | constexpr currency_unit MWK { "MWK", 454, 2, "Malawian kwacha" }; 173 | constexpr currency_unit MXN { "MXN", 484, 2, "Mexican peso" }; 174 | constexpr currency_unit MXV { "MXV", 979, 2, "Mexican Unidad de Inversion" }; 175 | constexpr currency_unit MYR { "MYR", 458, 2, "Malaysian ringgit" }; 176 | constexpr currency_unit MZN { "MZN", 943, 2, "Mozambican metical" }; 177 | constexpr currency_unit NAD { "NAD", 516, 2, "Namibian dollar" }; 178 | constexpr currency_unit NGN { "NGN", 566, 2, "Nigerian naira" }; 179 | constexpr currency_unit NIO { "NIO", 558, 2, "Nicaraguan cordoba" }; 180 | constexpr currency_unit NOK { "NOK", 578, 2, "Norwegian krone" }; 181 | constexpr currency_unit NPR { "NPR", 524, 2, "Nepalese rupee" }; 182 | constexpr currency_unit NZD { "NZD", 554, 2, "New Zealand dollar" }; 183 | constexpr currency_unit OMR { "OMR", 512, 3, "Omani rial" }; 184 | constexpr currency_unit PAB { "PAB", 590, 2, "Panamanian balboa" }; 185 | constexpr currency_unit PEN { "PEN", 604, 2, "Peruvian sol" }; 186 | constexpr currency_unit PGK { "PGK", 598, 2, "Papua New Guinean kina" }; 187 | constexpr currency_unit PHP { "PHP", 608, 2, "Philippine peso" }; 188 | constexpr currency_unit PKR { "PKR", 586, 2, "Pakistani rupee" }; 189 | constexpr currency_unit PLN { "PLN", 985, 2, "Polish zloty" }; 190 | constexpr currency_unit PYG { "PYG", 600, 0, "Paraguayan guarani" }; 191 | constexpr currency_unit QAR { "QAR", 634, 2, "Qatari riyal" }; 192 | constexpr currency_unit RON { "RON", 946, 2, "Romanian leu" }; 193 | constexpr currency_unit RSD { "RSD", 941, 2, "Serbian dinar" }; 194 | constexpr currency_unit RUB { "RUB", 643, 2, "Russian ruble" }; 195 | constexpr currency_unit RWF { "RWF", 646, 0, "Rwandan franc" }; 196 | constexpr currency_unit SAR { "SAR", 682, 2, "Saudi riyal" }; 197 | constexpr currency_unit SBD { "SBD", 90, 2, "Solomon Islands dollar" }; 198 | constexpr currency_unit SCR { "SCR", 690, 2, "Seychelles rupee" }; 199 | constexpr currency_unit SDG { "SDG", 938, 2, "Sudanese pound" }; 200 | constexpr currency_unit SEK { "SEK", 752, 2, "Swedish krona" }; 201 | constexpr currency_unit SGD { "SGD", 702, 2, "Singapore dollar" }; 202 | constexpr currency_unit SHP { "SHP", 654, 2, "Saint Helena pound" }; 203 | constexpr currency_unit SLL { "SLL", 694, 2, "Sierra Leonean leone" }; 204 | constexpr currency_unit SOS { "SOS", 706, 2, "Somali shilling" }; 205 | constexpr currency_unit SRD { "SRD", 968, 2, "Surinamese dollar" }; 206 | constexpr currency_unit SSP { "SSP", 728, 2, "South Sudanese pound" }; 207 | constexpr currency_unit STN { "STN", 930, 2, "Sao Tome and Principe dobra" }; 208 | constexpr currency_unit SVC { "SVC", 222, 2, "Salvadoran colon" }; 209 | constexpr currency_unit SYP { "SYP", 760, 2, "Syrian pound" }; 210 | constexpr currency_unit SZL { "SZL", 748, 2, "Swazi lilangeni" }; 211 | constexpr currency_unit THB { "THB", 764, 2, "Thai baht" }; 212 | constexpr currency_unit TJS { "TJS", 972, 2, "Tajikistani somoni" }; 213 | constexpr currency_unit TMT { "TMT", 934, 2, "Turkmenistan manat" }; 214 | constexpr currency_unit TND { "TND", 788, 3, "Tunisian dinar" }; 215 | constexpr currency_unit TOP { "TOP", 776, 2, "Tongan pa'anga" }; 216 | constexpr currency_unit TRY { "TRY", 949, 2, "Turkish lira" }; 217 | constexpr currency_unit TTD { "TTD", 780, 2, "Trinidad and Tobago dollar" }; 218 | constexpr currency_unit TWD { "TWD", 901, 2, "New Taiwan dollar" }; 219 | constexpr currency_unit TZS { "TZS", 834, 2, "Tanzanian shilling" }; 220 | constexpr currency_unit UAH { "UAH", 980, 2, "Ukrainian hryvnia" }; 221 | constexpr currency_unit UGX { "UGX", 800, 2, "Ugandan shilling" }; 222 | constexpr currency_unit USD { "USD", 840, 2, "United States dollar" }; 223 | constexpr currency_unit USN { "USN", 997, 2, "United States dollar (next day) " }; 224 | constexpr currency_unit UYI { "UYI", 940, 0, "Uruguay Peso en Unidades Indexadas " }; 225 | constexpr currency_unit UYU { "UYU", 858, 2, "Uruguayan peso" }; 226 | constexpr currency_unit UYW { "UYW", 927, 4, "Unidad previsional" }; 227 | constexpr currency_unit UZS { "UZS", 860, 2, "Uzbekistan som" }; 228 | constexpr currency_unit VES { "VES", 928, 2, "Venezuelan bolívar soberano" }; 229 | constexpr currency_unit VND { "VND", 704, 0, "Vietnamese dong" }; 230 | constexpr currency_unit VUV { "VUV", 548, 0, "Vanuatu vatu" }; 231 | constexpr currency_unit WST { "WST", 882, 2, "Samoan tala" }; 232 | constexpr currency_unit XAF { "XAF", 950, 0, "CFA franc BEAC" }; 233 | constexpr currency_unit XAG { "XAG", 961,-1, "Silver" }; 234 | constexpr currency_unit XAU { "XAU", 959,-1, "Gold" }; 235 | constexpr currency_unit XBA { "XBA", 955,-1, "European Composite Unit (EURCO)" }; 236 | constexpr currency_unit XBB { "XBB", 956,-1, "European Monetary Unit" }; 237 | constexpr currency_unit XBC { "XBC", 957,-1, "European Unit of Account 9" }; 238 | constexpr currency_unit XBD { "XBD", 958,-1, "European Unit of Account 17" }; 239 | constexpr currency_unit XCD { "XCD", 951, 2, "East Caribbean dollar" }; 240 | constexpr currency_unit XDR { "XDR", 960,-1, "Special drawing rights" }; 241 | constexpr currency_unit XOF { "XOF", 952, 0, "CFA franc BCEAO" }; 242 | constexpr currency_unit XPD { "XPD", 964,-1, "Palladium" }; 243 | constexpr currency_unit XPF { "XPF", 953, 0, "CFP franc" }; 244 | constexpr currency_unit XPT { "XPT", 962,-1, "Platinum" }; 245 | constexpr currency_unit XSU { "XSU", 994,-1, "SUCRE" }; 246 | constexpr currency_unit XTS { "XTS", 963,-1, "Reserved for testing" }; 247 | constexpr currency_unit XUA { "XUA", 965,-1, "ADB Unit of Account" }; 248 | constexpr currency_unit XXX { "XXX", 999,-1, "" }; 249 | constexpr currency_unit YER { "YER", 886, 2, "Yemeni rial" }; 250 | constexpr currency_unit ZAR { "ZAR", 710, 2, "South African rand" }; 251 | constexpr currency_unit ZMW { "ZMW", 967, 2, "Zambian kwacha" }; 252 | constexpr currency_unit ZWL { "ZWL", 932, 2, "Zimbabwean dollar" }; 253 | 254 | // unofficial codes 255 | constexpr currency_unit CNH { "CNH", 0, 2, "Chinese yuan" }; 256 | constexpr currency_unit CNT { "CNT", 0, 2, "Chinese yuan" }; 257 | constexpr currency_unit GGP { "GGP", 0, 2, "Guernsey pound" }; 258 | constexpr currency_unit IMP { "IMP", 0, 2, "Isle of Man pound" }; 259 | constexpr currency_unit JEP { "JEP", 0, 2, "Jersey pound" }; 260 | constexpr currency_unit KID { "KID", 0, 2, "Kiribati dollar" }; 261 | constexpr currency_unit NIS { "NIS", 0, 2, "Israeli new shekel" }; 262 | constexpr currency_unit NTD { "NTD", 0, 2, "New Taiwan dollar" }; 263 | constexpr currency_unit PRB { "PRB", 0, 2, "Transnistrian ruble" }; 264 | constexpr currency_unit SLS { "SLS", 0, 2, "Somaliland shilling" }; 265 | constexpr currency_unit RMB { "RMB", 0, 2, "Renminbi (Chinese) yuan" }; 266 | constexpr currency_unit TVD { "TVD", 0, 2, "Tuvalu dollar" }; 267 | 268 | // crypto-currencies 269 | constexpr currency_unit DASH { "DASH", 0, 8, "Dash" }; 270 | constexpr currency_unit ETH { "ETH", 0, 18, "Ether" }; 271 | constexpr currency_unit VTC { "VTC", 0, 8, "Vertcoin" }; 272 | constexpr currency_unit XBC_ { "XBC_", 0, 8, "Bitcoin Cash" }; 273 | constexpr currency_unit XBT { "XBT", 0, 8, "Bitcoin" }; 274 | constexpr currency_unit XLM { "XLM", 0, 8, "Stellar Lumen" }; 275 | constexpr currency_unit XMR { "XMR", 0, 12, "Monero" }; 276 | constexpr currency_unit XRP { "XRP", 0, 6, "Ripple" }; 277 | constexpr currency_unit ZEC { "ZEC", 0, 8, "Zcash" }; 278 | 279 | constexpr currency_unit NONE { std::string_view{}, 0, 0, std::string_view{} }; 280 | 281 | static const std::initializer_list currencies 282 | { 283 | // official 284 | AED, AFN, ALL, AMD, ANG, AOA, ARS, AUD, AWG, AZN, 285 | BAM, BBD, BDT, BGN, BHD, BIF, BMD, BND, BOB, BOV, BRL, BSD, BTN, BWP, BYN, BZD, 286 | CAD, CDF, CHE, CHF, CHW, CLF, CLP, CNY, COP, COU, CRC, CUC, CUP, CVE, CZK, 287 | DJF, DKK, DOP, DZD, 288 | EGP, ERN, ETB, EUR, 289 | FJD, FKP, 290 | GBP, GEL, GHS, GIP, GMD, GNF, GTQ, GYD, 291 | HKD, HNL, HRK, HTG, HUF, 292 | IDR, ILS, INR, IQD, IRR, ISK, 293 | JMD, JOD, JPY, 294 | KES, KGS, KHR, KMF, KPW, KRW, KWD, KYD, 295 | LAK, LBP, LKR, LRD, LSL, LYD, 296 | MAD, MDL, MGA, MKD, MMK, MNT, MOP, MRU, MUR, MVR, MWK, MXN, MXV, MYR, MZN, 297 | NAD, NGN, NIO, NOK, NPR, NZD, 298 | OMR, 299 | PAB, PEN, PGK, PHP, PKR, PLN, PYG, 300 | QAR, 301 | RON, RSD, RUB, RWF, 302 | SAR, SBD, SCR, SDG, SEK, SGD, SHP, SLL, SOS, SRD, SSP, STN, SVC, SYP, SZL, 303 | THB, TJS, TMT, TND, TOP, TRY, TTD, TWD, TZS, 304 | UAH, UGX, USD, USN, UYI, UYU, UYW, UZS, 305 | VES, VND, VUV, 306 | WST, 307 | XAF, XAG, XAU, XBA, XBB, XBC, XBD, XCD, XDR, XOF, XPD, XPF, XPT, XSU, XTS, XUA, XXX, 308 | YER, 309 | ZAR, ZMW, ZWL, 310 | 311 | // unofficial 312 | CNH, CNT, GGP, IMP, JEP, KID, NIS, NTD, PRB, SLS, RMB, TVD, 313 | 314 | // crypto-currencies 315 | DASH, ETH, VTC, XBC_, XBT, XLM, XMR, XRP, ZEC 316 | }; 317 | } 318 | 319 | #endif 320 | 321 | inline optional find_currency(std::string_view code) 322 | { 323 | #ifdef HAS_COUNTRY_AND_CURRENCY_DB 324 | auto it = find_currency( 325 | std::cbegin(currency::currencies), 326 | std::cend(currency::currencies), 327 | code); 328 | 329 | if (it != std::cend(currency::currencies)) 330 | return optional{ *it }; 331 | #endif 332 | return {}; 333 | } 334 | 335 | inline optional find_currency(int const number) 336 | { 337 | #ifdef HAS_COUNTRY_AND_CURRENCY_DB 338 | auto it = find_currency( 339 | std::cbegin(currency::currencies), 340 | std::cend(currency::currencies), 341 | number); 342 | 343 | if (it != std::cend(currency::currencies)) 344 | return optional{ *it }; 345 | #endif 346 | return {}; 347 | } 348 | 349 | struct bad_currency_error : public std::runtime_error 350 | { 351 | explicit bad_currency_error(std::string const & message) 352 | : std::runtime_error(message) 353 | { 354 | } 355 | 356 | explicit bad_currency_error(const char * message) 357 | : std::runtime_error(message) 358 | { 359 | } 360 | }; 361 | 362 | struct currency_mismatch_error : public bad_currency_error 363 | { 364 | currency_mismatch_error() 365 | : bad_currency_error("Currencies do not match") 366 | {} 367 | }; 368 | 369 | struct currency_not_found : public bad_currency_error 370 | { 371 | currency_not_found() 372 | : bad_currency_error("Currency not found") 373 | {} 374 | }; 375 | } 376 | -------------------------------------------------------------------------------- /include/ext/boost.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef HAS_BOOST_MULTIPRECISION 4 | 5 | #include "rounding.h" 6 | #include "boost\multiprecision\cpp_dec_float.hpp" 7 | 8 | namespace moneycpp 9 | { 10 | using decimal = boost::multiprecision::number>; 11 | 12 | inline decimal operator""_dec(char const * str, std::size_t) 13 | { 14 | return decimal(str); 15 | } 16 | 17 | template <> 18 | inline decimal round_up::operator()(decimal const value) const 19 | { 20 | if (value > decimal(0u)) 21 | return boost::multiprecision::ceil(value); 22 | else 23 | return boost::multiprecision::floor(value); 24 | } 25 | 26 | template <> 27 | inline decimal round_down::operator()(decimal const value) const 28 | { 29 | if (value > decimal(0u)) 30 | return boost::multiprecision::floor(value); 31 | else 32 | return boost::multiprecision::ceil(value); 33 | } 34 | 35 | template <> 36 | inline decimal round_ceiling::operator()(decimal const value) const 37 | { 38 | return boost::multiprecision::ceil(value); 39 | } 40 | 41 | template <> 42 | inline decimal round_floor::operator()(decimal const value) const 43 | { 44 | return boost::multiprecision::floor(value); 45 | } 46 | 47 | template <> 48 | inline decimal round_half_up::operator()(decimal const value) const 49 | { 50 | if (value > decimal(0u)) 51 | { 52 | return boost::multiprecision::floor(value + "0.5"_dec); 53 | } 54 | else 55 | { 56 | return boost::multiprecision::ceil(value - "0.5"_dec); 57 | } 58 | } 59 | 60 | template <> 61 | inline decimal round_half_down::operator()(decimal const value) const 62 | { 63 | if (value > decimal(0u)) 64 | { 65 | return boost::multiprecision::ceil(value - "0.5"_dec); 66 | } 67 | else 68 | { 69 | return boost::multiprecision::floor(value + "0.5"_dec); 70 | } 71 | } 72 | 73 | template <> 74 | inline decimal round_half_even::operator()(decimal const value) const 75 | { 76 | decimal rv = boost::multiprecision::remainder(value, 1.0); 77 | if (boost::multiprecision::abs(rv) == "0.5"_dec) 78 | { 79 | decimal ip; 80 | decimal fp = boost::multiprecision::modf(value, &ip); 81 | if (boost::multiprecision::remainder(ip, decimal(2U)) == decimal(0U)) 82 | { 83 | rv = -rv; 84 | } 85 | } 86 | 87 | return value - rv; 88 | } 89 | 90 | template <> 91 | inline decimal round_half_odd::operator()(decimal const value) const 92 | { 93 | decimal rv = boost::multiprecision::remainder(value, 1.0); 94 | 95 | if (boost::multiprecision::abs(rv) == "0.5"_dec) 96 | { 97 | decimal ip; 98 | decimal fp = boost::multiprecision::modf(value, &ip); 99 | if (boost::multiprecision::remainder(ip, decimal(2U)) == decimal(0U)) 100 | { 101 | rv = -rv; 102 | } 103 | 104 | return value + rv; 105 | } 106 | else 107 | return boost::multiprecision::floor(value + "0.5"_dec); 108 | } 109 | } 110 | 111 | #endif -------------------------------------------------------------------------------- /include/money.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "currency.h" 3 | #include "rounding.h" 4 | #include 5 | 6 | namespace moneycpp 7 | { 8 | 9 | template 10 | struct money 11 | { 12 | const TValue amount; 13 | const currency_unit currency; 14 | }; 15 | 16 | template 17 | inline bool operator==(money const & lhs, money const & rhs) 18 | { 19 | if (lhs.currency != rhs.currency) throw currency_mismatch_error(); 20 | return lhs.amount == rhs.amount; 21 | } 22 | 23 | template 24 | inline bool operator!=(money const & lhs, money const & rhs) 25 | { 26 | return !(lhs == rhs); 27 | } 28 | 29 | template 30 | inline bool operator<(money const & lhs, money const & rhs) 31 | { 32 | if (lhs.currency != rhs.currency) throw currency_mismatch_error(); 33 | return lhs.amount < rhs.amount; 34 | } 35 | 36 | template 37 | inline bool operator>(money const & lhs, money const & rhs) 38 | { 39 | return rhs < lhs; 40 | } 41 | 42 | template 43 | inline bool operator<=(money const & lhs, money const & rhs) 44 | { 45 | return !(rhs < lhs); 46 | } 47 | 48 | template 49 | inline bool operator>=(money const & lhs, money const & rhs) 50 | { 51 | return !(lhs < rhs); 52 | } 53 | 54 | template 55 | inline auto operator+(money const & lhs, money const & rhs) 56 | { 57 | if (lhs.currency != rhs.currency) throw currency_mismatch_error(); 58 | return money {lhs.amount + rhs.amount, lhs.currency}; 59 | } 60 | 61 | template 62 | inline auto operator-(money const & lhs, money const & rhs) 63 | { 64 | if (lhs.currency != rhs.currency) throw currency_mismatch_error(); 65 | return money {lhs.amount - rhs.amount, lhs.currency}; 66 | } 67 | 68 | template 69 | inline auto operator*(money const & lhs, TValue2 const rhs) 70 | { 71 | return money {lhs.amount * rhs, lhs.currency}; 72 | } 73 | 74 | template 75 | inline auto operator/(money const & lhs, TValue2 const rhs) 76 | { 77 | if (rhs == 0) throw std::runtime_error("cannot divide by 0"); 78 | return money {lhs.amount / rhs, lhs.currency}; 79 | } 80 | 81 | template 82 | constexpr money make_money(TValue const value, currency_unit const currency) noexcept 83 | { 84 | return money { value, currency }; 85 | } 86 | 87 | template 88 | struct rounding_policy_none 89 | { 90 | rounding_policy_none(RoundFunc = RoundFunc()) 91 | { 92 | } 93 | 94 | template 95 | money operator()(money const value) 96 | { 97 | return value; 98 | } 99 | }; 100 | 101 | template 102 | struct rounding_policy_standard 103 | { 104 | rounding_policy_standard(RoundFunc rounding):rfun(rounding) 105 | {} 106 | 107 | template 108 | money operator()(money const value) 109 | { 110 | auto c = TValue(10000.0); 111 | auto amount = static_cast(value.amount * c); 112 | auto r = std::invoke(std::forward(rfun), amount); 113 | return make_money(r / c, value.currency); 114 | } 115 | 116 | private: 117 | RoundFunc rfun; 118 | }; 119 | 120 | template 121 | struct rounding_policy_to_currency_digits 122 | { 123 | rounding_policy_to_currency_digits(RoundFunc rounding) :rfun(rounding) 124 | {} 125 | 126 | template 127 | money operator()(money const value) 128 | { 129 | auto c = std::pow(10, value.currency.minor_unit); 130 | auto amount = static_cast(value.amount * c); 131 | auto r = std::invoke(std::forward(rfun), amount); 132 | return make_money(r / c, value.currency); 133 | } 134 | 135 | private: 136 | RoundFunc rfun; 137 | }; 138 | 139 | template 140 | money exchange_money( 141 | money const & m, 142 | currency_unit const currency, 143 | TValue const rate, 144 | RoundPolicy roundingPolicy) 145 | { 146 | if(rate <= 0) 147 | throw std::runtime_error("Exchange rate must be positive"); 148 | 149 | if(m.currency == currency) 150 | return m; 151 | 152 | auto exchange = make_money(static_cast(m.amount * rate), currency); 153 | 154 | auto result = 155 | std::invoke( 156 | std::forward(roundingPolicy), 157 | exchange); 158 | 159 | return result; 160 | } 161 | 162 | template 163 | inline money const & min(money const & a, money const & b) 164 | { 165 | return (a < b) ? a : b; 166 | } 167 | 168 | template 169 | inline money const & max(money const & a, money const & b) 170 | { 171 | return (a > b) ? a : b; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /include/rounding.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace moneycpp 6 | { 7 | // Rounding Algorithms 101 Redux 8 | // https://www.eetimes.com/document.asp?doc_id=1274515 9 | // Clive Maxfield - 2006 10 | 11 | // round away from zero 12 | // input: -5.5, -2.5, -1.6, -1.1, -1.0, 1.0, 1.1, 1.6, 2.5, 5.5 13 | // result: -6.0, -3.0, -2.0, -2.0, -1.0, 1.0, 2.0, 2.0, 3.0, 6.0 14 | struct round_up 15 | { 16 | template 17 | T operator()(T const) const; 18 | }; 19 | 20 | // round towards zero 21 | // input: -5.5, -2.5, -1.6, -1.1, -1.0, 1.0, 1.1, 1.6, 2.5, 5.5 22 | // result: -5.0, -2.0, -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 2.0, 5.0 23 | struct round_down 24 | { 25 | template 26 | T operator()(T const) const; 27 | }; 28 | 29 | // round towards positive infinity 30 | // input: -5.5, -2.5, -1.6, -1.1, -1.0, 1.0, 1.1, 1.6, 2.5, 5.5 31 | // result: -5.0, -2.0, -1.0, -1.0, -1.0, 1.0, 2.0, 2.0, 3.0, 6.0 32 | struct round_ceiling 33 | { 34 | template 35 | T operator()(T const) const; 36 | }; 37 | 38 | // round towards negative infinity 39 | // input: -5.5, -2.5, -1.6, -1.1, -1.0, 1.0, 1.1, 1.6, 2.5, 5.5 40 | // result: -6.0, -3.0, -2.0, -2.0, -1.0, 1.0, 1.0, 1.0, 2.0, 5.0 41 | struct round_floor 42 | { 43 | template 44 | T operator()(T const) const; 45 | }; 46 | 47 | // round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up 48 | // input: -5.5, -2.5, -1.6, -1.1, -1.0, 1.0, 1.1, 1.6, 2.5, 5.5 49 | // result: -6.0, -3.0, -2.0, -1.0, -1.0, 1.0, 1.0, 2.0, 3.0, 6.0 50 | struct round_half_up 51 | { 52 | template 53 | T operator()(T const) const; 54 | }; 55 | 56 | // round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down 57 | // input: -5.5, -2.5, -1.6, -1.1, -1.0, 1.0, 1.1, 1.6, 2.5, 5.5 58 | // result: -5.0, -2.0, -2.0, -1.0, -1.0, 1.0, 1.0, 2.0, 2.0, 5.0 59 | struct round_half_down 60 | { 61 | template 62 | T operator()(T const) const; 63 | }; 64 | 65 | // round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor 66 | // input: -5.5, -2.5, -1.6, -1.1, -1.0, 1.0, 1.1, 1.6, 2.5, 5.5 67 | // result: -6.0, -2.0, -2.0, -1.0, -1.0, 1.0, 1.0, 2.0, 2.0, 6.0 68 | struct round_half_even 69 | { 70 | template 71 | T operator()(T const) const; 72 | }; 73 | 74 | // round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the odd neighbor 75 | // input: -5.5, -2.5, -1.6, -1.1, -1.0, 1.0, 1.1, 1.6, 2.5, 5.5 76 | // result: -5.0, -3.0, -2.0, -1.0, -1.0, 1.0, 1.0, 2.0, 3.0, 5.0 77 | struct round_half_odd 78 | { 79 | template 80 | T operator()(T const) const; 81 | }; 82 | 83 | // round nothing 84 | struct round_none 85 | { 86 | template 87 | constexpr T operator()(T const value) const { return value; } 88 | }; 89 | 90 | // specialization 91 | 92 | template <> 93 | inline float round_up::operator()(float const value) const 94 | { 95 | return value > 0.0f ? std::ceil(value) : std::floor(value); 96 | } 97 | 98 | template <> 99 | inline double round_up::operator()(double const value) const 100 | { 101 | return value > 0.0 ? std::ceil(value) : std::floor(value); 102 | } 103 | 104 | template <> 105 | inline long double round_up::operator()(long double const value) const 106 | { 107 | return value > 0.0l ? std::ceil(value) : std::floor(value); 108 | } 109 | 110 | template <> 111 | inline float round_down::operator()(float const value) const 112 | { 113 | return value > 0.0f ? std::floor(value) : std::ceil(value); 114 | } 115 | 116 | template <> 117 | inline double round_down::operator()(double const value) const 118 | { 119 | return value > 0.0 ? std::floor(value) : std::ceil(value); 120 | } 121 | 122 | template <> 123 | inline long double round_down::operator()(long double const value) const 124 | { 125 | return value > 0.0l ? std::floor(value) : std::ceil(value); 126 | } 127 | 128 | template <> 129 | inline float round_ceiling::operator()(float const value) const 130 | { 131 | return std::ceil(value); 132 | } 133 | 134 | template <> 135 | inline double round_ceiling::operator()(double const value) const 136 | { 137 | return std::ceil(value); 138 | } 139 | 140 | template <> 141 | inline long double round_ceiling::operator()(long double const value) const 142 | { 143 | return std::ceil(value); 144 | } 145 | 146 | template <> 147 | inline float round_floor::operator()(float const value) const 148 | { 149 | return std::floor(value); 150 | } 151 | 152 | template <> 153 | inline double round_floor::operator()(double const value) const 154 | { 155 | return std::floor(value); 156 | } 157 | 158 | template <> 159 | inline long double round_floor::operator()(long double const value) const 160 | { 161 | return std::floor(value); 162 | } 163 | 164 | template <> 165 | inline float round_half_up::operator()(float const value) const 166 | { 167 | return value > 0.0f ? std::floor(value + 0.5f) : std::ceil(value - 0.5f); 168 | } 169 | 170 | template <> 171 | inline double round_half_up::operator()(double const value) const 172 | { 173 | return value > 0.0 ? std::floor(value + 0.5) : std::ceil(value - 0.5); 174 | } 175 | 176 | template <> 177 | inline long double round_half_up::operator()(long double const value) const 178 | { 179 | return value > 0.0l ? std::floor(value + 0.5l) : std::ceil(value - 0.5l); 180 | } 181 | 182 | template <> 183 | inline float round_half_down::operator()(float const value) const 184 | { 185 | return value > 0.0f ? std::ceil(value - 0.5f) : std::floor(value + 0.5f); 186 | } 187 | 188 | template <> 189 | inline double round_half_down::operator()(double const value) const 190 | { 191 | return value > 0.0 ? std::ceil(value - 0.5) : std::floor(value + 0.5); 192 | } 193 | 194 | template <> 195 | inline long double round_half_down::operator()(long double const value) const 196 | { 197 | return value > 0.0l ? std::ceil(value - 0.5l) : std::floor(value + 0.5l); 198 | } 199 | 200 | template <> 201 | inline float round_half_even::operator()(float const value) const 202 | { 203 | return value - std::remainder(value, 1.0f); 204 | } 205 | 206 | template <> 207 | inline double round_half_even::operator()(double const value) const 208 | { 209 | return value - std::remainder(value, 1.0); 210 | } 211 | 212 | template <> 213 | inline long double round_half_even::operator()(long double const value) const 214 | { 215 | return value - std::remainder(value, 1.0l); 216 | } 217 | 218 | template <> 219 | inline float round_half_odd::operator()(float const value) const 220 | { 221 | auto r = std::remainder(value, 1.0f); 222 | return std::abs(r) == 0.5f ? value + r : std::floor(value + 0.5f); 223 | } 224 | 225 | template <> 226 | inline double round_half_odd::operator()(double const value) const 227 | { 228 | auto r = std::remainder(value, 1.0); 229 | return std::abs(r) == 0.5 ? value + r : std::floor(value + 0.5); 230 | } 231 | 232 | template <> 233 | inline long double round_half_odd::operator()(long double const value) const 234 | { 235 | auto r = std::remainder(value, 1.0l); 236 | return std::abs(r) == 0.5l ? value + r : std::floor(value + 0.5l); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /lgtm.yml: -------------------------------------------------------------------------------- 1 | path_classifiers: 2 | test: 3 | - "test" 4 | - exclude: "catch" 5 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.0) 2 | project(test_moneycpp) 3 | 4 | include_directories(${CMAKE_SOURCE_DIR}/include) 5 | include_directories(${CMAKE_SOURCE_DIR}/catch) 6 | 7 | if(BOOST_MULTIPRECISION OR BOOST_OPTIONAL) 8 | include_directories(${BOOST_INCLUDE_DIR}) 9 | endif() 10 | 11 | if(BOOST_MULTIPRECISION) 12 | add_definitions(-DHAS_BOOST_MULTIPRECISION) 13 | endif() 14 | 15 | if(BOOST_OPTIONAL) 16 | link_directories(${BOOST_LIB_DIR}) 17 | add_definitions(-DHAS_BOOST_OPTIONAL) 18 | endif() 19 | 20 | if(COUNTRY_AND_CURRENCY_DB) 21 | add_definitions(-DHAS_COUNTRY_AND_CURRENCY_DB) 22 | endif() 23 | 24 | file(GLOB headers ${CMAKE_SOURCE_DIR}/include/*.h ${CMAKE_SOURCE_DIR}/include/ext/*.h) 25 | file(GLOB SOURCES "*.cpp") #"include/*.cpp" 26 | 27 | add_executable(test_moneycpp ${SOURCES} ${headers}) 28 | 29 | if(BUILD_TESTS) 30 | enable_testing() 31 | 32 | add_test(NAME "test_moneycpp" COMMAND "test_moneycpp" "-r compact") 33 | set_tests_properties("test_moneycpp" 34 | PROPERTIES 35 | PASS_REGULAR_EXPRESSION "Passed all.*") 36 | set_tests_properties("test_moneycpp" 37 | PROPERTIES 38 | FAIL_REGULAR_EXPRESSION "Failed \\d+ test cases") 39 | set_tests_properties("test_moneycpp" 40 | PROPERTIES 41 | TIMEOUT 120) 42 | endif() -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | -------------------------------------------------------------------------------- /test/test_boost.cpp: -------------------------------------------------------------------------------- 1 | #ifndef HAS_COUNTRY_AND_CURRENCY_DB 2 | #define HAS_COUNTRY_AND_CURRENCY_DB 3 | #endif 4 | 5 | #include "money.h" 6 | #include "catch.hpp" 7 | 8 | #ifdef HAS_BOOST_MULTIPRECISION 9 | #include "ext\boost.h" 10 | #include 11 | 12 | using namespace moneycpp; 13 | 14 | TEST_CASE("Simple boost multiprecision test", "[boost]") 15 | { 16 | auto m1 = make_money("20.45"_dec, currency::USD); 17 | 18 | REQUIRE(m1.amount == "20.45"_dec); 19 | REQUIRE(m1.currency == currency::USD); 20 | } 21 | 22 | TEST_CASE("Boost decimal add money", "[boost][money]") 23 | { 24 | auto m1 = make_money("20.45"_dec, currency::USD); 25 | auto m2 = make_money("20.54"_dec, currency::USD); 26 | auto m3 = make_money("12.50"_dec, currency::USD); 27 | auto m4 = make_money("10.0"_dec, currency::EUR); 28 | 29 | REQUIRE((m1 + m2).amount == "40.99"_dec); 30 | REQUIRE((m1 + m3).amount == "32.95"_dec); 31 | REQUIRE_THROWS_AS(m1 + m4, currency_mismatch_error); 32 | } 33 | 34 | TEST_CASE("Boost decimal subtract money", "[boost][money]") 35 | { 36 | auto m1 = make_money("20.55"_dec, currency::USD); 37 | auto m2 = make_money("10.54"_dec, currency::USD); 38 | auto m3 = make_money("12.50"_dec, currency::USD); 39 | auto m4 = make_money("10.0"_dec, currency::EUR); 40 | 41 | REQUIRE((m1 - m2).amount == "10.01"_dec); 42 | REQUIRE((m1 - m3).amount == "8.05"_dec); 43 | REQUIRE_THROWS_AS(m1 - m4, currency_mismatch_error); 44 | } 45 | 46 | TEST_CASE("Boost decimal multiply money", "[boost][money]") 47 | { 48 | auto m1 = make_money("20.02"_dec, currency::USD); 49 | 50 | auto m2 = m1 * 2; 51 | auto m3 = m1 * 1.5; 52 | 53 | REQUIRE(m2.amount == "40.04"_dec); 54 | REQUIRE(m2.currency == currency::USD); 55 | 56 | REQUIRE(m3.amount == "30.03"_dec); 57 | } 58 | 59 | TEST_CASE("Boost decimal divide money", "[boost][money]") 60 | { 61 | auto m1 = make_money("20.10"_dec, currency::USD); 62 | 63 | auto m2 = m1 / 2; 64 | auto m3 = m1 / 2.5; 65 | 66 | REQUIRE(m2.amount == "10.05"_dec); 67 | REQUIRE(m2.currency == currency::USD); 68 | 69 | REQUIRE(m3.amount == "8.04"_dec); 70 | REQUIRE_THROWS_AS(m1 / "0"_dec, std::runtime_error); 71 | } 72 | 73 | template 74 | static std::vector inputs 75 | { T("5.5"), T("2.5"), T("1.6"), T("1.1"), T("1.0"), T("-1.0"), T("-1.1"), T("-1.6"), T("-2.5"), T("-5.5") }; 76 | 77 | template 78 | static std::vector exp_round_up 79 | { T("6.0"), T("3.0"), T("2.0"), T("2.0"), T("1.0"), T("-1.0"), T("-2.0"), T("-2.0"), T("-3.0"), T("-6.0") }; 80 | 81 | template 82 | static std::vector exp_round_down 83 | { T("5.0"), T("2.0"), T("1.0"), T("1.0"), T("1.0"), T("-1.0"), T("-1.0"), T("-1.0"), T("-2.0"), T("-5.0") }; 84 | 85 | template 86 | static std::vector exp_round_ceil 87 | { T("6.0"), T("3.0"), T("2.0"), T("2.0"), T("1.0"), T("-1.0"), T("-1.0"), T("-1.0"), T("-2.0"), T("-5.0") }; 88 | 89 | template 90 | static std::vector exp_round_floor 91 | { T("5.0"), T("2.0"), T("1.0"), T("1.0"), T("1.0"), T("-1.0"), T("-2.0"), T("-2.0"), T("-3.0"), T("-6.0") }; 92 | 93 | template 94 | static std::vector exp_round_half_up 95 | { T("6.0"), T("3.0"), T("2.0"), T("1.0"), T("1.0"), T("-1.0"), T("-1.0"), T("-2.0"), T("-3.0"), T("-6.0") }; 96 | 97 | template 98 | static std::vector exp_round_half_down 99 | { T("5.0"), T("2.0"), T("2.0"), T("1.0"), T("1.0"), T("-1.0"), T("-1.0"), T("-2.0"), T("-2.0"), T("-5.0") }; 100 | 101 | template 102 | static std::vector exp_round_half_even 103 | { T("6.0"), T("2.0"), T("2.0"), T("1.0"), T("1.0"), T("-1.0"), T("-1.0"), T("-2.0"), T("-2.0"), T("-6.0") }; 104 | 105 | template 106 | static std::vector exp_round_half_odd 107 | { T("5.0"), T("3.0"), T("2.0"), T("1.0"), T("1.0"), T("-1.0"), T("-1.0"), T("-2.0"), T("-3.0"), T("-5.0") }; 108 | 109 | TEST_CASE("Boost decimal round up test", "[rounding][boost]") 110 | { 111 | for (size_t i = 0; i < inputs.size(); ++i) 112 | { 113 | auto r = round_up()(inputs[i]); 114 | REQUIRE(r == exp_round_up[i]); 115 | } 116 | } 117 | 118 | TEST_CASE("Boost decimal round down test", "[rounding][boost]") 119 | { 120 | for (size_t i = 0; i < inputs.size(); ++i) 121 | { 122 | auto r = round_down()(inputs[i]); 123 | REQUIRE(r == exp_round_down[i]); 124 | } 125 | } 126 | 127 | TEST_CASE("Boost decimal round ceil test", "[rounding][boost]") 128 | { 129 | for (size_t i = 0; i < inputs.size(); ++i) 130 | { 131 | auto r = round_ceiling()(inputs[i]); 132 | REQUIRE(r == exp_round_ceil[i]); 133 | } 134 | } 135 | 136 | TEST_CASE("Boost decimal round floor test", "[rounding][boost]") 137 | { 138 | for (size_t i = 0; i < inputs.size(); ++i) 139 | { 140 | auto r = round_floor()(inputs[i]); 141 | REQUIRE(r == exp_round_floor[i]); 142 | } 143 | } 144 | 145 | TEST_CASE("Boost decimal round half up test", "[rounding][boost]") 146 | { 147 | for (size_t i = 0; i < inputs.size(); ++i) 148 | { 149 | auto r = round_half_up()(inputs[i]); 150 | REQUIRE(r == exp_round_half_up[i]); 151 | } 152 | } 153 | 154 | TEST_CASE("Boost decimal round half down test", "[rounding][boost]") 155 | { 156 | for (size_t i = 0; i < inputs.size(); ++i) 157 | { 158 | auto r = round_half_down()(inputs[i]); 159 | REQUIRE(r == exp_round_half_down[i]); 160 | } 161 | } 162 | 163 | TEST_CASE("Boost decimal round half even test", "[rounding][boost]") 164 | { 165 | for (size_t i = 0; i < inputs.size(); ++i) 166 | { 167 | auto r = round_half_even()(inputs[i]); 168 | REQUIRE(r == exp_round_half_even[i]); 169 | } 170 | } 171 | 172 | TEST_CASE("Boost decimal round half odd test", "[rounding][boost]") 173 | { 174 | for (size_t i = 0; i < inputs.size(); ++i) 175 | { 176 | auto r = round_half_odd()(inputs[i]); 177 | REQUIRE(r == exp_round_half_odd[i]); 178 | } 179 | } 180 | 181 | TEST_CASE("Boost decimal rounding policy none test", "[policy][boost]") 182 | { 183 | std::vector> values 184 | { 185 | { "0.0"_dec, "0.0"_dec }, 186 | { "1.5"_dec, "1.5"_dec }, 187 | { "1.1"_dec, "1.1"_dec }, 188 | { "-1.12345"_dec, "-1.12345"_dec } 189 | }; 190 | 191 | for (auto const p : values) 192 | { 193 | auto m = make_money(p.first, currency::USD); 194 | auto r = rounding_policy_none()(m); 195 | 196 | REQUIRE(p.second == r.amount); 197 | } 198 | } 199 | 200 | TEST_CASE("Boost decimal rounding policy standard test", "[policy][boost]") 201 | { 202 | std::vector> values 203 | { 204 | { "0.0"_dec, "0.0"_dec }, 205 | { "1.5"_dec, "1.5"_dec }, 206 | { "1.1"_dec, "1.1"_dec }, 207 | { "-1.12345"_dec, "-1.1234"_dec }, 208 | { "-2.234567"_dec, "-2.2345"_dec }, 209 | { "4.88999"_dec, "4.8900"_dec } 210 | }; 211 | 212 | for (auto const p : values) 213 | { 214 | auto m = make_money(p.first, currency::USD); 215 | auto r = rounding_policy_standard(round_ceiling())(m); 216 | 217 | REQUIRE(p.second == r.amount); 218 | } 219 | } 220 | 221 | TEST_CASE("Boost decimal rounding policy to currency digits test", "[policy][boost]") 222 | { 223 | std::vector> values 224 | { 225 | { "0.00"_dec, "0.00"_dec }, 226 | { "1.50"_dec, "1.50"_dec }, 227 | { "1.10"_dec, "1.10"_dec }, 228 | { "-1.12345"_dec, "-1.12"_dec }, 229 | { "-2.234567"_dec, "-2.23"_dec }, 230 | { "4.88999"_dec, "4.89"_dec }, 231 | { "3.12001"_dec, "3.13"_dec }, 232 | }; 233 | 234 | for (auto const p : values) 235 | { 236 | auto m = make_money(p.first, currency::USD); 237 | auto r = rounding_policy_to_currency_digits(round_ceiling())(m); 238 | 239 | REQUIRE(p.second == r.amount); 240 | } 241 | } 242 | 243 | TEST_CASE("Boost decimal rounding policy to currency with multi digits test", "[policy][boost]") 244 | { 245 | std::vector> values 246 | { 247 | { "12.123456789012345678901"_dec, "12.123456789012345678"_dec }, 248 | { "-12.123456789012345678901"_dec, "-12.123456789012345679"_dec }, 249 | }; 250 | 251 | for (auto const p : values) 252 | { 253 | auto m = make_money(p.first, currency::ETH); 254 | auto r = rounding_policy_to_currency_digits(round_floor())(m); 255 | 256 | REQUIRE(p.second == r.amount); 257 | } 258 | } 259 | 260 | TEST_CASE("Boost decimal invalid exchange test", "[exchange][boost]") 261 | { 262 | auto m = make_money(decimal(20U), currency::USD); 263 | REQUIRE_THROWS_AS( 264 | exchange_money(m, currency::EUR, decimal(0U), rounding_policy_standard(round_ceiling())), 265 | std::runtime_error); 266 | REQUIRE_THROWS_AS( 267 | exchange_money(m, currency::EUR, decimal(-1), rounding_policy_standard(round_ceiling())), 268 | std::runtime_error); 269 | } 270 | 271 | TEST_CASE("Boost decimal exchange same currency test", "[exchange][boost]") 272 | { 273 | auto m1 = make_money(decimal(20U), currency::USD); 274 | auto m2 = exchange_money(m1, currency::USD, decimal(2U), rounding_policy_none(round_ceiling())); 275 | auto m3 = exchange_money(m1, currency::USD, decimal(2U), rounding_policy_standard(round_ceiling())); 276 | auto m4 = exchange_money(m1, currency::USD, decimal(2U), rounding_policy_to_currency_digits(round_ceiling())); 277 | 278 | REQUIRE(m1 == m2); 279 | REQUIRE(m1 == m3); 280 | REQUIRE(m1 == m4); 281 | } 282 | 283 | TEST_CASE("Boost decimal exchange test", "[exchange][boost]") 284 | { 285 | std::vector> values 286 | { 287 | { "0.0"_dec, "1.0"_dec, "0.00"_dec }, 288 | 289 | { "20.0"_dec, "1.0"_dec, "20.0"_dec }, 290 | { "20.12"_dec, "1.0"_dec, "20.12"_dec }, 291 | { "20.0"_dec, "1.1"_dec, "22.0"_dec }, 292 | { "22.55"_dec, "1.2345"_dec, "27.84"_dec }, 293 | { "101.95"_dec, "1.1149"_dec, "113.67"_dec }, 294 | 295 | { "-20.0"_dec, "1.0"_dec, "-20.0"_dec }, 296 | { "-20.12"_dec, "1.0"_dec, "-20.12"_dec }, 297 | { "-20.0"_dec, "1.1"_dec, "-22.0"_dec }, 298 | { "-22.55"_dec, "1.2345"_dec, "-27.83"_dec }, 299 | { "-101.95"_dec, "1.1149"_dec, "-113.66"_dec }, 300 | 301 | }; 302 | 303 | for (auto const & t : values) 304 | { 305 | auto[amount, rate, result] = t; 306 | 307 | auto m1 = make_money(amount, currency::USD); 308 | auto m2 = exchange_money(m1, currency::EUR, rate, rounding_policy_to_currency_digits(round_ceiling())); 309 | 310 | REQUIRE(m2.amount == result); 311 | } 312 | } 313 | 314 | TEST_CASE("Boost decimal exchange extra decimals test", "[exchange][boost]") 315 | { 316 | std::vector> values 317 | { 318 | { "0.0"_dec, "1.0"_dec, "0.0"_dec }, 319 | 320 | { "20.0"_dec, "1.0"_dec, "20.0000"_dec }, 321 | { "20.12"_dec, "1.0"_dec, "20.1200"_dec }, 322 | { "20.0"_dec, "1.1"_dec, "22.0000"_dec }, 323 | { "22.55"_dec, "1.2345"_dec, "27.8380"_dec }, 324 | { "101.95"_dec, "1.1149"_dec, "113.6641"_dec }, 325 | 326 | { "-20.0"_dec, "1.0"_dec, "-20.0000"_dec }, 327 | { "-20.12"_dec, "1.0"_dec, "-20.1200"_dec }, 328 | { "-20.0"_dec, "1.1"_dec, "-22.0000"_dec }, 329 | { "-22.55"_dec, "1.2345"_dec, "-27.8379"_dec }, 330 | { "-101.95"_dec, "1.1149"_dec, "-113.6640"_dec }, 331 | }; 332 | 333 | for (auto const & t : values) 334 | { 335 | auto[amount, rate, result] = t; 336 | 337 | auto m1 = make_money(amount, currency::USD); 338 | auto m2 = exchange_money(m1, currency::CLF, rate, rounding_policy_standard(round_ceiling())); 339 | 340 | REQUIRE(m2.amount == result); 341 | } 342 | } 343 | 344 | #endif -------------------------------------------------------------------------------- /test/test_country.cpp: -------------------------------------------------------------------------------- 1 | #ifndef HAS_COUNTRY_AND_CURRENCY_DB 2 | #define HAS_COUNTRY_AND_CURRENCY_DB 3 | #endif 4 | 5 | #include "country.h" 6 | #include "catch.hpp" 7 | 8 | #include 9 | #include 10 | 11 | using namespace moneycpp; 12 | 13 | TEST_CASE("Equality country", "[country]") 14 | { 15 | REQUIRE(country::AU == country::AU); 16 | REQUIRE(country::AU != country::AR); 17 | 18 | country_unit cu1 { 1, "VA", "VAX", "Virtual 1", true }; 19 | country_unit cu2 { 2, "VA", "VAX", "Virtual 2", true }; 20 | country_unit cu3 { 1, "VB", "VAX", "Virtual 1", true }; 21 | country_unit cu4 { 1, "VA", "VBX", "Virtual 1", true }; 22 | 23 | REQUIRE(cu1 != cu2); 24 | REQUIRE(cu1 != cu3); 25 | REQUIRE(cu1 != cu4); 26 | } 27 | 28 | TEST_CASE("Unique countries", "[country]") 29 | { 30 | auto db = std::vector{ country::countries }; 31 | 32 | std::set codes; 33 | std::set alpha2s; 34 | std::set alpha3s; 35 | 36 | for (auto const & cu : db) 37 | { 38 | if (cu.code > 0) 39 | { 40 | codes.insert(cu.code); 41 | alpha2s.insert(cu.alpha2); 42 | alpha3s.insert(cu.alpha3); 43 | } 44 | } 45 | 46 | auto count = std::count_if( 47 | std::begin(db), std::end(db), 48 | [](country_unit const & cu) {return cu.code > 0; }); 49 | 50 | REQUIRE(codes.size() == count); 51 | REQUIRE(alpha2s.size() == count); 52 | REQUIRE(alpha3s.size() == count); 53 | } 54 | 55 | TEST_CASE("Test find country", "[find][country]") 56 | { 57 | { 58 | auto cu1 = find_country("AU"); 59 | REQUIRE(cu1.has_value()); 60 | REQUIRE(cu1.value() == country::AU); 61 | 62 | auto cu2 = find_country("AA"); 63 | REQUIRE(!cu2.has_value()); 64 | } 65 | 66 | { 67 | auto cu1 = find_country(36); 68 | REQUIRE(cu1.has_value()); 69 | REQUIRE(cu1.value() == country::AU); 70 | 71 | auto cu2 = find_country(1); 72 | REQUIRE(!cu2.has_value()); 73 | } 74 | 75 | { 76 | auto cu1 = find_country("US"); 77 | auto cu2 = find_country(840); 78 | REQUIRE(cu1 == cu2); 79 | } 80 | } 81 | 82 | TEST_CASE("Test find country iter", "[find][country]") 83 | { 84 | { 85 | auto cu1 = find_country( 86 | std::cbegin(country::countries), 87 | std::cend(country::countries), 88 | "AU"); 89 | REQUIRE(cu1 != std::cend(country::countries)); 90 | REQUIRE(*cu1 == country::AU); 91 | 92 | auto cu2 = find_country( 93 | std::cbegin(country::countries), 94 | std::cend(country::countries), 95 | "AA"); 96 | REQUIRE(cu2 == std::cend(country::countries)); 97 | } 98 | 99 | { 100 | auto cu1 = find_country( 101 | std::cbegin(country::countries), 102 | std::cend(country::countries), 103 | 36); 104 | REQUIRE(cu1 != std::cend(country::countries)); 105 | REQUIRE(*cu1 == country::AU); 106 | 107 | auto cu2 = find_country( 108 | std::cbegin(country::countries), 109 | std::cend(country::countries), 110 | 1); 111 | REQUIRE(cu2 == std::cend(country::countries)); 112 | } 113 | } 114 | 115 | TEST_CASE("Test find my country", "[find][country]") 116 | { 117 | std::vector my_countries{country::countries}; 118 | my_countries.emplace_back(country_unit{ 1001, "OT", "OTH", "Otheria", true }); 119 | 120 | auto cu1 = find_country( 121 | std::cbegin(my_countries), 122 | std::cend(my_countries), 123 | "OT"); 124 | 125 | auto cu2 = find_country( 126 | std::cbegin(my_countries), 127 | std::cend(my_countries), 128 | 1001); 129 | 130 | REQUIRE(cu1 != std::cend(my_countries)); 131 | REQUIRE(cu1 == cu2); 132 | } -------------------------------------------------------------------------------- /test/test_country_currency.cpp: -------------------------------------------------------------------------------- 1 | #ifndef HAS_COUNTRY_AND_CURRENCY_DB 2 | #define HAS_COUNTRY_AND_CURRENCY_DB 3 | #endif 4 | 5 | #include "country_currency.h" 6 | #include "catch.hpp" 7 | 8 | using namespace moneycpp; 9 | 10 | TEST_CASE("Find all country currencies test", "[country][currency]") 11 | { 12 | { 13 | auto s = country::find_country_currencies(country::AF); 14 | REQUIRE(s.size() == 1); 15 | REQUIRE(*s.begin() == currency::AFN); 16 | } 17 | 18 | { 19 | auto s = country::find_country_currencies(country::BO); 20 | REQUIRE(s.size() == 2); 21 | REQUIRE(*s.begin() == currency::BOB); 22 | REQUIRE(*std::next(s.begin()) == currency::BOV); 23 | } 24 | 25 | { 26 | country_currency_map mymap{ country::currencies }; 27 | 28 | country_unit vc{ 1, "VX", "VAX", "Virtual Country", true }; 29 | currency_unit cu1{ "VIR", 1001, 2, "Virtual Currency 1" }; 30 | currency_unit cu2{ "VIX", 1002, 2, "Virtual Currency 2" }; 31 | 32 | mymap.insert({ vc, cu1 }); 33 | mymap.insert({ vc, cu2 }); 34 | 35 | auto s = country::find_country_currencies(mymap, vc); 36 | REQUIRE(s.size() == 2); 37 | REQUIRE(*s.begin() == cu1); 38 | REQUIRE(*std::next(s.begin()) == cu2); 39 | } 40 | } 41 | 42 | TEST_CASE("Find country currency equal range test", "[country][currency]") 43 | { 44 | { 45 | auto r = country::country_currency_equal_range( 46 | country::currencies, 47 | country::AF); 48 | 49 | REQUIRE(r.first != std::end(country::currencies)); 50 | REQUIRE(r.second != std::end(country::currencies)); 51 | REQUIRE(std::distance(r.first, r.second) == 1); 52 | REQUIRE(r.first->second == currency::AFN); 53 | } 54 | 55 | { 56 | auto r = country::country_currency_equal_range( 57 | country::currencies, 58 | country::BO); 59 | 60 | REQUIRE(r.first != std::end(country::currencies)); 61 | REQUIRE(r.second != std::end(country::currencies)); 62 | REQUIRE(std::distance(r.first, r.second) == 2); 63 | REQUIRE(r.first->second == currency::BOB); 64 | REQUIRE(std::next(r.first)->second == currency::BOV); 65 | } 66 | 67 | { 68 | country_currency_map mymap{ country::currencies }; 69 | 70 | country_unit vc{ 1, "VX", "VAX", "Virtual Country", true }; 71 | currency_unit cu1{ "VIR", 1001, 2, "Virtual Currency 1" }; 72 | currency_unit cu2{ "VIX", 1002, 2, "Virtual Currency 2" }; 73 | 74 | mymap.insert({ vc, cu1 }); 75 | mymap.insert({ vc, cu2 }); 76 | 77 | auto r = country::country_currency_equal_range(mymap, vc); 78 | 79 | REQUIRE(r.first != std::end(mymap)); 80 | REQUIRE(r.second != std::end(mymap)); 81 | REQUIRE(std::distance(r.first, r.second) == 2); 82 | REQUIRE(r.first->second == cu1); 83 | REQUIRE(std::next(r.first)->second == cu2); 84 | } 85 | } 86 | 87 | TEST_CASE("Find main country currency test", "[country][currency]") 88 | { 89 | REQUIRE(country::find_country_currency(country::AF) == currency::AFN); 90 | REQUIRE(country::find_country_currency(country::BO) == currency::BOB); 91 | REQUIRE_THROWS_AS(country::find_country_currency(country::AQ), currency_not_found); 92 | } 93 | 94 | TEST_CASE("Check orphan countries", "[country][currency]") 95 | { 96 | std::set unique_countries; 97 | for (auto const & kvp : country::currencies) 98 | unique_countries.insert(kvp.first); 99 | 100 | std::set all_countries{country::countries}; 101 | 102 | std::vector diff; 103 | 104 | std::set_difference( 105 | std::begin(all_countries), std::end(all_countries), 106 | std::begin(unique_countries), std::end(unique_countries), 107 | std::back_inserter(diff)); 108 | 109 | REQUIRE(diff.size() == 1); 110 | REQUIRE(diff[0] == country::AQ); 111 | } -------------------------------------------------------------------------------- /test/test_currency.cpp: -------------------------------------------------------------------------------- 1 | #ifndef HAS_COUNTRY_AND_CURRENCY_DB 2 | #define HAS_COUNTRY_AND_CURRENCY_DB 3 | #endif 4 | 5 | #include "currency.h" 6 | #include "catch.hpp" 7 | 8 | #include 9 | #include 10 | 11 | using namespace moneycpp; 12 | 13 | TEST_CASE("Equality currency", "[currency]") 14 | { 15 | REQUIRE(currency::USD == currency::USD); 16 | REQUIRE(currency::USD != currency::EUR); 17 | 18 | currency_unit cu1{ "VIR", 1001, 2, "Virtual 1" }; 19 | currency_unit cu2{ "VIX", 1001, 2, "Virtual 1" }; 20 | currency_unit cu3{ "VIR", 1002, 2, "Virtual 1" }; 21 | currency_unit cu4{ "VIR", 1001, 3, "Virtual 1" }; 22 | currency_unit cu5{ "VIR", 1001, 2, "Virtual 2" }; 23 | 24 | REQUIRE(cu1 != cu2); 25 | REQUIRE(cu1 != cu3); 26 | REQUIRE(cu1 != cu4); 27 | REQUIRE(cu1 != cu5); 28 | } 29 | 30 | TEST_CASE("Unique currencies", "[currency]") 31 | { 32 | auto db = std::vector {currency::currencies}; 33 | 34 | std::set codes; 35 | std::set names; 36 | for(auto const & cu : db) 37 | { 38 | if (cu.number > 0) 39 | { 40 | codes.insert(cu.number); 41 | names.insert(cu.name); 42 | } 43 | } 44 | 45 | auto count = std::count_if( 46 | std::begin(db), std::end(db), 47 | [](currency_unit const & cu) {return cu.number > 0; }); 48 | 49 | REQUIRE(codes.size() == count); 50 | REQUIRE(names.size() == count); 51 | } 52 | 53 | TEST_CASE("Test find currency", "[find][currency]") 54 | { 55 | { 56 | auto cu1 = find_currency("USD"); 57 | REQUIRE(cu1.has_value()); 58 | REQUIRE(cu1.value() == currency::USD); 59 | 60 | auto cu2 = find_currency("DSU"); 61 | REQUIRE(!cu2.has_value()); 62 | } 63 | 64 | { 65 | auto cu1 = find_currency(840); 66 | REQUIRE(cu1.has_value()); 67 | REQUIRE(cu1.value() == currency::USD); 68 | 69 | auto cu2 = find_currency(1); 70 | REQUIRE(!cu2.has_value()); 71 | } 72 | 73 | { 74 | auto cu1 = find_currency("EUR"); 75 | auto cu2 = find_currency(978); 76 | REQUIRE(cu1 == cu2); 77 | } 78 | } 79 | 80 | TEST_CASE("Test find currency iter", "[find][currency]") 81 | { 82 | { 83 | auto cu1 = find_currency( 84 | std::cbegin(currency::currencies), 85 | std::cend(currency::currencies), 86 | "USD"); 87 | REQUIRE(cu1 != std::cend(currency::currencies)); 88 | REQUIRE(*cu1 == currency::USD); 89 | 90 | auto cu2 = find_currency( 91 | std::cbegin(currency::currencies), 92 | std::cend(currency::currencies), 93 | "DSU"); 94 | REQUIRE(cu2 == std::cend(currency::currencies)); 95 | } 96 | 97 | { 98 | auto cu1 = find_currency( 99 | std::cbegin(currency::currencies), 100 | std::cend(currency::currencies), 101 | 840); 102 | REQUIRE(cu1 != std::cend(currency::currencies)); 103 | REQUIRE(*cu1 == currency::USD); 104 | 105 | auto cu2 = find_currency( 106 | std::cbegin(currency::currencies), 107 | std::cend(currency::currencies), 108 | 1); 109 | REQUIRE(cu2 == std::cend(currency::currencies)); 110 | } 111 | } 112 | 113 | TEST_CASE("Test find my currency", "[find][currency]") 114 | { 115 | std::vector my_currencies{ currency::currencies }; 116 | my_currencies.emplace_back(currency_unit{ "VIR", 1001, 2, "Virtual Currency" }); 117 | 118 | auto cu1 = find_currency( 119 | std::cbegin(my_currencies), 120 | std::cend(my_currencies), 121 | "VIR"); 122 | 123 | auto cu2 = find_currency( 124 | std::cbegin(my_currencies), 125 | std::cend(my_currencies), 126 | 1001); 127 | 128 | REQUIRE(cu1 != std::cend(my_currencies)); 129 | REQUIRE(cu1 == cu2); 130 | } -------------------------------------------------------------------------------- /test/test_money.cpp: -------------------------------------------------------------------------------- 1 | #ifndef HAS_COUNTRY_AND_CURRENCY_DB 2 | #define HAS_COUNTRY_AND_CURRENCY_DB 3 | #endif 4 | 5 | #include "money.h" 6 | #include "catch.hpp" 7 | #include 8 | 9 | using namespace moneycpp; 10 | 11 | template 12 | inline void REQUIRE_EQ(T const v1, T const v2, T const epsilon = 0.00001) 13 | { 14 | REQUIRE(std::fabs(v1 - v2) <= epsilon); 15 | } 16 | 17 | TEST_CASE("Make money", "[money]") 18 | { 19 | auto m1 = make_money(20.0, currency::USD); 20 | REQUIRE(m1.amount == 20.0); 21 | REQUIRE(m1.currency == currency::USD); 22 | 23 | auto m2 = make_money(20.0, find_currency("USD").value()); 24 | REQUIRE(m2.amount == 20.0); 25 | REQUIRE(m2.currency == currency::USD); 26 | } 27 | 28 | TEST_CASE("Equality money", "[money]") 29 | { 30 | auto m1 = make_money(20.0, currency::USD); 31 | auto m2 = make_money(20.0, currency::USD); 32 | auto m3 = make_money(30.0, currency::USD); 33 | auto m4 = make_money(20.0, currency::EUR); 34 | 35 | REQUIRE(m1 == m2); 36 | REQUIRE(m1 != m3); 37 | REQUIRE_THROWS_AS(m1 == m4, currency_mismatch_error); 38 | 39 | REQUIRE(m1 <= m2); 40 | REQUIRE(m1 < m3); 41 | REQUIRE(m3 > m2); 42 | REQUIRE(m3 >= m3); 43 | REQUIRE_THROWS_AS(m1 < m4, currency_mismatch_error); 44 | } 45 | 46 | TEST_CASE("Add money", "[money]") 47 | { 48 | auto m1 = make_money(20.0, currency::USD); 49 | auto m2 = make_money(20.0, currency::USD); 50 | auto m3 = make_money(12.50, currency::USD); 51 | auto m4 = make_money(10.0, currency::EUR); 52 | 53 | REQUIRE((m1 + m2).amount == 40.0); 54 | REQUIRE((m1 + m3).amount == 32.50); 55 | REQUIRE_THROWS_AS(m1 + m4, currency_mismatch_error); 56 | } 57 | 58 | TEST_CASE("Subtract money", "[money]") 59 | { 60 | auto m1 = make_money(20.0, currency::USD); 61 | auto m2 = make_money(20.0, currency::USD); 62 | auto m3 = make_money(12.50, currency::USD); 63 | auto m4 = make_money(10.0, currency::EUR); 64 | 65 | REQUIRE((m1 - m2).amount == 0.0); 66 | REQUIRE((m1 - m3).amount == 7.50); 67 | REQUIRE_THROWS_AS(m1 - m4, currency_mismatch_error); 68 | } 69 | 70 | TEST_CASE("Multiply money", "[money]") 71 | { 72 | auto m1 = make_money(20.0, currency::USD); 73 | 74 | auto m2 = m1 * 2; 75 | auto m3 = m1 * 1.5; 76 | 77 | REQUIRE(m2.amount == 40.0); 78 | REQUIRE(m2.currency == currency::USD); 79 | 80 | REQUIRE(m3.amount == 30.0); 81 | } 82 | 83 | TEST_CASE("Divide money", "[money]") 84 | { 85 | auto m1 = make_money(20.0, currency::USD); 86 | 87 | auto m2 = m1 / 2; 88 | auto m3 = m1 / 2.5; 89 | 90 | REQUIRE(m2.amount == 10.0); 91 | REQUIRE(m2.currency == currency::USD); 92 | 93 | REQUIRE(m3.amount == 8.0); 94 | REQUIRE_THROWS_AS(m1 / 0, std::runtime_error); 95 | } 96 | 97 | TEST_CASE("Test rounding policy none", "[policy]") 98 | { 99 | std::vector> values 100 | { 101 | { 0.0, 0.0 }, 102 | { 1.5, 1.5 }, 103 | { 1.1, 1.1 }, 104 | { -1.12345, -1.12345 } 105 | }; 106 | 107 | for (auto const p : values) 108 | { 109 | auto m = make_money(p.first, currency::USD); 110 | auto r = rounding_policy_none(round_none())(m); 111 | 112 | REQUIRE_EQ(p.second, r.amount); 113 | } 114 | } 115 | 116 | TEST_CASE("Test rounding policy standard", "[policy]") 117 | { 118 | std::vector> values 119 | { 120 | { 0.0, 0.0 }, 121 | { 1.5, 1.5 }, 122 | { 1.1, 1.1 }, 123 | { -1.12345, -1.1234 }, 124 | { -2.234567, -2.2345 }, 125 | { 4.88999, 4.8900 } 126 | }; 127 | 128 | for (auto const p : values) 129 | { 130 | auto m = make_money(p.first, currency::USD); 131 | auto r = rounding_policy_standard(round_ceiling())(m); 132 | 133 | REQUIRE_EQ(p.second, r.amount); 134 | } 135 | } 136 | 137 | TEST_CASE("Test rounding policy to currency digits", "[policy]") 138 | { 139 | std::vector> values 140 | { 141 | { 0.00, 0.00 }, 142 | { 1.50, 1.50 }, 143 | { 1.10, 1.11 }, 144 | { -1.12345, -1.12 }, 145 | { -2.234567, -2.23 }, 146 | { 4.88999, 4.89 }, 147 | { 3.12001, 3.13 }, 148 | }; 149 | 150 | for (auto const p : values) 151 | { 152 | auto m = make_money(p.first, currency::USD); 153 | auto r = rounding_policy_to_currency_digits(round_ceiling())(m); 154 | 155 | REQUIRE_EQ(p.second, r.amount); 156 | } 157 | } 158 | 159 | TEST_CASE("Test invalid exchange", "[exchange]") 160 | { 161 | auto m = make_money(20.0, currency::USD); 162 | REQUIRE_THROWS_AS( 163 | exchange_money(m, currency::EUR, 0.0, rounding_policy_standard(round_ceiling())), 164 | std::runtime_error); 165 | REQUIRE_THROWS_AS( 166 | exchange_money(m, currency::EUR, -1.0, rounding_policy_standard(round_ceiling())), 167 | std::runtime_error); 168 | } 169 | 170 | TEST_CASE("Test exchange same currency", "[exchange]") 171 | { 172 | auto m1 = make_money(20.0, currency::USD); 173 | auto m2 = exchange_money(m1, currency::USD, 2.0, rounding_policy_none(round_ceiling())); 174 | auto m3 = exchange_money(m1, currency::USD, 2.0, rounding_policy_standard(round_ceiling())); 175 | auto m4 = exchange_money(m1, currency::USD, 2.0, rounding_policy_to_currency_digits(round_ceiling())); 176 | 177 | REQUIRE(m1 == m2); 178 | REQUIRE(m1 == m3); 179 | REQUIRE(m1 == m4); 180 | } 181 | 182 | TEST_CASE("Test exchange", "[exchange]") 183 | { 184 | std::vector> values 185 | { 186 | { 0.0, 1.0, 0.00 }, 187 | 188 | { 20.0, 1.0, 20.0 }, 189 | { 20.12, 1.0, 20.12 }, 190 | { 20.0, 1.1, 22.0 }, 191 | { 22.55, 1.2345, 27.84 }, 192 | { 101.95, 1.1149, 113.67 }, 193 | 194 | { -20.0, 1.0, -20.0 }, 195 | { -20.12, 1.0, -20.12 }, 196 | { -20.0, 1.1, -22.0 }, 197 | { -22.55, 1.2345, -27.83 }, 198 | { -101.95, 1.1149, -113.66 }, 199 | 200 | }; 201 | 202 | for (auto const & t : values) 203 | { 204 | auto[amount, rate, result] = t; 205 | 206 | auto m1 = make_money(amount, currency::USD); 207 | auto m2 = exchange_money(m1, currency::EUR, rate, rounding_policy_to_currency_digits(round_ceiling())); 208 | 209 | REQUIRE_EQ(m2.amount, result); 210 | } 211 | } 212 | 213 | TEST_CASE("Test exchange extra decimals", "[exchange]") 214 | { 215 | std::vector> values 216 | { 217 | { 0.0, 1.0, 0.0 }, 218 | 219 | { 20.0, 1.0, 20.0000 }, 220 | { 20.12, 1.0, 20.1200 }, 221 | { 20.0, 1.1, 22.0000 }, 222 | { 22.55, 1.2345, 27.8380 }, 223 | { 101.95, 1.1149, 113.6641 }, 224 | 225 | { -20.0, 1.0, -20.0000 }, 226 | { -20.12, 1.0, -20.1200 }, 227 | { -20.0, 1.1, -22.0000 }, 228 | { -22.55, 1.2345, -27.8379 }, 229 | { -101.95, 1.1149, -113.6640 }, 230 | }; 231 | 232 | for (auto const & t : values) 233 | { 234 | auto[amount, rate, result] = t; 235 | 236 | auto m1 = make_money(amount, currency::USD); 237 | auto m2 = exchange_money(m1, currency::CLF, rate, rounding_policy_standard(round_ceiling())); 238 | 239 | REQUIRE_EQ(m2.amount, result); 240 | } 241 | } 242 | 243 | TEST_CASE("Test min", "[money]") 244 | { 245 | auto m1 = make_money(20.0, currency::USD); 246 | auto m2 = make_money(30.25, currency::USD); 247 | auto m3 = make_money(42.5, currency::EUR); 248 | 249 | auto const & r = min(m1, m2); 250 | REQUIRE(r == m1); 251 | REQUIRE_THROWS_AS(min(m1, m3), currency_mismatch_error); 252 | } 253 | 254 | TEST_CASE("Test max", "[money]") 255 | { 256 | auto m1 = make_money(20.0, currency::USD); 257 | auto m2 = make_money(30.25, currency::USD); 258 | auto m3 = make_money(42.5, currency::EUR); 259 | 260 | auto const & r = max(m1, m2); 261 | REQUIRE(r == m2); 262 | REQUIRE_THROWS_AS(max(m1, m3), currency_mismatch_error); 263 | } -------------------------------------------------------------------------------- /test/test_rounding.cpp: -------------------------------------------------------------------------------- 1 | #include "rounding.h" 2 | #include "catch.hpp" 3 | #include 4 | 5 | using namespace moneycpp; 6 | 7 | template 8 | static std::vector inputs 9 | { T(5.5), T(2.5), T(1.6), T(1.1), T(1.0), T(-1.0), T(-1.1), T(-1.6), T(-2.5), T(-5.5) }; 10 | 11 | template 12 | static std::vector exp_round_up 13 | { T(6.0), T(3.0), T(2.0), T(2.0), T(1.0), T(-1.0), T(-2.0), T(-2.0), T(-3.0), T(-6.0) }; 14 | 15 | template 16 | static std::vector exp_round_down 17 | { T(5.0), T(2.0), T(1.0), T(1.0), T(1.0), T(-1.0), T(-1.0), T(-1.0), T(-2.0), T(-5.0) }; 18 | 19 | template 20 | static std::vector exp_round_ceil 21 | { T(6.0), T(3.0), T(2.0), T(2.0), T(1.0), T(-1.0), T(-1.0), T(-1.0), T(-2.0), T(-5.0) }; 22 | 23 | template 24 | static std::vector exp_round_floor 25 | { T(5.0), T(2.0), T(1.0), T(1.0), T(1.0), T(-1.0), T(-2.0), T(-2.0), T(-3.0), T(-6.0) }; 26 | 27 | template 28 | static std::vector exp_round_half_up 29 | { T(6.0), T(3.0), T(2.0), T(1.0), T(1.0), T(-1.0), T(-1.0), T(-2.0), T(-3.0), T(-6.0) }; 30 | 31 | template 32 | static std::vector exp_round_half_down 33 | { T(5.0), T(2.0), T(2.0), T(1.0), T(1.0), T(-1.0), T(-1.0), T(-2.0), T(-2.0), T(-5.0) }; 34 | 35 | template 36 | static std::vector exp_round_half_even 37 | { T(6.0), T(2.0), T(2.0), T(1.0), T(1.0), T(-1.0), T(-1.0), T(-2.0), T(-2.0), T(-6.0) }; 38 | 39 | template 40 | static std::vector exp_round_half_odd 41 | { T(5.0), T(3.0), T(2.0), T(1.0), T(1.0), T(-1.0), T(-1.0), T(-2.0), T(-3.0), T(-5.0) }; 42 | 43 | TEST_CASE("Test round up", "[rounding]") 44 | { 45 | for (size_t i = 0; i < inputs.size(); ++i) 46 | { 47 | auto r = round_up()(inputs[i]); 48 | REQUIRE(r == exp_round_up[i]); 49 | } 50 | 51 | for (size_t i = 0; i < inputs.size(); ++i) 52 | { 53 | auto r = round_up()(inputs[i]); 54 | REQUIRE(r == exp_round_up[i]); 55 | } 56 | 57 | for (size_t i = 0; i < inputs.size(); ++i) 58 | { 59 | auto r = round_up()(inputs[i]); 60 | REQUIRE(r == exp_round_up[i]); 61 | } 62 | } 63 | 64 | TEST_CASE("Test round down", "[rounding]") 65 | { 66 | for (size_t i = 0; i < inputs.size(); ++i) 67 | { 68 | auto r = round_down()(inputs[i]); 69 | REQUIRE(r == exp_round_down[i]); 70 | } 71 | 72 | for (size_t i = 0; i < inputs.size(); ++i) 73 | { 74 | auto r = round_down()(inputs[i]); 75 | REQUIRE(r == exp_round_down[i]); 76 | } 77 | 78 | for (size_t i = 0; i < inputs.size(); ++i) 79 | { 80 | auto r = round_down()(inputs[i]); 81 | REQUIRE(r == exp_round_down[i]); 82 | } 83 | } 84 | 85 | TEST_CASE("Test round ceil", "[rounding]") 86 | { 87 | for (size_t i = 0; i < inputs.size(); ++i) 88 | { 89 | auto r = round_ceiling()(inputs[i]); 90 | REQUIRE(r == exp_round_ceil[i]); 91 | } 92 | 93 | for (size_t i = 0; i < inputs.size(); ++i) 94 | { 95 | auto r = round_ceiling()(inputs[i]); 96 | REQUIRE(r == exp_round_ceil[i]); 97 | } 98 | 99 | for (size_t i = 0; i < inputs.size(); ++i) 100 | { 101 | auto r = round_ceiling()(inputs[i]); 102 | REQUIRE(r == exp_round_ceil[i]); 103 | } 104 | } 105 | 106 | TEST_CASE("Test round floor", "[rounding]") 107 | { 108 | for (size_t i = 0; i < inputs.size(); ++i) 109 | { 110 | auto r = round_floor()(inputs[i]); 111 | REQUIRE(r == exp_round_floor[i]); 112 | } 113 | 114 | for (size_t i = 0; i < inputs.size(); ++i) 115 | { 116 | auto r = round_floor()(inputs[i]); 117 | REQUIRE(r == exp_round_floor[i]); 118 | } 119 | 120 | for (size_t i = 0; i < inputs.size(); ++i) 121 | { 122 | auto r = round_floor()(inputs[i]); 123 | REQUIRE(r == exp_round_floor[i]); 124 | } 125 | } 126 | 127 | TEST_CASE("Test round half up", "[rounding]") 128 | { 129 | for (size_t i = 0; i < inputs.size(); ++i) 130 | { 131 | auto r = round_half_up()(inputs[i]); 132 | REQUIRE(r == exp_round_half_up[i]); 133 | } 134 | 135 | for (size_t i = 0; i < inputs.size(); ++i) 136 | { 137 | auto r = round_half_up()(inputs[i]); 138 | REQUIRE(r == exp_round_half_up[i]); 139 | } 140 | 141 | for (size_t i = 0; i < inputs.size(); ++i) 142 | { 143 | auto r = round_half_up()(inputs[i]); 144 | REQUIRE(r == exp_round_half_up[i]); 145 | } 146 | } 147 | 148 | TEST_CASE("Test round half down", "[rounding]") 149 | { 150 | for (size_t i = 0; i < inputs.size(); ++i) 151 | { 152 | auto r = round_half_down()(inputs[i]); 153 | REQUIRE(r == exp_round_half_down[i]); 154 | } 155 | 156 | for (size_t i = 0; i < inputs.size(); ++i) 157 | { 158 | auto r = round_half_down()(inputs[i]); 159 | REQUIRE(r == exp_round_half_down[i]); 160 | } 161 | 162 | for (size_t i = 0; i < inputs.size(); ++i) 163 | { 164 | auto r = round_half_down()(inputs[i]); 165 | REQUIRE(r == exp_round_half_down[i]); 166 | } 167 | } 168 | 169 | TEST_CASE("Test round half even", "[rounding]") 170 | { 171 | for (size_t i = 0; i < inputs.size(); ++i) 172 | { 173 | auto r = round_half_even()(inputs[i]); 174 | REQUIRE(r == exp_round_half_even[i]); 175 | } 176 | 177 | for (size_t i = 0; i < inputs.size(); ++i) 178 | { 179 | auto r = round_half_even()(inputs[i]); 180 | REQUIRE(r == exp_round_half_even[i]); 181 | } 182 | 183 | for (size_t i = 0; i < inputs.size(); ++i) 184 | { 185 | auto r = round_half_even()(inputs[i]); 186 | REQUIRE(r == exp_round_half_even[i]); 187 | } 188 | } 189 | 190 | TEST_CASE("Test round half odd", "[rounding]") 191 | { 192 | for (size_t i = 0; i < inputs.size(); ++i) 193 | { 194 | auto r = round_half_odd()(inputs[i]); 195 | REQUIRE(r == exp_round_half_odd[i]); 196 | } 197 | 198 | for (size_t i = 0; i < inputs.size(); ++i) 199 | { 200 | auto r = round_half_odd()(inputs[i]); 201 | REQUIRE(r == exp_round_half_odd[i]); 202 | } 203 | 204 | for (size_t i = 0; i < inputs.size(); ++i) 205 | { 206 | auto r = round_half_odd()(inputs[i]); 207 | REQUIRE(r == exp_round_half_odd[i]); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(PackageTest CXX) 3 | set(CMAKE_CXX_STANDARD 17) 4 | 5 | include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) 6 | conan_basic_setup() 7 | 8 | add_executable(example example.cpp) 9 | target_link_libraries(example ${CONAN_LIBS}) -------------------------------------------------------------------------------- /test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from conans import ConanFile, CMake, tools 4 | 5 | 6 | class MoneycppTestConan(ConanFile): 7 | settings = "os", "compiler", "build_type", "arch" 8 | generators = "cmake" 9 | 10 | def build(self): 11 | cmake = CMake(self) 12 | # Current dir is "test_package/build/" and CMakeLists.txt is 13 | # in "test_package" 14 | cmake.configure() 15 | cmake.build() 16 | 17 | def imports(self): 18 | self.copy("*.dll", dst="bin", src="bin") 19 | self.copy("*.dylib*", dst="bin", src="lib") 20 | self.copy('*.so*', dst='bin', src='lib') 21 | 22 | def test(self): 23 | if not tools.cross_building(self): 24 | os.chdir("bin") 25 | self.run(".%sexample" % os.sep) 26 | -------------------------------------------------------------------------------- /test_package/example.cpp: -------------------------------------------------------------------------------- 1 | #define HAS_COUNTRY_AND_CURRENCY_DB 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | std::string to_string(moneycpp::money const& m) 9 | { 10 | return std::to_string(m.amount); 11 | } 12 | 13 | std::string_view to_string(moneycpp::currency_unit const& c) 14 | { 15 | return c.code; 16 | } 17 | 18 | int main() 19 | { 20 | using namespace moneycpp; 21 | 22 | // create and operate with money values 23 | auto const m1 = make_money(20.0, currency::USD); 24 | auto const m2 = make_money(30.0, currency::USD); 25 | 26 | auto const m3 = m1 + m2; 27 | 28 | std::cout << to_string(m1) << '+' << to_string(m2) << '=' << to_string(m3) << ' ' << to_string(m3.currency) << std::endl; 29 | return 0; 30 | } 31 | --------------------------------------------------------------------------------