├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── include ├── single_header.cmake ├── single_header_builder.bat └── usf │ ├── develop │ ├── usf_arg_custom_type.hpp │ ├── usf_arg_format.hpp │ ├── usf_argument.hpp │ ├── usf_config.hpp │ ├── usf_float.hpp │ ├── usf_integer.hpp │ ├── usf_main.hpp │ ├── usf_string_span.hpp │ ├── usf_string_view.hpp │ └── usf_traits.hpp │ └── usf.hpp └── unit_tests ├── CMakeLists.txt ├── include ├── doctest.h └── unit_tests_config.hpp └── source ├── unit_tests.cpp ├── unit_tests_basic_types.cpp ├── unit_tests_benchmarks.cpp ├── unit_tests_custom_types.cpp ├── unit_tests_floating_point.cpp ├── unit_tests_format_spec.cpp └── unit_tests_positional_args.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Compiled Dynamic libraries 11 | *.so 12 | *.dylib 13 | *.dll 14 | 15 | # Compiled Static libraries 16 | *.lai 17 | *.la 18 | *.a 19 | *.lib 20 | 21 | # Executables 22 | *.exe 23 | *.out 24 | *.app 25 | *.axf 26 | *.elf 27 | *.hex 28 | *.bin 29 | 30 | # Archives 31 | *.zip 32 | *.7z 33 | 34 | # Ignore everything inside these directories 35 | /**/.vscode/** 36 | /**/build/** 37 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | 3 | language: cpp 4 | 5 | matrix: 6 | fast_finish: true 7 | include: 8 | 9 | - os: linux 10 | addons: 11 | apt: 12 | sources: 13 | - ubuntu-toolchain-r-test 14 | packages: 15 | - g++-6 16 | env: 17 | - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" 18 | 19 | - os: linux 20 | addons: 21 | apt: 22 | sources: 23 | - ubuntu-toolchain-r-test 24 | packages: 25 | - g++-7 26 | env: 27 | - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" 28 | 29 | - os: linux 30 | addons: 31 | apt: 32 | sources: 33 | - ubuntu-toolchain-r-test 34 | packages: 35 | - g++-8 36 | env: 37 | - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" 38 | 39 | before_install: 40 | - eval "${MATRIX_EVAL}" 41 | 42 | script: 43 | - cd unit_tests 44 | - mkdir build 45 | - cd build 46 | - cmake -DCMAKE_BUILD_TYPE=Release .. 47 | - make unit_tests 48 | - ctest -V 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Helder Parracho 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 | # μSF - Micro String Format 2 | [![Standard](https://img.shields.io/badge/c%2B%2B-11/14/17-blue.svg)](https://isocpp.org/std/the-standard) 3 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT) 4 | [![Download](https://img.shields.io/badge/download%20%20-latest-blue.svg)](https://raw.githubusercontent.com/hparracho/usflib/master/include/usf/usf.hpp) 5 | [![Version](https://img.shields.io/badge/version-0.2.0-brightgreen.svg)](https://github.com/hparracho/usflib/releases) 6 | [![Build Status](https://travis-ci.org/hparracho/usflib.svg?branch=master)](https://travis-ci.org/hparracho/usflib) 7 | 8 | μSF is a lean C++ string format library (mainly) for embedded platforms. 9 | Its main purpose is to be a small, safe and fast alternative to (s)(n)printf functions adding some additional features. 10 | The library is in its very early stages of development and its interface can (inevitably will) change at any time based on my own usage experience and any feedback / comments I get. 11 | 12 | ## Motivation 13 | Damn... yet another formatting library??? 14 | Well, basically I have no sympathy for the (s)(n)printf functions, even less for the cumbersome IOStream's interface. 15 | I really love the [**\{fmt\}**](http://fmtlib.net) library but it has an hefty price tag when working with targets with only a few kB of flash and RAM available. 16 | This happens mostly because {fmt} has a lot of features (rarely needed in an embedded target) and also because it falls back to printf in some cases (hopefully this is still true in the latest version). 17 | 18 | So, based on my love for the {fmt}'s format string syntax (itself similar to the one of [str.format](https://docs.python.org/3.8/library/string.html) in Python), I decided to roll my own version with a nice balance in mind between code size, execution speed and available features that made sense to my main target platforms, small embedded micro-controllers, mostly ARM Cortex-M0/3/4. 19 | 20 | I also did it for the challenge of making it and it turned out to be more demanding than I has expecting at first... 21 | 22 | ## Features 23 | - Safe alternative to (s)(n)printf and IOStream functions. 24 | - Doesn't allocate any dynamic memory. 25 | - Ease of use: single header file library [usf.hpp](https://raw.githubusercontent.com/hparracho/usflib/master/include/usf/usf.hpp) without any external dependencies. 26 | - Small code size: both in terms of source code (around 2500 loc) and produced binary size. 27 | - Reliable: it has an extensive set of [unit tests](https://github.com/hparracho/usflib/tree/master/unit_tests) (*work in progress*). 28 | - Support for positional arguments. 29 | - Support for user-defined custom types. 30 | - Portable and consistent across platforms and compilers. 31 | - Clean warning-free codebase even with [aggressive](https://github.com/hparracho/usflib/blob/master/unit_tests/CMakeLists.txt#L10) warning levels. 32 | - Support for C\+\+11/14/17 standards: 33 | - GCC 4.8.1 or greater 34 | - Clang 3.4.0 or greater 35 | - Should work with MSVC\+\+ 14.0 or greater (*untested yet*) 36 | 37 | #### Roadmap - upcoming features 38 | - Complete unit tests. 39 | - IAR and Arm Compiler 5/6 support. 40 | - Dynamic width and precision support. 41 | - Add a std::string replacement with inplace storage. 42 | - Add more functions to usf::StringSpan and usf::StringView classes. 43 | - Besides formatting, also printing capability with custom output handler, e.g.: Console, UARTs, LCDs, etc... 44 | - Wide character strings support (the foundations are done but it was never used or tested). 45 | - Better floating point conversion routines (the current ones suck!). Looking at the possibility of adapting [ryu](https://github.com/ulfjack/ryu)'s algorithm. 46 | 47 | ## Benchmarks 48 | (*wip*) 49 | 50 | ## Usage 51 | To get started with **usflib** all you need to do is download the [**latest version**](https://raw.githubusercontent.com/hparracho/usflib/master/include/usf/usf.hpp) which is just a single header file and add it to your source files (or add this repository as a git submodule). 52 | 53 | Assuming you can use the single header file directly: ```#include "usf.hpp"``` - place it either in the same folder of your source files or add its location to the include paths of your build system. 54 | 55 | #### A minimal example 56 | ```c++ 57 | #include "usf.hpp" 58 | /*...*/ 59 | char str[64]{}; 60 | usf::format_to(str, 64, "Hello, {}!", "world"); // str == "Hello, world!" 61 | usf::format_to(str, 64, "dec:{0} hex:{0:x}", 16); // str == "dec:16 hex:10" 62 | usf::format_to(str, 64, "{0}{1}{0}", "abra", "cad"); // str == "abracadabra" 63 | ``` 64 | 65 | ## API Reference 66 | All functions and types provided by the μSF library reside in namespace ```usf``` and macros have prefix ```USF_```. 67 | The library API provides a few simple format functions and a minimal string view / string span like classes that offer an alternative to the standard ones. 68 | 69 | The string view / string span types are template classes that accepts the type of character via a template parameter, having (for now) a *char* type specialization. 70 | ```c++ 71 | template 72 | class BasicStringView{ /*...*/ }; 73 | using StringView = BasicStringView; 74 | 75 | template 76 | class BasicStringSpan{ /*...*/ }; 77 | using StringSpan = BasicStringSpan; 78 | ``` 79 | Similarly, the format functions are template functions, allowing the use of different character types. Two sets are defined, a ```basic_format_to``` being the template version and another called ```format_to``` being the *char* type specialization. 80 | 81 | The following format functions use the same [format string syntax](#syntax) of {fmt}, which is itself similar to that of Pythons's [str.format](https://docs.python.org/3.8/library/string.html) (although not every option is implemented). 82 | 83 | The output format is specified in the *str_fmt* string/string view. It can contain literal text and replacement fields surrounded by braces ```{}```. The fields are replaced with formatted arguments in the resulting string. 84 | 85 | The objects to be formated are represented by the *args* argument list. 86 | 87 | 1. Formats the *args* arguments, writes the result to the iterator (pointer) *str* and returns an iterator (pointer) past the end of the output range. 88 | The function fails if the resulting output string size is greater than *str_count* size. 89 | ```c++ 90 | template 91 | CharT* usf::basic_format_to(CharT* str, const std::ptrdiff_t str_count, 92 | usf::BasicStringView str_fmt, Args&&... args); 93 | 94 | template 95 | char* usf::format_to(char* str, const std::ptrdiff_t str_count, 96 | usf::StringView str_fmt, Args&&... args); 97 | ``` 98 | 2. Formats the *args* arguments, writes the result to the string holded by the string span *str* (don't change the span range) and returns a new string span with the range adjusted to the output string size. 99 | The function fails if the resulting output string size is greater than *str*'s span range. 100 | ```c++ 101 | template 102 | usf::BasicStringSpan usf::basic_format_to(usf::BasicStringSpan str, 103 | usf::BasicStringView str_fmt, Args&&... args); 104 | 105 | template 106 | usf::StringSpan usf::format_to(usf::StringSpan str, usf::StringView str_fmt, Args&&... args); 107 | ``` 108 | ## Syntax 109 | (*wip*) 110 | 111 | ## License 112 | (*wip*) 113 | 114 | ## Acknowledgments 115 | (*wip*) 116 | -------------------------------------------------------------------------------- /include/single_header.cmake: -------------------------------------------------------------------------------- 1 | set(USF_VERSION:STRING "undefined") 2 | 3 | string(TIMESTAMP date "%d %B %Y") 4 | string(TIMESTAMP year "%Y") 5 | 6 | set(usf_develop_folder "${CMAKE_CURRENT_LIST_DIR}/usf/develop") 7 | set(usf_release_folder "${CMAKE_CURRENT_LIST_DIR}/usf") 8 | 9 | file(READ ${usf_develop_folder}/usf_config.hpp usf_config_hpp) 10 | file(READ ${usf_develop_folder}/usf_traits.hpp usf_traits_hpp) 11 | file(READ ${usf_develop_folder}/usf_string_span.hpp usf_string_span_hpp) 12 | file(READ ${usf_develop_folder}/usf_string_view.hpp usf_string_view_hpp) 13 | file(READ ${usf_develop_folder}/usf_integer.hpp usf_integer_hpp) 14 | file(READ ${usf_develop_folder}/usf_float.hpp usf_float_hpp) 15 | file(READ ${usf_develop_folder}/usf_arg_format.hpp usf_arg_format_hpp) 16 | file(READ ${usf_develop_folder}/usf_arg_custom_type.hpp usf_arg_custom_type_hpp) 17 | file(READ ${usf_develop_folder}/usf_argument.hpp usf_argument_hpp) 18 | file(READ ${usf_develop_folder}/usf_main.hpp usf_main_hpp) 19 | 20 | file(WRITE ${usf_release_folder}/usf.hpp "// ----------------------------------------------------------------------------\n") 21 | file(APPEND ${usf_release_folder}/usf.hpp "// DO NOT MANUALLY MODIFY THIS FILE, IT IS AUTO GENERATED USING A CMAKE SCRIPT!\n") 22 | file(APPEND ${usf_release_folder}/usf.hpp "// ----------------------------------------------------------------------------\n") 23 | file(APPEND ${usf_release_folder}/usf.hpp "// @file usf.hpp\n") 24 | file(APPEND ${usf_release_folder}/usf.hpp "// @brief usflib single header auto generated file.\n") 25 | file(APPEND ${usf_release_folder}/usf.hpp "// @date ${date}\n") 26 | file(APPEND ${usf_release_folder}/usf.hpp "// ----------------------------------------------------------------------------\n") 27 | file(APPEND ${usf_release_folder}/usf.hpp "//\n") 28 | file(APPEND ${usf_release_folder}/usf.hpp "// μSF - Micro String Format ${USF_VERSION} - https://github.com/hparracho/usflib\n") 29 | file(APPEND ${usf_release_folder}/usf.hpp "// Copyright (c) ${year} Helder Parracho (hparracho@gmail.com)\n") 30 | file(APPEND ${usf_release_folder}/usf.hpp "//\n") 31 | file(APPEND ${usf_release_folder}/usf.hpp "// See README.md file for additional credits and acknowledgments.\n") 32 | file(APPEND ${usf_release_folder}/usf.hpp "//\n") 33 | file(APPEND ${usf_release_folder}/usf.hpp "// Permission is hereby granted, free of charge, to any person obtaining a\n") 34 | file(APPEND ${usf_release_folder}/usf.hpp "// copy of this software and associated documentation files (the \"Software\"),\n") 35 | file(APPEND ${usf_release_folder}/usf.hpp "// to deal in the Software without restriction, including without limitation\n") 36 | file(APPEND ${usf_release_folder}/usf.hpp "// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n") 37 | file(APPEND ${usf_release_folder}/usf.hpp "// and/or sell copies of the Software, and to permit persons to whom the\n") 38 | file(APPEND ${usf_release_folder}/usf.hpp "// Software is furnished to do so, subject to the following conditions:\n") 39 | file(APPEND ${usf_release_folder}/usf.hpp "//\n") 40 | file(APPEND ${usf_release_folder}/usf.hpp "// The above copyright notice and this permission notice shall be included\n") 41 | file(APPEND ${usf_release_folder}/usf.hpp "// in all copies or substantial portions of the Software.\n") 42 | file(APPEND ${usf_release_folder}/usf.hpp "//\n") 43 | file(APPEND ${usf_release_folder}/usf.hpp "// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n") 44 | file(APPEND ${usf_release_folder}/usf.hpp "// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n") 45 | file(APPEND ${usf_release_folder}/usf.hpp "// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n") 46 | file(APPEND ${usf_release_folder}/usf.hpp "// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n") 47 | file(APPEND ${usf_release_folder}/usf.hpp "// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n") 48 | file(APPEND ${usf_release_folder}/usf.hpp "// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n") 49 | file(APPEND ${usf_release_folder}/usf.hpp "// DEALINGS IN THE SOFTWARE.\n") 50 | file(APPEND ${usf_release_folder}/usf.hpp "//\n") 51 | file(APPEND ${usf_release_folder}/usf.hpp "// ----------------------------------------------------------------------------\n\n") 52 | file(APPEND ${usf_release_folder}/usf.hpp "#ifndef USF_HPP\n") 53 | file(APPEND ${usf_release_folder}/usf.hpp "#define USF_HPP\n\n") 54 | 55 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_config_hpp}\n\n") 56 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_traits_hpp}\n\n") 57 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_string_span_hpp}\n\n") 58 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_string_view_hpp}\n\n") 59 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_integer_hpp}\n\n") 60 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_float_hpp}\n\n") 61 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_arg_format_hpp}\n\n") 62 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_arg_custom_type_hpp}\n\n") 63 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_argument_hpp}\n\n") 64 | file(APPEND ${usf_release_folder}/usf.hpp "${usf_main_hpp}\n") 65 | 66 | file(APPEND ${usf_release_folder}/usf.hpp "#endif // USF_HPP\n") 67 | -------------------------------------------------------------------------------- /include/single_header_builder.bat: -------------------------------------------------------------------------------- 1 | @cmake -DUSF_VERSION=%1 -P single_header.cmake 2 | -------------------------------------------------------------------------------- /include/usf/develop/usf_arg_custom_type.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_arg_custom_type.hpp 3 | // @brief User-defined custom type class (using the delegate idiom). 4 | // @date 07 January 2019 5 | // ---------------------------------------------------------------------------- 6 | 7 | #ifndef USF_ARG_CUSTOM_TYPE_HPP 8 | #define USF_ARG_CUSTOM_TYPE_HPP 9 | 10 | namespace usf 11 | { 12 | namespace internal 13 | { 14 | 15 | template 16 | class ArgCustomType 17 | { 18 | public: 19 | 20 | // -------------------------------------------------------------------- 21 | // PUBLIC MEMBER FUNCTIONS 22 | // -------------------------------------------------------------------- 23 | 24 | USF_CPP14_CONSTEXPR ArgCustomType() = delete; 25 | 26 | template(*func)(BasicStringSpan, const T&)> 27 | static USF_CPP14_CONSTEXPR ArgCustomType create(const T* obj) 28 | { 29 | return ArgCustomType(invoke_func, obj); 30 | } 31 | 32 | USF_CPP14_CONSTEXPR BasicStringSpan operator()(BasicStringSpan dst) const 33 | { 34 | return m_function(dst, m_obj); 35 | } 36 | 37 | private: 38 | 39 | // -------------------------------------------------------------------- 40 | // PRIVATE TYPE ALIASES 41 | // -------------------------------------------------------------------- 42 | 43 | using FunctionType = BasicStringSpan(*)(BasicStringSpan, const void*); 44 | 45 | // -------------------------------------------------------------------- 46 | // PRIVATE MEMBER FUNCTIONS 47 | // -------------------------------------------------------------------- 48 | 49 | USF_CPP14_CONSTEXPR ArgCustomType(const FunctionType func, const void* obj) 50 | : m_function{func}, m_obj{obj} {} 51 | 52 | template(*func)(BasicStringSpan, const T&)> 53 | static USF_CPP14_CONSTEXPR BasicStringSpan invoke_func(BasicStringSpan dst, const void* obj) 54 | { 55 | return func(dst, *static_cast(obj)); 56 | } 57 | 58 | // -------------------------------------------------------------------- 59 | // PRIVATE VARIABLES 60 | // -------------------------------------------------------------------- 61 | 62 | const FunctionType m_function{nullptr}; 63 | const void* m_obj {nullptr}; 64 | }; 65 | 66 | } // namespace internal 67 | } // namespace usf 68 | 69 | #endif // USF_ARG_CUSTOM_TYPE_HPP 70 | -------------------------------------------------------------------------------- /include/usf/develop/usf_arg_format.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_arg_format.hpp 3 | // @brief Argument format parser class. 4 | // @date 07 January 2019 5 | // ---------------------------------------------------------------------------- 6 | 7 | #ifndef USF_ARG_FORMAT_HPP 8 | #define USF_ARG_FORMAT_HPP 9 | 10 | namespace usf 11 | { 12 | namespace internal 13 | { 14 | 15 | template 16 | class ArgFormat 17 | { 18 | public: 19 | 20 | // -------------------------------------------------------------------- 21 | // PUBLIC TYPE ALIASES 22 | // -------------------------------------------------------------------- 23 | 24 | using iterator = CharT*; 25 | using const_iterator = const CharT*; 26 | 27 | // -------------------------------------------------------------------- 28 | // PUBLIC DEFINITIONS 29 | // -------------------------------------------------------------------- 30 | 31 | enum class Align : uint8_t 32 | { 33 | kNone = (0U << 1U), 34 | kLeft = (1U << 1U), 35 | kRight = (2U << 1U), 36 | kCenter = (3U << 1U), 37 | kNumeric = (4U << 1U) 38 | }; 39 | 40 | enum class Sign : uint8_t 41 | { 42 | kNone = (0U << 4U), 43 | kMinus = (1U << 4U), 44 | kPlus = (2U << 4U), 45 | kSpace = (3U << 4U) 46 | }; 47 | 48 | enum class Type : uint8_t 49 | { 50 | kNone, 51 | kChar, 52 | kIntegerDec, 53 | kIntegerHex, 54 | kIntegerOct, 55 | kIntegerBin, 56 | kPointer, 57 | kFloatFixed, 58 | kFloatScientific, 59 | kFloatGeneral, 60 | kString, 61 | kInvalid 62 | }; 63 | 64 | // -------------------------------------------------------------------- 65 | // PUBLIC MEMBER FUNCTIONS 66 | // -------------------------------------------------------------------- 67 | 68 | USF_CPP14_CONSTEXPR ArgFormat(usf::BasicStringView& fmt, const int arg_count) 69 | { 70 | const_iterator it = fmt.cbegin(); 71 | 72 | USF_ENFORCE(*it == '{', std::runtime_error); 73 | 74 | // Iterator is placed at "{" character, so advance it. 75 | ++it; 76 | 77 | // Parse argument index 78 | if(*it >= '0' && *it <= '9') 79 | { 80 | // Index limited to `arg_count` value. 81 | m_index = static_cast(parse_positive_small_int(it, arg_count)); 82 | } 83 | 84 | if(*it == ':' && *(it + 1) != '}') 85 | { 86 | // A format spec is expected next... 87 | 88 | //Remove the empty format flag 89 | m_flags = Flags::kNone; 90 | 91 | // Advance ':' character 92 | ++it; 93 | 94 | // Try to parse alignment flag at second character of format spec. 95 | m_flags = parse_align_flag(*(it + 1)); 96 | 97 | if(m_flags != Flags::kNone) 98 | { 99 | // Alignment flag present at second character of format spec. 100 | // Should also have a fill character at the first character. 101 | 102 | // The fill character can be any character except '{' or '}'. 103 | USF_ENFORCE(*it != '{' && *it != '}', std::runtime_error); 104 | 105 | m_fill_char = *it; 106 | it += 2; 107 | } 108 | else 109 | { 110 | // Alignment flag not present at the second character of format spec. 111 | // Try to parse the alignment flag at the first character instead... 112 | m_flags = parse_align_flag(*it); 113 | 114 | if(m_flags != Flags::kNone) 115 | { 116 | ++it; 117 | } 118 | } 119 | 120 | // Parse sign flag 121 | switch(*it) 122 | { 123 | case '-': m_flags |= Flags::kSignMinus; ++it; break; 124 | case '+': m_flags |= Flags::kSignPlus; ++it; break; 125 | case ' ': m_flags |= Flags::kSignSpace; ++it; break; 126 | default : break; 127 | } 128 | 129 | // Parse hash flag 130 | if(*it == '#') 131 | { 132 | m_flags |= Flags::kHash; 133 | ++it; 134 | } 135 | 136 | bool fill_zero = false; 137 | 138 | // Parse fill zero flag 139 | if(*it == '0') 140 | { 141 | fill_zero = true; 142 | ++it; 143 | } 144 | 145 | // Parse width 146 | if(*it >= '0' && *it <= '9') 147 | { 148 | // Limit width to 255 characters 149 | m_width = parse_positive_small_int(it, 255); 150 | } 151 | 152 | // Parse precision 153 | if(*it == '.') 154 | { 155 | ++it; 156 | 157 | // Check for a missing/invalid precision specifier. 158 | USF_ENFORCE(*it >= '0' && *it <= '9', std::runtime_error); 159 | 160 | m_precision = static_cast(parse_positive_small_int(it, 127)); 161 | } 162 | 163 | // Parse type 164 | if(*it != '}') 165 | { 166 | switch(*it++) 167 | { 168 | case 'c': m_type = Type::kChar; break; 169 | 170 | case 'd': m_type = Type::kIntegerDec; break; 171 | 172 | case 'X': m_flags |= Flags::kUppercase; USF_FALLTHROUGH; 173 | case 'x': m_type = Type::kIntegerHex; break; 174 | 175 | case 'o': m_type = Type::kIntegerOct; break; 176 | 177 | case 'B': m_flags |= Flags::kUppercase; USF_FALLTHROUGH; 178 | case 'b': m_type = Type::kIntegerBin; break; 179 | 180 | case 'P': m_flags |= Flags::kUppercase; USF_FALLTHROUGH; 181 | case 'p': m_type = Type::kPointer; break; 182 | 183 | case 'F': m_flags |= Flags::kUppercase; USF_FALLTHROUGH; 184 | case 'f': m_type = Type::kFloatFixed; break; 185 | 186 | case 'E': m_flags |= Flags::kUppercase; USF_FALLTHROUGH; 187 | case 'e': m_type = Type::kFloatScientific; break; 188 | 189 | case 'G': m_flags |= Flags::kUppercase; USF_FALLTHROUGH; 190 | case 'g': m_type = Type::kFloatGeneral; break; 191 | 192 | case 's': m_type = Type::kString; break; 193 | 194 | default : m_type = Type::kInvalid; break; 195 | } 196 | 197 | USF_ENFORCE(m_type != Type::kInvalid, std::runtime_error); 198 | } 199 | 200 | // Validate the read format spec! 201 | 202 | if(fill_zero) 203 | { 204 | // Fill zero flag has precedence over any other alignment and fill character. 205 | m_flags = static_cast((m_flags & (~Flags::kAlignBitmask)) | Flags::kAlignNumeric); 206 | m_fill_char = '0'; 207 | } 208 | 209 | if(align() == Align::kNumeric) 210 | { 211 | // Numeric alignment are only valid for numeric and pointer types. 212 | USF_ENFORCE(type_is_numeric() || type_is_pointer(), std::runtime_error); 213 | } 214 | 215 | if(sign() != Sign::kNone) 216 | { 217 | // Sign is only valid for numeric types. 218 | USF_ENFORCE(type_is_numeric(), std::runtime_error); 219 | } 220 | 221 | if(hash()) 222 | { 223 | // Alternative format is valid for hexadecimal (including 224 | // pointers), octal, binary and all floating point types. 225 | USF_ENFORCE(type_allow_hash(), std::runtime_error); 226 | } 227 | 228 | if(m_precision != -1) 229 | { 230 | // Precision is only valid for floating point and string types. 231 | USF_ENFORCE(type_is_float() || type_is_string(), std::runtime_error); 232 | } 233 | } 234 | 235 | // Test for unterminated argument format spec. 236 | USF_ENFORCE(it < fmt.cend() && *it++ == '}', std::runtime_error); 237 | 238 | fmt.remove_prefix(it - fmt.cbegin()); 239 | } 240 | 241 | // Writes the alignment (sign, prefix and fill before) for any 242 | // argument type. Returns the fill counter to write after argument. 243 | USF_CPP14_CONSTEXPR int write_alignment(iterator& it, const_iterator end, 244 | int digits, const bool negative) const 245 | { 246 | digits += sign_width(negative) + prefix_width(); 247 | 248 | int fill_after = 0; 249 | 250 | if(width() <= digits) 251 | { 252 | USF_ENFORCE(it + digits < end, std::runtime_error); 253 | write_sign(it, negative); 254 | write_prefix(it); 255 | } 256 | else 257 | { 258 | USF_ENFORCE(it + width() < end, std::runtime_error); 259 | 260 | int fill_count = width() - digits; 261 | 262 | const Align al = align(); 263 | 264 | if(al == Align::kLeft) 265 | { 266 | fill_after = fill_count; 267 | } 268 | else if(al == Align::kCenter) 269 | { 270 | fill_after = fill_count - (fill_count / 2); 271 | fill_count /= 2; 272 | } 273 | 274 | if(al != Align::kLeft && al != Align::kNumeric) 275 | { 276 | // None (default right), Right or Center alignment 277 | CharTraits::assign(it, fill_char(), fill_count); 278 | } 279 | 280 | write_sign(it, negative); 281 | write_prefix(it); 282 | 283 | if(al == Align::kNumeric) 284 | { 285 | CharTraits::assign(it, fill_char(), fill_count); 286 | } 287 | } 288 | 289 | return fill_after; 290 | } 291 | 292 | inline constexpr CharT fill_char() const noexcept { return m_fill_char; } 293 | inline constexpr Type type () const noexcept { return m_type; } 294 | inline constexpr int width () const noexcept { return static_cast(m_width ); } 295 | inline constexpr int precision() const noexcept { return static_cast(m_precision); } 296 | inline constexpr int index () const noexcept { return static_cast(m_index ); } 297 | 298 | inline constexpr Align align() const noexcept { return Align(m_flags & Flags::kAlignBitmask); } 299 | inline constexpr Sign sign () const noexcept { return Sign (m_flags & Flags::kSignBitmask ); } 300 | 301 | inline constexpr bool is_empty () const noexcept { return (m_flags & Flags::kEmpty ) != 0; } 302 | inline constexpr bool hash () const noexcept { return (m_flags & Flags::kHash ) != 0; } 303 | inline constexpr bool uppercase() const noexcept { return (m_flags & Flags::kUppercase) != 0; } 304 | 305 | inline constexpr bool type_is_none () const noexcept { return m_type == Type::kNone; } 306 | inline constexpr bool type_is_char () const noexcept { return m_type == Type::kChar; } 307 | inline constexpr bool type_is_string () const noexcept { return m_type == Type::kString; } 308 | inline constexpr bool type_is_pointer() const noexcept { return m_type == Type::kPointer; } 309 | 310 | inline constexpr bool type_is_integer() const noexcept { return m_type >= Type::kIntegerDec && m_type <= Type::kIntegerBin; } 311 | inline constexpr bool type_is_float () const noexcept { return m_type >= Type::kFloatFixed && m_type <= Type::kFloatGeneral; } 312 | inline constexpr bool type_is_numeric() const noexcept { return m_type >= Type::kIntegerDec && m_type <= Type::kFloatGeneral; } 313 | 314 | inline constexpr bool type_is_integer_dec() const noexcept { return m_type == Type::kIntegerDec; } 315 | inline constexpr bool type_is_integer_hex() const noexcept { return m_type == Type::kIntegerHex; } 316 | inline constexpr bool type_is_integer_oct() const noexcept { return m_type == Type::kIntegerOct; } 317 | inline constexpr bool type_is_integer_bin() const noexcept { return m_type == Type::kIntegerBin; } 318 | 319 | inline constexpr bool type_is_float_fixed () const noexcept { return m_type == Type::kFloatFixed; } 320 | inline constexpr bool type_is_float_scientific() const noexcept { return m_type == Type::kFloatScientific; } 321 | inline constexpr bool type_is_float_general () const noexcept { return m_type == Type::kFloatGeneral; } 322 | 323 | inline constexpr bool type_allow_hash() const noexcept 324 | { 325 | // Alternative format is valid for hexadecimal (including 326 | // pointers), octal, binary and all floating point types. 327 | return m_type >= Type::kIntegerHex && m_type <= Type::kFloatGeneral; 328 | } 329 | 330 | inline USF_CPP14_CONSTEXPR void default_align_left() noexcept 331 | { 332 | if((m_flags & Flags::kAlignBitmask) == Flags::kAlignNone) 333 | { 334 | m_flags |= Flags::kAlignLeft; 335 | } 336 | } 337 | 338 | private: 339 | 340 | // -------------------------------------------------------------------- 341 | // PRIVATE DEFINITIONS 342 | // -------------------------------------------------------------------- 343 | 344 | enum Flags : uint8_t 345 | { 346 | kNone = (0U << 0U), 347 | 348 | kEmpty = (1U << 0U), 349 | 350 | kAlignNone = (0U << 1U), 351 | kAlignLeft = (1U << 1U), 352 | kAlignRight = (2U << 1U), 353 | kAlignCenter = (3U << 1U), 354 | kAlignNumeric = (4U << 1U), 355 | kAlignBitmask = (7U << 1U), 356 | 357 | kSignNone = (0U << 4U), 358 | kSignMinus = (1U << 4U), 359 | kSignPlus = (2U << 4U), 360 | kSignSpace = (3U << 4U), 361 | kSignBitmask = (3U << 4U), 362 | 363 | kHash = (1U << 6U), 364 | 365 | kUppercase = (1U << 7U) 366 | }; 367 | 368 | // -------------------------------------------------------------------- 369 | // PRIVATE MEMBER FUNCTIONS 370 | // -------------------------------------------------------------------- 371 | 372 | inline constexpr int sign_width(const bool negative) const noexcept 373 | { 374 | return (!negative && sign() <= Sign::kMinus) ? 0 : 1; 375 | } 376 | 377 | inline constexpr int prefix_width() const noexcept 378 | { 379 | // Alternative format is valid for hexadecimal (including 380 | // pointers), octal, binary and all floating point types. 381 | return (!hash() || type_is_float()) ? 0 : type_is_integer_oct() ? 1 : 2; 382 | } 383 | 384 | USF_CPP14_CONSTEXPR void write_sign(iterator& it, const bool negative) const noexcept 385 | { 386 | if(negative) 387 | { 388 | *it++ = '-'; 389 | } 390 | else 391 | { 392 | const Sign s = sign(); 393 | 394 | if(s != Sign::kNone) 395 | { 396 | if(s == Sign::kPlus ) 397 | { 398 | *it++ = '+'; 399 | } 400 | else if(s == Sign::kSpace) 401 | { 402 | *it++ = ' '; 403 | } 404 | } 405 | } 406 | } 407 | 408 | USF_CPP14_CONSTEXPR void write_prefix(iterator& it) const noexcept 409 | { 410 | // Alternative format is valid for hexadecimal (including 411 | // pointers), octal, binary and all floating point types. 412 | if(hash() && !type_is_float()) 413 | { 414 | *it++ = '0'; 415 | 416 | if(type_is_integer_bin()) 417 | { 418 | *it++ = uppercase() ? 'B' : 'b'; 419 | } 420 | else if(type_is_integer_hex() || type_is_pointer()) 421 | { 422 | *it++ = uppercase() ? 'X' : 'x'; 423 | } 424 | } 425 | } 426 | 427 | // -------------------------------------------------------------------- 428 | // PRIVATE STATIC FUNCTIONS 429 | // -------------------------------------------------------------------- 430 | 431 | // Parses the input as a positive integer that fits into a `uint8_t` type. This 432 | // function assumes that the first character is a digit and terminates parsing 433 | // at the presence of the first non-digit character or when value overflows. 434 | static USF_CPP14_CONSTEXPR 435 | uint8_t parse_positive_small_int(const_iterator& it, const int max_value) 436 | { 437 | assert(max_value < 256); 438 | 439 | int value = 0; 440 | 441 | do 442 | { 443 | value = (value * 10) + static_cast(*it++ - '0'); 444 | 445 | // Check for overflow 446 | USF_ENFORCE(value <= max_value, std::runtime_error); 447 | }while(*it >= '0' && *it <= '9'); 448 | 449 | return static_cast(value); 450 | } 451 | 452 | static USF_CPP14_CONSTEXPR uint8_t parse_align_flag(const CharT ch) noexcept 453 | { 454 | switch(ch) 455 | { 456 | case '<': return Flags::kAlignLeft; break; 457 | case '>': return Flags::kAlignRight; break; 458 | case '^': return Flags::kAlignCenter; break; 459 | case '=': return Flags::kAlignNumeric; break; 460 | default : return Flags::kNone; break; 461 | } 462 | } 463 | 464 | // -------------------------------------------------------------------- 465 | // PRIVATE MEMBER VARIABLES 466 | // -------------------------------------------------------------------- 467 | 468 | CharT m_fill_char = ' '; 469 | Type m_type = Type::kNone; 470 | uint8_t m_flags = Flags::kEmpty; 471 | uint8_t m_width = 0; 472 | int8_t m_precision = -1; 473 | int8_t m_index = -1; 474 | }; 475 | 476 | } // namespace internal 477 | } // namespace usf 478 | 479 | #endif // USF_ARG_FORMAT_HPP 480 | -------------------------------------------------------------------------------- /include/usf/develop/usf_argument.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_argument.hpp 3 | // @brief Argument format processor class. 4 | // @date 14 January 2019 5 | // ---------------------------------------------------------------------------- 6 | 7 | #ifndef USF_ARGUMENT_HPP 8 | #define USF_ARGUMENT_HPP 9 | 10 | namespace usf 11 | { 12 | namespace internal 13 | { 14 | 15 | template 16 | class Argument 17 | { 18 | public: 19 | 20 | // -------------------------------------------------------------------- 21 | // PUBLIC TYPE ALIASES 22 | // -------------------------------------------------------------------- 23 | 24 | using iterator = CharT*; 25 | using const_iterator = const CharT*; 26 | 27 | using Format = ArgFormat; 28 | 29 | // -------------------------------------------------------------------- 30 | // PUBLIC MEMBER FUNCTIONS 31 | // -------------------------------------------------------------------- 32 | 33 | constexpr Argument() = delete; 34 | 35 | constexpr Argument(const bool value) noexcept 36 | : m_bool(value), m_type_id(TypeId::kBool) {} 37 | 38 | constexpr Argument(const CharT value) noexcept 39 | : m_char(value), m_type_id(TypeId::kChar) {} 40 | 41 | constexpr Argument(const int32_t value) noexcept 42 | : m_int32(value), m_type_id(TypeId::kInt32) {} 43 | 44 | constexpr Argument(const uint32_t value) noexcept 45 | : m_uint32(value), m_type_id(TypeId::kUint32) {} 46 | 47 | constexpr Argument(const int64_t value) noexcept 48 | : m_int64(value), m_type_id(TypeId::kInt64) {} 49 | 50 | constexpr Argument(const uint64_t value) noexcept 51 | : m_uint64(value), m_type_id(TypeId::kUint64) {} 52 | 53 | constexpr Argument(const void* value) noexcept 54 | : m_pointer(reinterpret_cast(value)), m_type_id(TypeId::kPointer) {} 55 | 56 | #if !defined(USF_DISABLE_FLOAT_SUPPORT) 57 | constexpr Argument(const double value) noexcept 58 | : m_float(value), m_type_id(TypeId::kFloat) {} 59 | #endif 60 | constexpr Argument(const usf::BasicStringView value) noexcept 61 | : m_string(value), m_type_id(TypeId::kString) {} 62 | 63 | constexpr Argument(const ArgCustomType value) noexcept 64 | : m_custom(value), m_type_id(TypeId::kCustom) {} 65 | 66 | USF_CPP14_CONSTEXPR void format(usf::BasicStringSpan& dst, Format& format) const 67 | { 68 | iterator it = dst.begin(); 69 | 70 | switch(m_type_id) 71 | { 72 | case TypeId::kBool: format_bool (it, dst.end(), format, m_bool ); break; 73 | case TypeId::kChar: format_char (it, dst.end(), format, m_char ); break; 74 | case TypeId::kInt32: format_integer(it, dst.end(), format, m_int32 ); break; 75 | case TypeId::kUint32: format_integer(it, dst.end(), format, m_uint32 ); break; 76 | case TypeId::kInt64: format_integer(it, dst.end(), format, m_int64 ); break; 77 | case TypeId::kUint64: format_integer(it, dst.end(), format, m_uint64 ); break; 78 | case TypeId::kPointer: format_pointer(it, dst.end(), format, m_pointer); break; 79 | #if !defined(USF_DISABLE_FLOAT_SUPPORT) 80 | case TypeId::kFloat: format_float (it, dst.end(), format, m_float ); break; 81 | #endif 82 | case TypeId::kString: format_string (it, dst.end(), format, m_string ); break; 83 | case TypeId::kCustom: USF_ENFORCE(format.is_empty(), std::runtime_error); 84 | it = m_custom(dst).end(); break; 85 | } 86 | 87 | dst.remove_prefix(it - dst.begin()); 88 | } 89 | 90 | private: 91 | 92 | // -------------------------------------------------------------------- 93 | // PRIVATE STATIC FUNCTIONS 94 | // -------------------------------------------------------------------- 95 | 96 | static USF_CPP14_CONSTEXPR void format_bool(iterator& it, const_iterator end, 97 | const Format& format, const bool value) 98 | { 99 | if(format.type_is_none()) 100 | { 101 | format_string(it, end, format, value ? "true" : "false", value ? 4 : 5); 102 | } 103 | else if(format.type_is_integer()) 104 | { 105 | format_integer(it, end, format, static_cast(value)); 106 | } 107 | else 108 | { 109 | // Argument type / format mismatch 110 | USF_CONTRACT_VIOLATION(std::runtime_error); 111 | } 112 | } 113 | 114 | static USF_CPP14_CONSTEXPR void format_char(iterator& it, const_iterator end, 115 | Format& format, const CharT value) 116 | { 117 | if(format.type_is_none() || format.type_is_char()) 118 | { 119 | // Characters and strings align to left by default. 120 | format.default_align_left(); 121 | 122 | const int fill_after = format.write_alignment(it, end, 1, false); 123 | *it++ = value; 124 | CharTraits::assign(it, format.fill_char(), fill_after); 125 | } 126 | else if(format.type_is_integer()) 127 | { 128 | format_integer(it, end, format, static_cast(value)); 129 | } 130 | else 131 | { 132 | // Argument type / format mismatch 133 | USF_CONTRACT_VIOLATION(std::runtime_error); 134 | } 135 | } 136 | 137 | template ::value, bool>::type = true> 138 | static USF_CPP14_CONSTEXPR void format_integer(iterator& it, const_iterator end, 139 | const Format& format, const T value) 140 | { 141 | using unsigned_type = typename std::make_unsigned::type; 142 | 143 | const bool negative = (value < 0); 144 | const auto uvalue = static_cast(negative ? -value : value); 145 | 146 | format_integer(it, end, format, uvalue, negative); 147 | } 148 | 149 | template ::value, bool>::type = true> 150 | static USF_CPP14_CONSTEXPR void format_integer(iterator& it, const_iterator end, const Format& format, 151 | const T value, const bool negative = false) 152 | { 153 | int fill_after = 0; 154 | 155 | if(format.type_is_none() || format.type_is_integer_dec()) 156 | { 157 | const auto digits = Integer::count_digits_dec(value); 158 | fill_after = format.write_alignment(it, end, digits, negative); 159 | it += digits; 160 | Integer::convert_dec(it, value); 161 | } 162 | else if(format.type_is_integer_hex()) 163 | { 164 | const auto digits = Integer::count_digits_hex(value); 165 | fill_after = format.write_alignment(it, end, digits, negative); 166 | it += digits; 167 | Integer::convert_hex(it, value, format.uppercase()); 168 | } 169 | else if(format.type_is_integer_oct()) 170 | { 171 | const auto digits = Integer::count_digits_oct(value); 172 | fill_after = format.write_alignment(it, end, digits, negative); 173 | it += digits; 174 | Integer::convert_oct(it, value); 175 | } 176 | else if(format.type_is_integer_bin()) 177 | { 178 | const auto digits = Integer::count_digits_bin(value); 179 | fill_after = format.write_alignment(it, end, digits, negative); 180 | it += digits; 181 | Integer::convert_bin(it, value); 182 | } 183 | else 184 | { 185 | // Argument type / format mismatch 186 | USF_CONTRACT_VIOLATION(std::runtime_error); 187 | } 188 | 189 | CharTraits::assign(it, format.fill_char(), fill_after); 190 | } 191 | 192 | static USF_CPP14_CONSTEXPR void format_pointer(iterator& it, const_iterator end, 193 | const Format& format, const std::uintptr_t value) 194 | { 195 | if(format.type_is_none() || format.type_is_pointer()) 196 | { 197 | #if defined(USF_TARGET_64_BITS) 198 | const auto ivalue = static_cast(value); 199 | #else 200 | const auto ivalue = static_cast(value); 201 | #endif 202 | const auto digits = Integer::count_digits_hex(ivalue); 203 | const auto fill_after = format.write_alignment(it, end, digits, false); 204 | it += digits; 205 | Integer::convert_hex(it, ivalue, format.uppercase()); 206 | CharTraits::assign(it, format.fill_char(), fill_after); 207 | } 208 | else 209 | { 210 | // Argument type / format mismatch 211 | USF_CONTRACT_VIOLATION(std::runtime_error); 212 | } 213 | } 214 | 215 | #if !defined(USF_DISABLE_FLOAT_SUPPORT) 216 | static USF_CPP14_CONSTEXPR 217 | void format_float(iterator& it, const_iterator end, const Format& format, double value) 218 | { 219 | // Test for argument type / format match 220 | USF_ENFORCE(format.type_is_none() || format.type_is_float(), std::runtime_error); 221 | 222 | if(std::isnan(value)) 223 | { 224 | format_string(it, end, format, format.uppercase() ? "NAN" : "nan", 3); 225 | } 226 | else 227 | { 228 | const bool negative = std::signbit(value); 229 | 230 | if(std::isinf(value)) 231 | { 232 | format_string(it, end, format, format.uppercase() ? "INF" : "inf", 3, negative); 233 | } 234 | else 235 | { 236 | if(negative) { value = -value; } 237 | 238 | struct fp_t 239 | { 240 | union 241 | { 242 | double d; 243 | uint64_t i; 244 | }; 245 | }; 246 | 247 | const fp_t fp_value{{value}}; 248 | 249 | if(fp_value.i == 0) 250 | { 251 | format_float_zero(it, end, format, negative); 252 | } 253 | else if(value >= 1E-19 && value <= 1.8446744E19) 254 | { 255 | int precision = format.precision(); 256 | 257 | if(precision < 0) { precision = 6; } 258 | 259 | bool format_fixed = format.type_is_float_fixed(); 260 | bool significant_figures = false; 261 | 262 | if(format.type_is_none() || format.type_is_float_general()) 263 | { 264 | // General format 265 | significant_figures = true; 266 | 267 | if(precision > 0) { --precision; } 268 | } 269 | 270 | CharT significand[36]{}; // 34 characters should be the maximum size needed 271 | int exponent = 0; 272 | 273 | const auto significand_size = Float::convert(significand, exponent, value, format_fixed, precision); 274 | 275 | if(significant_figures) 276 | { 277 | if(exponent >= -4 && exponent <= precision) 278 | { 279 | format_fixed = true; 280 | } 281 | 282 | if(!format.hash()) { precision = significand_size - 1; } 283 | 284 | if(format_fixed) 285 | { 286 | precision -= exponent; 287 | } 288 | } 289 | 290 | int fill_after = 0; 291 | 292 | if(format_fixed) 293 | { 294 | // Fixed point format 295 | if(exponent < 0) 296 | { 297 | // 0.<0>SIGNIFICAND[0:N]<0> 298 | 299 | const int full_digits = precision + 2; 300 | fill_after = format.write_alignment(it, end, full_digits, negative); 301 | 302 | *it++ = '0'; 303 | *it++ = '.'; 304 | 305 | int zero_digits = -exponent - 1; 306 | CharTraits::assign(it, '0', zero_digits); 307 | CharTraits::copy(it, significand, significand_size); 308 | 309 | // Padding is needed if conversion function removes trailing zeros. 310 | zero_digits = precision - zero_digits - significand_size; 311 | CharTraits::assign(it, '0', zero_digits); 312 | } 313 | else 314 | { 315 | const int full_digits = exponent + 1 + precision + static_cast(precision > 0 || format.hash()); 316 | fill_after = format.write_alignment(it, end, full_digits, negative); 317 | 318 | const int ipart_digits = exponent + 1; 319 | 320 | if(ipart_digits >= significand_size) 321 | { 322 | // [SIGNIFICAND]<0><.><0> 323 | 324 | CharTraits::copy(it, significand, significand_size); 325 | CharTraits::assign(it, '0', ipart_digits - significand_size); 326 | 327 | if(precision > 0 || format.hash()) 328 | { 329 | *it++ = '.'; 330 | } 331 | 332 | if(precision > 0) 333 | { 334 | CharTraits::assign(it, '0', precision); 335 | } 336 | } 337 | else 338 | { 339 | // SIGNIFICAND[0:x].SIGNIFICAND[x:N]<0> 340 | 341 | CharTraits::copy(it, significand, ipart_digits); 342 | *it++ = '.'; 343 | 344 | const int copy_size = significand_size - ipart_digits; 345 | CharTraits::copy(it, significand + ipart_digits, copy_size); 346 | 347 | // Padding is needed if conversion function removes trailing zeros. 348 | CharTraits::assign(it, '0', precision - copy_size); 349 | } 350 | } 351 | } 352 | else 353 | { 354 | // Exponent format 355 | // SIGNIFICAND[0:N]<.>eEXP 356 | // OR 357 | // SIGNIFICAND[0].SIGNIFICAND[1:N]<0>eEXP 358 | 359 | const int full_digits = 5 + precision + static_cast(precision > 0 || format.hash()); 360 | fill_after = format.write_alignment(it, end, full_digits, negative); 361 | 362 | *it++ = *significand; 363 | 364 | if(precision > 0 || format.hash()) 365 | { 366 | *it++ = '.'; 367 | 368 | const int copy_size = significand_size - 1; 369 | CharTraits::copy(it, significand + 1, copy_size); 370 | CharTraits::assign(it, '0', precision - copy_size); 371 | } 372 | 373 | write_float_exponent(it, exponent, format.uppercase()); 374 | } 375 | 376 | CharTraits::assign(it, format.fill_char(), fill_after); 377 | 378 | //it += sprintf(it, "[%s] Size:%d Exponent:%d Precision:%d Fixed:%d->", significand, significand_size, exponent, precision, int(format_fixed)); 379 | } 380 | else 381 | { 382 | format_string(it, end, format, format.uppercase() ? "OVF" : "ovf", 3, negative); 383 | } 384 | } 385 | } 386 | } 387 | 388 | static USF_CPP14_CONSTEXPR 389 | void write_float_exponent(iterator& it, int exponent, const bool uppercase) noexcept 390 | { 391 | *it++ = uppercase ? 'E' : 'e'; 392 | 393 | if(exponent < 0) 394 | { 395 | exponent = -exponent; 396 | *it++ = '-'; 397 | } 398 | else 399 | { 400 | *it++ = '+'; 401 | } 402 | 403 | // No point in making a proper integer to string 404 | // conversion for exponent since we only support [e-19; e19]. 405 | assert(exponent <= 19); 406 | 407 | if(exponent < 10) 408 | { 409 | *it++ = '0'; 410 | *it++ = static_cast('0' + exponent); 411 | } 412 | else 413 | { 414 | *it++ = '1'; 415 | *it++ = static_cast('0' + (exponent - 10)); 416 | } 417 | } 418 | 419 | static USF_CPP14_CONSTEXPR 420 | void format_float_zero(iterator& it, const_iterator end, const Format& format, const bool negative) 421 | { 422 | int precision = 0; 423 | 424 | if(format.type_is_float_fixed() || format.type_is_float_scientific()) 425 | { 426 | precision = format.precision(); 427 | } 428 | 429 | int digits = 1; 430 | 431 | if(precision > 0) { digits += precision + 1; } 432 | 433 | if(format.type_is_float_scientific()) { digits += 4; } 434 | 435 | const int fill_after = format.write_alignment(it, end, digits, negative); 436 | 437 | *it++ = '0'; 438 | 439 | if(precision > 0) 440 | { 441 | *it++ = '.'; 442 | CharTraits::assign(it, '0', precision); 443 | } 444 | 445 | if(format.type_is_float_scientific()) 446 | { 447 | *it++ = format.uppercase() ? 'E' : 'e'; 448 | *it++ = '+'; 449 | *it++ = '0'; 450 | *it++ = '0'; 451 | } 452 | 453 | CharTraits::assign(it, format.fill_char(), fill_after); 454 | } 455 | #endif // !defined(USF_DISABLE_FLOAT_SUPPORT) 456 | 457 | static USF_CPP14_CONSTEXPR void format_string(iterator& it, const_iterator end, 458 | Format& format, const usf::BasicStringView& str) 459 | { 460 | // Test for argument type / format match 461 | USF_ENFORCE(format.type_is_none() || format.type_is_string(), std::runtime_error); 462 | 463 | // Characters and strings align to left by default. 464 | format.default_align_left(); 465 | 466 | // If precision is specified use it up to string size. 467 | const int str_length = (format.precision() == -1) 468 | ? static_cast(str.size()) 469 | : std::min(static_cast(format.precision()), static_cast(str.size())); 470 | 471 | format_string(it, end, format, str.data(), str_length); 472 | } 473 | 474 | template ::value, bool>::type = true> 476 | static USF_CPP14_CONSTEXPR void format_string(iterator& it, const_iterator end, 477 | const Format& format, const CharSrc* str, 478 | const int str_length, const bool negative = false) 479 | { 480 | const int fill_after = format.write_alignment(it, end, str_length, negative); 481 | 482 | CharTraits::copy(it, str, str_length); 483 | CharTraits::assign(it, format.fill_char(), fill_after); 484 | } 485 | 486 | // -------------------------------------------------------------------- 487 | // PRIVATE MEMBER VARIABLES 488 | // -------------------------------------------------------------------- 489 | 490 | enum class TypeId 491 | { 492 | kBool = 0, 493 | kChar, 494 | kInt32, 495 | kUint32, 496 | kInt64, 497 | kUint64, 498 | kPointer, 499 | #if !defined(USF_DISABLE_FLOAT_SUPPORT) 500 | kFloat, 501 | #endif 502 | kString, 503 | kCustom 504 | }; 505 | 506 | union 507 | { 508 | bool m_bool; 509 | CharT m_char; 510 | int32_t m_int32; 511 | uint32_t m_uint32; 512 | int64_t m_int64; 513 | uint64_t m_uint64; 514 | std::uintptr_t m_pointer; 515 | #if !defined(USF_DISABLE_FLOAT_SUPPORT) 516 | double m_float; 517 | #endif 518 | usf::BasicStringView m_string; 519 | ArgCustomType m_custom; 520 | }; 521 | 522 | TypeId m_type_id; 523 | }; 524 | 525 | 526 | 527 | 528 | // Boolean 529 | template inline USF_CPP14_CONSTEXPR 530 | Argument make_argument(const bool arg) 531 | { 532 | return arg; 533 | } 534 | 535 | // Character (char) 536 | template inline USF_CPP14_CONSTEXPR 537 | Argument make_argument(const char arg) 538 | { 539 | return static_cast(arg); 540 | } 541 | 542 | // Character (CharT != char) 543 | template ::value, bool>::type = true> 544 | inline USF_CPP14_CONSTEXPR 545 | Argument make_argument(const CharT arg) 546 | { 547 | return arg; 548 | } 549 | 550 | // 8 bit signed integer 551 | template inline USF_CPP14_CONSTEXPR 552 | Argument make_argument(const int8_t arg) 553 | { 554 | return static_cast(arg); 555 | } 556 | 557 | // 8 bit unsigned integer 558 | template inline USF_CPP14_CONSTEXPR 559 | Argument make_argument(const uint8_t arg) 560 | { 561 | return static_cast(arg); 562 | } 563 | 564 | // 16 bit signed integer 565 | template inline USF_CPP14_CONSTEXPR 566 | Argument make_argument(const int16_t arg) 567 | { 568 | return static_cast(arg); 569 | } 570 | 571 | // 16 bit unsigned integer 572 | template inline USF_CPP14_CONSTEXPR 573 | Argument make_argument(const uint16_t arg) 574 | { 575 | return static_cast(arg); 576 | } 577 | 578 | // 32 bit signed integer 579 | template inline USF_CPP14_CONSTEXPR 580 | Argument make_argument(const int arg) 581 | { 582 | return static_cast(arg); 583 | } 584 | 585 | // 32 bit unsigned integer 586 | template inline USF_CPP14_CONSTEXPR 587 | Argument make_argument(const unsigned int arg) 588 | { 589 | return static_cast(arg); 590 | } 591 | 592 | #if (__LONG_MAX__ != __LONG_LONG_MAX__) 593 | 594 | // 32 bit signed integer 595 | template inline USF_CPP14_CONSTEXPR 596 | Argument make_argument(const long int arg) 597 | { 598 | return static_cast(arg); 599 | } 600 | 601 | // 32 bit unsigned integer 602 | template inline USF_CPP14_CONSTEXPR 603 | Argument make_argument(const unsigned long int arg) 604 | { 605 | return static_cast(arg); 606 | } 607 | 608 | #endif // (__LONG_MAX__ != __LONG_LONG_MAX__) 609 | 610 | // 64 bit signed integer 611 | template inline USF_CPP14_CONSTEXPR 612 | Argument make_argument(const int64_t arg) 613 | { 614 | if(arg >= std::numeric_limits::min() 615 | && arg <= std::numeric_limits::max()) 616 | { 617 | return static_cast(arg); 618 | } 619 | 620 | return arg; 621 | } 622 | 623 | // 64 bit unsigned integer 624 | template inline USF_CPP14_CONSTEXPR 625 | Argument make_argument(const uint64_t arg) 626 | { 627 | if(arg <= std::numeric_limits::max()) 628 | { 629 | return static_cast(arg); 630 | } 631 | 632 | return arg; 633 | } 634 | 635 | // Pointer (void*) 636 | template inline USF_CPP14_CONSTEXPR 637 | Argument make_argument(void* arg) 638 | { 639 | return arg; 640 | } 641 | 642 | // Pointer (const void*) 643 | template inline USF_CPP14_CONSTEXPR 644 | Argument make_argument(const void* arg) 645 | { 646 | return arg; 647 | } 648 | 649 | #if !defined(USF_DISABLE_FLOAT_SUPPORT) 650 | // Floating point (float) 651 | template inline USF_CPP14_CONSTEXPR 652 | Argument make_argument(float arg) 653 | { 654 | return static_cast(arg); 655 | } 656 | 657 | // Floating point (double) 658 | template inline USF_CPP14_CONSTEXPR 659 | Argument make_argument(double arg) 660 | { 661 | return arg; 662 | } 663 | #endif // !defined(USF_DISABLE_FLOAT_SUPPORT) 664 | 665 | // String (convertible to string view) 666 | template >::value, bool>::type = true> 668 | inline USF_CPP14_CONSTEXPR Argument make_argument(const T& arg) 669 | { 670 | return usf::BasicStringView(arg); 671 | } 672 | 673 | } // namespace internal 674 | 675 | // User-defined custom type formatter forward declaration 676 | template 677 | struct Formatter 678 | { 679 | static BasicStringSpan format_to(BasicStringSpan, const T&); 680 | }; 681 | 682 | namespace internal 683 | { 684 | 685 | // User-defined custom type 686 | template >::value, bool>::type = true> 688 | inline USF_CPP14_CONSTEXPR Argument make_argument(const T& arg) 689 | { 690 | using _T = typename std::decay::type; 691 | 692 | return ArgCustomType::template create<_T, &usf::Formatter::format_to>(&arg); 693 | } 694 | 695 | } // namespace internal 696 | } // namespace usf 697 | 698 | #endif // USF_ARGUMENT_HPP 699 | -------------------------------------------------------------------------------- /include/usf/develop/usf_config.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_config.hpp 3 | // @brief usflib configuration header file. 4 | // @date 14 January 2019 5 | // ---------------------------------------------------------------------------- 6 | 7 | #ifndef USF_CONFIG_HPP 8 | #define USF_CONFIG_HPP 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // ---------------------------------------------------------------------------- 21 | // usflib configuration options 22 | // ---------------------------------------------------------------------------- 23 | 24 | // Configuration of floating point support. 25 | // USF_DISABLE_FLOAT_SUPPORT : disables the support of floating point types (it will save considerable code size) 26 | 27 | // Configuration of format output string termination option. 28 | // USF_DISABLE_STRING_TERMINATION : disables the null termination of the format output string 29 | 30 | // Configuration of possible behavior when a condition is violated. 31 | // USF_TERMINATE_ON_CONTRACT_VIOLATION : std::terminate() will be called (default) 32 | // USF_ABORT_ON_CONTRACT_VIOLATION : std::abort() will be called (more suitable for embedded platforms, maybe?) 33 | // USF_THROW_ON_CONTRACT_VIOLATION : an exception will be thrown 34 | 35 | 36 | // ---------------------------------------------------------------------------- 37 | // Compiler version detection 38 | // ---------------------------------------------------------------------------- 39 | 40 | // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) 41 | // MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) 42 | #if defined(_MSC_VER) && !defined(__clang__) 43 | # define USF_COMPILER_MSVC 44 | # define USF_MSVC_VERSION (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900))) 45 | # if (USF_MSVC_VERSION < 140) 46 | # error usflib requires MSVC++ 14.0 or greater 47 | # endif 48 | // Note: VC14.0/1900 (VS2015) lacks too much from C++14. 49 | # define USF_CPLUSPLUS (_MSC_VER <= 1900 ? 201103L : _MSVC_LANG) 50 | #endif 51 | 52 | #define USF_COMPILER_VERSION(major, minor, patch) (10 * (10 * (major) + (minor)) + (patch)) 53 | 54 | #if defined(__clang__) 55 | # define USF_COMPILER_CLANG 56 | # define USF_CLANG_VERSION USF_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) 57 | # if (USF_CLANG_VERSION < 340) 58 | # error usflib requires Clang 3.4.0 or greater 59 | # endif 60 | #endif 61 | 62 | #if defined(__GNUC__) && !defined(__clang__) 63 | # define USF_COMPILER_GCC 64 | # define USF_GCC_VERSION USF_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) 65 | # if (USF_GCC_VERSION < 481) 66 | # error usflib requires GCC 4.8.1 or greater 67 | # endif 68 | #endif 69 | 70 | 71 | // ---------------------------------------------------------------------------- 72 | // C++ language version detection 73 | // ---------------------------------------------------------------------------- 74 | #ifndef USF_CPLUSPLUS 75 | # define USF_CPLUSPLUS __cplusplus 76 | #endif 77 | 78 | #define USF_CPP11_OR_GREATER (USF_CPLUSPLUS >= 201103L) 79 | #define USF_CPP14_OR_GREATER (USF_CPLUSPLUS >= 201402L) 80 | #define USF_CPP17_OR_GREATER (USF_CPLUSPLUS >= 201703L) 81 | 82 | #if !USF_CPP11_OR_GREATER 83 | #error usflib requires compiler and library support \ 84 | for the ISO C++ 2011 standard. This support must be enabled \ 85 | with the -std=c++11 or -std=gnu++11 compiler options. 86 | #endif 87 | 88 | // C++14 features 89 | #if USF_CPP14_OR_GREATER 90 | # define USF_CPP14_CONSTEXPR constexpr 91 | # define USF_CPP14_CONSTEXPR_VAR constexpr 92 | #else 93 | # define USF_CPP14_CONSTEXPR 94 | # define USF_CPP14_CONSTEXPR_VAR const 95 | #endif 96 | 97 | // C++17 features 98 | #if USF_CPP17_OR_GREATER 99 | # define USF_CPP17_CONSTEXPR constexpr 100 | #else 101 | # define USF_CPP17_CONSTEXPR 102 | #endif 103 | 104 | // Fall through attribute 105 | #if USF_CPP17_OR_GREATER 106 | # define USF_FALLTHROUGH [[fallthrough]] 107 | #else 108 | # if defined(USF_COMPILER_GCC) && (USF_GCC_VERSION >= 710) 109 | # define USF_FALLTHROUGH [[gnu::fallthrough]] 110 | # elif defined(USF_COMPILER_CLANG) 111 | # define USF_FALLTHROUGH [[clang::fallthrough]] 112 | # else 113 | # define USF_FALLTHROUGH /*fall through*/ 114 | # endif 115 | #endif 116 | 117 | // Always inline attribute 118 | #if defined(USF_COMPILER_GCC) || defined(USF_COMPILER_CLANG) 119 | # define USF_ALWAYS_INLINE [[gnu::always_inline]] 120 | #elif defined(USF_COMPILER_MSVC) 121 | # define USF_ALWAYS_INLINE __forceinline 122 | #else 123 | # define USF_ALWAYS_INLINE inline 124 | #endif 125 | 126 | // __has_include() support 127 | #if defined(__has_include) && !defined(__INTELLISENSE__) 128 | # define USF_HAS_INCLUDE(x) __has_include(x) 129 | #else 130 | # define USF_HAS_INCLUDE(x) 0 131 | #endif 132 | 133 | // std::string_view support 134 | #if USF_CPP17_OR_GREATER && USF_HAS_INCLUDE() 135 | # include 136 | # define USF_STD_BASIC_STRING_VIEW std::basic_string_view 137 | #elif USF_CPP14_OR_GREATER && USF_HAS_INCLUDE() 138 | # include 139 | # define USF_STD_BASIC_STRING_VIEW std::experimental::basic_string_view 140 | #endif 141 | 142 | // char8_t support (C++20 only) 143 | #if defined(__cpp_lib_char8_t) && (__cpp_lib_char8_t >= 201811L) 144 | # define USF_CPP20_CHAR8_T_SUPPORT 145 | #endif 146 | 147 | 148 | // ---------------------------------------------------------------------------- 149 | // Target detection (maybe not the best way of doing it...) 150 | // ---------------------------------------------------------------------------- 151 | #if (UINTPTR_MAX == UINT32_MAX) 152 | # define USF_TARGET_32_BITS 1 153 | #elif (UINTPTR_MAX == UINT64_MAX) 154 | # define USF_TARGET_64_BITS 1 155 | #else 156 | # error "usflib could not determine target architecture." 157 | #endif 158 | 159 | 160 | // ---------------------------------------------------------------------------- 161 | // Missing intrinsic functions definition for MSVC 162 | // ---------------------------------------------------------------------------- 163 | #if defined(USF_COMPILER_MSVC) 164 | #include 165 | 166 | #pragma intrinsic(_BitScanReverse, _BitScanReverse64) 167 | 168 | int __builtin_clz(uint32_t value) 169 | { 170 | unsigned long leading_zero = 0; 171 | return _BitScanReverse(&leading_zero, value) ? static_cast(31 - leading_zero) : 32; 172 | } 173 | 174 | int __builtin_clzll(uint64_t value) 175 | { 176 | unsigned long leading_zero = 0; 177 | return _BitScanReverse64(&leading_zero, value) ? static_cast(63 - leading_zero) : 64; 178 | } 179 | #endif // defined(USF_COMPILER_MSVC) 180 | 181 | 182 | // ---------------------------------------------------------------------------- 183 | // Error handling 184 | // ---------------------------------------------------------------------------- 185 | // Two macros ensures any macro passed will 186 | // be expanded before being stringified. 187 | #define USF_STRINGIFY_DETAIL(x) #x 188 | #define USF_STRINGIFY(x) USF_STRINGIFY_DETAIL(x) 189 | 190 | #if defined(USF_THROW_ON_CONTRACT_VIOLATION) 191 | 192 | namespace usf 193 | { 194 | namespace internal 195 | { 196 | template 197 | [[noreturn]] USF_ALWAYS_INLINE constexpr 198 | void throw_exception(const char* const msg) 199 | { 200 | static_assert(std::is_convertible::value, 201 | "usf::throw_exception(): exception type should inherit from std::exception."); 202 | 203 | throw Except(msg); 204 | } 205 | } // namespace internal 206 | } // namespace usf 207 | 208 | # define USF_CONTRACT_VIOLATION(except) usf::internal::throw_exception("Failure at " __FILE__ ", Line " USF_STRINGIFY(__LINE__)) 209 | #elif defined(USF_ABORT_ON_CONTRACT_VIOLATION) 210 | # define USF_CONTRACT_VIOLATION(except) std::abort() 211 | #else 212 | # define USF_CONTRACT_VIOLATION(except) std::terminate() 213 | #endif 214 | 215 | #define USF_ENFORCE(cond, except) ((!!(cond)) ? static_cast(0) : USF_CONTRACT_VIOLATION(except)) 216 | 217 | #endif // USF_CONFIG_HPP 218 | -------------------------------------------------------------------------------- /include/usf/develop/usf_float.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_float.hpp 3 | // @brief Floating point conversion and helper functions. Naive and limited 4 | // implementation with the usual precision/rounding errors (good for now). 5 | // @date 07 January 2019 6 | // ---------------------------------------------------------------------------- 7 | 8 | #if !defined(USF_DISABLE_FLOAT_SUPPORT) 9 | 10 | #ifndef USF_FLOAT_HPP 11 | #define USF_FLOAT_HPP 12 | 13 | namespace usf 14 | { 15 | namespace internal 16 | { 17 | 18 | class Float 19 | { 20 | public: 21 | 22 | // -------------------------------------------------------------------- 23 | // PUBLIC STATIC FUNCTIONS 24 | // -------------------------------------------------------------------- 25 | 26 | template static USF_CPP14_CONSTEXPR 27 | int convert(CharT* const significand, int& exponent, 28 | double value, const bool format_fixed, const int precision) noexcept 29 | { 30 | uint64_t ipart = 0; 31 | uint64_t fpart = 0; 32 | 33 | int ipart_digits = 0; 34 | int fpart_digits = 0; 35 | 36 | int fpart_padding = 0; 37 | 38 | if(value < 1) 39 | { 40 | // Negative exponent 41 | 42 | value *= 1e19; 43 | 44 | fpart = static_cast(value); 45 | fpart_digits = Integer::count_digits_dec(fpart); 46 | 47 | exponent = fpart_digits - 20; 48 | 49 | fpart_padding = -exponent - 1; 50 | 51 | //if(!format_fixed && precision > 19 - fpart_padding) 52 | if(fpart_padding > 14 - precision) 53 | { 54 | fpart = static_cast(value * static_cast(Integer::pow10_uint64(fpart_padding))); 55 | fpart_digits = Integer::count_digits_dec(fpart); 56 | } 57 | } 58 | else 59 | { 60 | // Positive exponent 61 | 62 | ipart = static_cast(value); 63 | ipart_digits = Integer::count_digits_dec(ipart); 64 | 65 | fpart = static_cast((value - static_cast(ipart)) * 1e14); 66 | fpart_digits = Integer::count_digits_dec(fpart); 67 | 68 | exponent = ipart_digits - 1; 69 | 70 | fpart_padding = 14 - fpart_digits; 71 | } 72 | 73 | const auto round_index = 1 + precision + (format_fixed ? exponent : 0); 74 | 75 | if(round_index < 0) 76 | { 77 | // Specified precision higher than converted value. 78 | // Should print all zeros. Bail! 79 | significand[0] = '0'; 80 | exponent = 0; 81 | return 1; 82 | } 83 | 84 | CharT* it = significand; 85 | 86 | if(ipart != 0) 87 | { 88 | it += ipart_digits; 89 | Integer::convert_dec(it, ipart); 90 | } 91 | 92 | if(fpart != 0) 93 | { 94 | if(ipart != 0) 95 | { 96 | CharTraits::assign(it, '0', fpart_padding); 97 | } 98 | 99 | it += fpart_digits; 100 | Integer::convert_dec(it, fpart); 101 | } 102 | 103 | const auto significand_size = remove_trailing_zeros(significand, it); 104 | 105 | if(significand_size <= round_index) 106 | { 107 | // Rounding not needed. Bail! 108 | return significand_size; 109 | } 110 | 111 | //Round to the specified precision. 112 | return round(significand, significand_size, exponent, format_fixed, round_index); 113 | } 114 | 115 | private: 116 | 117 | // -------------------------------------------------------------------- 118 | // PRIVATE STATIC FUNCTIONS 119 | // -------------------------------------------------------------------- 120 | 121 | template static USF_CPP14_CONSTEXPR 122 | int round(CharT* const significand, const int significand_size, int& exponent, 123 | const bool format_fixed, const int round_index) noexcept 124 | { 125 | CharT* it = significand + round_index; 126 | 127 | bool round_up = false; 128 | 129 | if(round_index == significand_size - 1) 130 | { 131 | // Round the last digit of the significand buffer. 132 | // It can simultaneously be the first one if the 133 | // significant buffer has only one digit. 134 | 135 | const bool prev_digit_odd = (round_index > 0) ? (('0' - *(it - 1)) & 1) != 0 : false; 136 | 137 | if(*it > '5' || (*it == '5' && prev_digit_odd)) 138 | { 139 | // Round up if digit is: 140 | // 1) greater than 5 141 | // e.g. 2.6 -> 3 142 | // 2) exactly 5 and previous digit is odd 143 | // e.g. 2.5 -> 2 144 | // e.g. 3.5 -> 3 145 | round_up = true; 146 | } 147 | } 148 | else if(*it >= '5') 149 | { 150 | // Round any digit except the last one. Since the trailing zeros were 151 | // removed, we only need to test if the digit is at least '5' because it 152 | // is granted that other non-zero digits are present after this position. 153 | round_up = true; 154 | } 155 | 156 | if(round_up) 157 | { 158 | bool carry = false; 159 | 160 | if(round_index > 0) 161 | { 162 | --it; 163 | 164 | do 165 | { 166 | if(*it < '9') 167 | { 168 | carry = false; 169 | ++(*it); 170 | } 171 | else 172 | { 173 | carry = true; 174 | *it = '0'; 175 | } 176 | }while(--it >= significand && carry); 177 | } 178 | else 179 | { 180 | carry = true; 181 | } 182 | 183 | // Buffer termination is not necessary since the caller functions 184 | // rely on the returned size and not on null terminator. 185 | 186 | if(carry) 187 | { 188 | significand[0] = '1'; 189 | ++exponent; 190 | return 1; 191 | } 192 | } 193 | else if(round_index == 0) 194 | { 195 | significand[0] = '0'; 196 | exponent = 0; 197 | return 1; 198 | } 199 | 200 | // Do not remove the trailing zeros if format is fixed. 201 | if(format_fixed) { return round_index; } 202 | 203 | return remove_trailing_zeros(significand, significand + round_index); 204 | } 205 | 206 | // Evaluates the range [first, last), truncates all the trailing zeros and return the 207 | // new range size. Keeps always at least 1 element of the range (even if it is zero). 208 | template static USF_CPP14_CONSTEXPR 209 | int remove_trailing_zeros(const CharT* const first, const CharT* last) noexcept 210 | { 211 | while((last - 1) > first && *(last - 1) == '0') { --last; } 212 | 213 | // Buffer termination is not really necessary since the caller 214 | // functions rely on the returned size and not on null terminator. 215 | 216 | return static_cast(last - first); 217 | } 218 | }; 219 | 220 | } // namespace internal 221 | } // namespace usf 222 | 223 | #endif // USF_FLOAT_HPP 224 | #endif // !defined(USF_DISABLE_FLOAT_SUPPORT) 225 | -------------------------------------------------------------------------------- /include/usf/develop/usf_integer.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_integer.hpp 3 | // @brief Integer conversion and helper functions. 4 | // @date 14 January 2019 5 | // ---------------------------------------------------------------------------- 6 | 7 | #ifndef USF_INTEGER_HPP 8 | #define USF_INTEGER_HPP 9 | 10 | namespace usf 11 | { 12 | namespace internal 13 | { 14 | 15 | constexpr uint32_t pow10_uint32_lut[] 16 | { 17 | 1, 18 | 10, 19 | 100, 20 | 1000, 21 | 10000, 22 | 100000, 23 | 1000000, 24 | 10000000, 25 | 100000000, 26 | 1000000000 27 | }; 28 | 29 | constexpr uint64_t pow10_uint64_lut[] 30 | { 31 | 1, 32 | 10, 33 | 100, 34 | 1000, 35 | 10000, 36 | 100000, 37 | 1000000, 38 | 10000000, 39 | 100000000, 40 | 1000000000, 41 | 10000000000, 42 | 100000000000, 43 | 1000000000000, 44 | 10000000000000, 45 | 100000000000000, 46 | 1000000000000000, 47 | 10000000000000000, 48 | 100000000000000000, 49 | 1000000000000000000, 50 | 10000000000000000000U 51 | }; 52 | 53 | constexpr char digits_hex_uppercase[]{"0123456789ABCDEF"}; 54 | constexpr char digits_hex_lowercase[]{"0123456789abcdef"}; 55 | 56 | class Integer 57 | { 58 | public: 59 | 60 | // -------------------------------------------------------------------- 61 | // PUBLIC STATIC FUNCTIONS 62 | // -------------------------------------------------------------------- 63 | 64 | // -------- POWERS OF 10 ---------------------------------------------- 65 | static USF_CPP14_CONSTEXPR uint32_t pow10_uint32(const int index) noexcept 66 | { 67 | assert(index >= 0 && index < 10); 68 | 69 | return pow10_uint32_lut[index]; 70 | } 71 | 72 | static USF_CPP14_CONSTEXPR uint64_t pow10_uint64(const int index) noexcept 73 | { 74 | assert(index >= 0 && index < 20); 75 | 76 | return pow10_uint64_lut[index]; 77 | } 78 | 79 | // -------- COUNT DIGITS ---------------------------------------------- 80 | // Based on the code from: 81 | // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 82 | // --------------------- ---------------------------------------------- 83 | 84 | static USF_CPP14_CONSTEXPR int count_digits_dec(const uint32_t n) noexcept 85 | { 86 | if(n < 10) return 1; 87 | 88 | // The algorithm below doesn't work when `n` is 0 because: 89 | // 1. the result of __builtin_clz() is undefined if `n` is 0. 90 | // 2. the `pow10_uint32_lut` lookup table has the value 1 in 91 | // the first element and not a 0 as the algorithm expects. 92 | // (both cases are covered by the previous if statement or 93 | // by the slower commented OR operation below). 94 | 95 | // n = n | 1; 96 | 97 | const int t = (32 - __builtin_clz(n)) * 1233 >> 12; 98 | return t - (n < pow10_uint32_lut[t]) + 1; 99 | } 100 | 101 | static USF_CPP14_CONSTEXPR int count_digits_dec(const uint64_t n) noexcept 102 | { 103 | if(n <= std::numeric_limits::max()) 104 | { 105 | return count_digits_dec(static_cast(n)); 106 | } 107 | 108 | // The algorithm below doesn't work when `n` is 0 because: 109 | // 1. the result of __builtin_clzll() is undefined if `n` is 0. 110 | // 2. the `pow10_uint64_lut` lookup table has the value 1 in 111 | // the first element and not a 0 as the algorithm expects. 112 | // (both cases are covered by the previous if statement or 113 | // by the slower commented OR operation below). 114 | 115 | // n = n | 1; 116 | 117 | const int t = (64 - __builtin_clzll(n)) * 1233 >> 12; 118 | return t - (n < pow10_uint64_lut[t]) + 1; 119 | } 120 | 121 | static USF_CPP14_CONSTEXPR int count_digits_bin(const uint32_t n) noexcept 122 | { 123 | // The result of __builtin_clz() is undefined if `n` is 0. 124 | return (n < 2) ? 1 : (32 - __builtin_clz(n)); 125 | } 126 | 127 | static USF_CPP14_CONSTEXPR int count_digits_bin(const uint64_t n) noexcept 128 | { 129 | // The result of __builtin_clzll() is undefined if `n` is 0. 130 | return (n < 2) ? 1 : (64 - __builtin_clzll(n)); 131 | } 132 | 133 | template ::is_integer && std::is_unsigned::value, bool>::type = true> 135 | static USF_CPP14_CONSTEXPR int count_digits_oct(T n) noexcept 136 | { 137 | int digits = 1; 138 | while((n >>= 3U) != 0) { ++digits; } 139 | return digits; 140 | } 141 | 142 | template ::is_integer && std::is_unsigned::value, bool>::type = true> 144 | static USF_CPP14_CONSTEXPR int count_digits_hex(T n) noexcept 145 | { 146 | int digits = 1; 147 | while((n >>= 4U) != 0) { ++digits; } 148 | return digits; 149 | } 150 | 151 | // -------- FAST DIVIDE BY 10 ----------------------------------------- 152 | // Based on the code from Hacker's Delight: 153 | // http://www.hackersdelight.org/divcMore.pdf 154 | // --------------------- ---------------------------------------------- 155 | static USF_CPP14_CONSTEXPR uint32_t div10(const uint32_t n) noexcept 156 | { 157 | #if defined(__arm__) 158 | uint32_t q = (n >> 1) + (n >> 2); 159 | q += (q >> 4); 160 | q += (q >> 8); 161 | q += (q >> 16); 162 | q >>= 3; 163 | 164 | const uint32_t r = n - (q << 3) - (q << 1); 165 | 166 | return q + ((r + 6) >> 4); 167 | // return q + (r > 9); 168 | #else 169 | return n / 10; 170 | #endif 171 | } 172 | 173 | static USF_CPP14_CONSTEXPR uint64_t div10(const uint64_t n) noexcept 174 | { 175 | #if defined(__arm__) 176 | uint64_t q = (n >> 1) + (n >> 2); 177 | q += (q >> 4); 178 | q += (q >> 8); 179 | q += (q >> 16); 180 | q += (q >> 32); 181 | q >>= 3; 182 | 183 | const uint64_t r = n - (q << 3) - (q << 1); 184 | 185 | return q + ((r + 6) >> 4); 186 | // return q + (r > 9); 187 | #else 188 | return n / 10; 189 | #endif 190 | } 191 | 192 | // -------- CONVERTERS ------------------------------------------------ 193 | // The following converters write the value from back to front. 194 | // It is assumed that the pointer `dst` is already placed at the 195 | // position after the last character. The pointer position is 196 | // calculated using the corresponding count_digits_xxx() functions. 197 | 198 | // Example: 199 | // value -> 1234 200 | // array -> [........] 201 | // dst -> ^ 202 | 203 | // -------- DECIMAL CONVERSION ---------------------------------------- 204 | template 205 | static USF_CPP14_CONSTEXPR void convert_dec(CharT* dst, uint32_t value) noexcept 206 | { 207 | do 208 | { 209 | const uint32_t v = value; 210 | value = div10(value); 211 | *(--dst) = static_cast('0' + (v - (value * 10))); 212 | }while(value); 213 | } 214 | 215 | template 216 | static USF_CPP14_CONSTEXPR void convert_dec(CharT* dst, uint64_t value) noexcept 217 | { 218 | while(value > std::numeric_limits::max()) 219 | { 220 | const uint64_t v = value; 221 | value = div10(value); 222 | *(--dst) = static_cast('0' + (v - (value * 10))); 223 | } 224 | 225 | convert_dec(dst, static_cast(value)); 226 | } 227 | 228 | // -------- BINARY CONVERSION ----------------------------------------- 229 | template 230 | static USF_CPP14_CONSTEXPR void convert_bin(CharT* dst, uint32_t value) noexcept 231 | { 232 | do 233 | { 234 | const uint32_t v = value; 235 | value >>= 1U; 236 | *(--dst) = static_cast('0' + (v - (value << 1U))); 237 | }while(value); 238 | } 239 | 240 | template 241 | static USF_CPP14_CONSTEXPR void convert_bin(CharT* dst, uint64_t value) noexcept 242 | { 243 | while(value > std::numeric_limits::max()) 244 | { 245 | const uint64_t v = value; 246 | value >>= 1U; 247 | *(--dst) = static_cast('0' + (v - (value << 1U))); 248 | } 249 | 250 | convert_bin(dst, static_cast(value)); 251 | } 252 | 253 | // -------- OCTAL CONVERSION ------------------------------------------ 254 | template 255 | static USF_CPP14_CONSTEXPR void convert_oct(CharT* dst, uint32_t value) noexcept 256 | { 257 | do 258 | { 259 | const uint32_t v = value; 260 | value >>= 3U; 261 | *(--dst) = static_cast('0' + (v - (value << 3U))); 262 | }while(value); 263 | } 264 | 265 | template 266 | static USF_CPP14_CONSTEXPR void convert_oct(CharT* dst, uint64_t value) noexcept 267 | { 268 | while(value > std::numeric_limits::max()) 269 | { 270 | const uint64_t v = value; 271 | value >>= 3U; 272 | *(--dst) = static_cast('0' + (v - (value << 3U))); 273 | } 274 | 275 | convert_oct(dst, static_cast(value)); 276 | } 277 | 278 | // -------- HEXADECIMAL CONVERSION ------------------------------------ 279 | template 280 | static USF_CPP14_CONSTEXPR void convert_hex(CharT* dst, uint32_t value, const bool uppercase) noexcept 281 | { 282 | const char* digits = uppercase ? digits_hex_uppercase : digits_hex_lowercase; 283 | 284 | do 285 | { 286 | const uint32_t v = value; 287 | value >>= 4U; 288 | *(--dst) = static_cast(digits[v - (value << 4U)]); 289 | }while(value); 290 | 291 | } 292 | 293 | template 294 | static USF_CPP14_CONSTEXPR void convert_hex(CharT* dst, uint64_t value, const bool uppercase) noexcept 295 | { 296 | const char* digits = uppercase ? digits_hex_uppercase : digits_hex_lowercase; 297 | 298 | while(value > std::numeric_limits::max()) 299 | { 300 | const uint64_t v = value; 301 | value >>= 4U; 302 | *(--dst) = static_cast(digits[v - (value << 4U)]); 303 | } 304 | 305 | convert_hex(dst, static_cast(value), uppercase); 306 | } 307 | }; 308 | 309 | } // namespace internal 310 | } // namespace usf 311 | 312 | #endif // USF_INTEGER_HPP 313 | -------------------------------------------------------------------------------- /include/usf/develop/usf_main.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_main.hpp 3 | // @brief Main process functions and public interface. 4 | // @date 14 January 2019 5 | // ---------------------------------------------------------------------------- 6 | 7 | #ifndef USF_MAIN_HPP 8 | #define USF_MAIN_HPP 9 | 10 | namespace usf 11 | { 12 | namespace internal 13 | { 14 | 15 | template USF_CPP14_CONSTEXPR 16 | void parse_format_string(usf::BasicStringSpan& str, usf::BasicStringView& fmt) 17 | { 18 | CharT* str_it = str.begin(); 19 | const CharT* fmt_it = fmt.cbegin(); 20 | 21 | while(fmt_it < fmt.cend() && str_it < str.end()) 22 | { 23 | if(*fmt_it == '{' ) 24 | { 25 | if(*(fmt_it + 1) == '{') 26 | { 27 | // Found '{{' escape character, skip the first and copy the second '{'. 28 | ++fmt_it; 29 | *str_it++ = *fmt_it++; 30 | } 31 | else 32 | { 33 | // A type format should follow... 34 | break; 35 | } 36 | } 37 | else if(*fmt_it == '}') 38 | { 39 | USF_ENFORCE(*(fmt_it + 1) == '}', std::runtime_error); 40 | 41 | // Found '}}' escape character, skip the first and copy the second '}'. 42 | ++fmt_it; 43 | *str_it++ = *fmt_it++; 44 | } 45 | else 46 | { 47 | // Copy literal text 48 | *str_it++ = *fmt_it++; 49 | } 50 | } 51 | 52 | //USF_ENFORCE(str_it < str.end(), std::runtime_error); 53 | 54 | str.remove_prefix(str_it - str.begin()); 55 | fmt.remove_prefix(fmt_it - fmt.cbegin()); 56 | } 57 | 58 | 59 | 60 | 61 | template USF_CPP14_CONSTEXPR 62 | void process(usf::BasicStringSpan& str, usf::BasicStringView& fmt, 63 | const Argument* const args, const int arg_count) 64 | { 65 | // Argument's sequential index 66 | int arg_seq_index = 0; 67 | 68 | parse_format_string(str, fmt); 69 | 70 | while(!fmt.empty()) 71 | { 72 | ArgFormat format(fmt, arg_count); 73 | 74 | // Determine which argument index to use, sequential or positional. 75 | int arg_index = format.index(); 76 | 77 | if(arg_index < 0) 78 | { 79 | USF_ENFORCE(arg_seq_index < arg_count, std::runtime_error); 80 | arg_index = arg_seq_index++; 81 | } 82 | 83 | args[arg_index].format(str, format); 84 | 85 | parse_format_string(str, fmt); 86 | } 87 | } 88 | 89 | } // namespace internal 90 | 91 | 92 | 93 | 94 | template USF_CPP14_CONSTEXPR 95 | BasicStringSpan basic_format_to(BasicStringSpan str, BasicStringView fmt) 96 | { 97 | auto str_begin = str.begin(); 98 | 99 | internal::parse_format_string(str, fmt); 100 | 101 | USF_ENFORCE(fmt.empty(), std::runtime_error); 102 | 103 | #if !defined(USF_DISABLE_STRING_TERMINATION) 104 | // If not disabled in configuration, null terminate the resulting string. 105 | str[0] = CharT{}; 106 | #endif 107 | 108 | // Return a string span to the resulting string 109 | return BasicStringSpan(str_begin, str.begin()); 110 | } 111 | 112 | template USF_CPP14_CONSTEXPR 113 | BasicStringSpan basic_format_to(BasicStringSpan str, BasicStringView fmt, Args&&... args) 114 | { 115 | // Nobody should be that crazy, still... it costs nothing to be sure! 116 | static_assert(sizeof...(Args) < 128, "usf::basic_format_to(): crazy number of arguments supplied!"); 117 | 118 | auto str_begin = str.begin(); 119 | 120 | const internal::Argument arguments[sizeof...(Args)]{internal::make_argument(args)...}; 121 | 122 | internal::process(str, fmt, arguments, static_cast(sizeof...(Args))); 123 | 124 | #if !defined(USF_DISABLE_STRING_TERMINATION) 125 | // If not disabled in configuration, null terminate the resulting string. 126 | str[0] = CharT{}; 127 | #endif 128 | 129 | // Return a string span to the resulting string 130 | return BasicStringSpan(str_begin, str.begin()); 131 | } 132 | 133 | template USF_CPP14_CONSTEXPR 134 | CharT* basic_format_to(CharT* str, const std::ptrdiff_t str_count, BasicStringView fmt, Args&&... args) 135 | { 136 | return basic_format_to(BasicStringSpan(str, str_count), fmt, args...).end(); 137 | } 138 | 139 | 140 | 141 | 142 | // ---------------------------------------------------------------------------- 143 | // Formats a char string 144 | // --------------------------------------------------------------------------- 145 | template USF_CPP14_CONSTEXPR 146 | StringSpan format_to(StringSpan str, StringView fmt, Args&&... args) 147 | { 148 | return basic_format_to(str, fmt, args...); 149 | } 150 | 151 | template USF_CPP14_CONSTEXPR 152 | char* format_to(char* str, const std::ptrdiff_t str_count, StringView fmt, Args&&... args) 153 | { 154 | return basic_format_to(str, str_count, fmt, args...); 155 | } 156 | 157 | // ---------------------------------------------------------------------------- 158 | // Formats a wchar_t string 159 | // --------------------------------------------------------------------------- 160 | template USF_CPP14_CONSTEXPR 161 | WStringSpan format_to(WStringSpan str, WStringView fmt, Args&&... args) 162 | { 163 | return basic_format_to(str, fmt, args...); 164 | } 165 | 166 | template USF_CPP14_CONSTEXPR 167 | wchar_t* format_to(wchar_t* str, const std::ptrdiff_t str_count, WStringView fmt, Args&&... args) 168 | { 169 | return basic_format_to(str, str_count, fmt, args...); 170 | } 171 | 172 | // ---------------------------------------------------------------------------- 173 | // Formats a char8_t string 174 | // --------------------------------------------------------------------------- 175 | #if defined(USF_CPP20_CHAR8_T_SUPPORT) 176 | template USF_CPP14_CONSTEXPR 177 | U8StringSpan format_to(U8StringSpan str, U8StringView fmt, Args&&... args) 178 | { 179 | return basic_format_to(str, fmt, args...); 180 | } 181 | 182 | template USF_CPP14_CONSTEXPR 183 | char8_t* format_to(char8_t* str, const std::ptrdiff_t str_count, U8StringView fmt, Args&&... args) 184 | { 185 | return basic_format_to(str, str_count, fmt, args...); 186 | } 187 | #endif // defined(USF_CPP20_CHAR8_T_SUPPORT) 188 | 189 | // ---------------------------------------------------------------------------- 190 | // Formats a char16_t string 191 | // --------------------------------------------------------------------------- 192 | template USF_CPP14_CONSTEXPR 193 | U16StringSpan format_to(U16StringSpan str, U16StringView fmt, Args&&... args) 194 | { 195 | return basic_format_to(str, fmt, args...); 196 | } 197 | 198 | template USF_CPP14_CONSTEXPR 199 | char16_t* format_to(char16_t* str, const std::ptrdiff_t str_count, U16StringView fmt, Args&&... args) 200 | { 201 | return basic_format_to(str, str_count, fmt, args...); 202 | } 203 | 204 | // ---------------------------------------------------------------------------- 205 | // Formats a char32_t string 206 | // --------------------------------------------------------------------------- 207 | template USF_CPP14_CONSTEXPR 208 | U32StringSpan format_to(U32StringSpan str, U32StringView fmt, Args&&... args) 209 | { 210 | return basic_format_to(str, fmt, args...); 211 | } 212 | 213 | template USF_CPP14_CONSTEXPR 214 | char32_t* format_to(char32_t* str, const std::ptrdiff_t str_count, U32StringView fmt, Args&&... args) 215 | { 216 | return basic_format_to(str, str_count, fmt, args...); 217 | } 218 | 219 | // ---------------------------------------------------------------------------- 220 | // Formats a byte string as char string 221 | // ---------------------------------------------------------------------------- 222 | template USF_CPP14_CONSTEXPR 223 | ByteStringSpan format_to(ByteStringSpan str, StringView fmt, Args&&... args) 224 | { 225 | static_assert(CHAR_BIT == 8, "usf::format_to(): invalid char size."); 226 | char *end = basic_format_to(reinterpret_cast(str.data()), str.size(), fmt, args...); 227 | 228 | return ByteStringSpan(str.begin(), reinterpret_cast(end)); 229 | } 230 | 231 | template USF_CPP14_CONSTEXPR 232 | uint8_t* format_to(uint8_t* str, const std::ptrdiff_t str_count, StringView fmt, Args&&... args) 233 | { 234 | static_assert(CHAR_BIT == 8, "usf::format_to(): invalid char size."); 235 | return reinterpret_cast(basic_format_to(reinterpret_cast(str), str_count, fmt, args...)); 236 | } 237 | 238 | } // namespace usf 239 | 240 | #endif // USF_MAIN_HPP 241 | -------------------------------------------------------------------------------- /include/usf/develop/usf_string_span.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_string_span.hpp 3 | // @brief String span class (minimal implementation). 4 | // Intended to replace basic functionality of std::span throughout 5 | // the library. It uses a begin / end iterator approach instead of 6 | // the standard data pointer and size implementations. 7 | // @date 14 January 2019 8 | // ---------------------------------------------------------------------------- 9 | 10 | #ifndef USF_STRING_SPAN_HPP 11 | #define USF_STRING_SPAN_HPP 12 | 13 | namespace usf 14 | { 15 | 16 | template 17 | class BasicStringSpan 18 | { 19 | public: 20 | 21 | // -------------------------------------------------------------------- 22 | // PUBLIC TYPE ALIASES 23 | // -------------------------------------------------------------------- 24 | 25 | using char_type = CharT; 26 | using size_type = std::ptrdiff_t; 27 | using reference = char_type&; 28 | using const_reference = const char_type&; 29 | using pointer = char_type*; 30 | using const_pointer = const char_type*; 31 | using iterator = pointer; 32 | using const_iterator = const_pointer; 33 | 34 | // -------------------------------------------------------------------- 35 | // TEMPLATE PARAMETERS VALIDATION 36 | // -------------------------------------------------------------------- 37 | 38 | static_assert(std::is_trivial::value && std::is_standard_layout::value, 39 | "usf::BasicStringSpan: CharT must be a POD type (both trivial and standard-layout)."); 40 | 41 | // -------------------------------------------------------------------- 42 | // PUBLIC MEMBER FUNCTIONS 43 | // -------------------------------------------------------------------- 44 | 45 | // -------- CONSTRUCTORS ---------------------------------------------- 46 | 47 | USF_CPP14_CONSTEXPR BasicStringSpan() noexcept = delete; 48 | 49 | USF_CPP14_CONSTEXPR BasicStringSpan(const BasicStringSpan&) noexcept = default; 50 | USF_CPP14_CONSTEXPR BasicStringSpan(BasicStringSpan&&) noexcept = default; 51 | #if 0 52 | template // N includes the null terminator! 53 | USF_CPP14_CONSTEXPR BasicStringSpan(char_type (&str)[N]) noexcept 54 | : m_begin{str}, m_end{str + N - 1} 55 | {} 56 | #endif 57 | USF_CPP14_CONSTEXPR BasicStringSpan(pointer str) noexcept 58 | : m_begin{str}, m_end{str + internal::CharTraits::length(str)} 59 | {} 60 | 61 | USF_CPP14_CONSTEXPR BasicStringSpan(pointer str, const size_type str_count) 62 | : m_begin{str}, m_end{str + str_count} 63 | { 64 | USF_ENFORCE(str_count >= 0, std::runtime_error); 65 | } 66 | 67 | USF_CPP14_CONSTEXPR BasicStringSpan(iterator first, iterator last) 68 | : m_begin{first}, m_end{last} 69 | { 70 | USF_ENFORCE(first <= last, std::runtime_error); 71 | } 72 | 73 | template USF_CPP14_CONSTEXPR 74 | BasicStringSpan(std::array& array) noexcept 75 | : m_begin{array.begin()}, m_end{array.end()} 76 | {} 77 | 78 | // -------- ASSIGNMENT ------------------------------------------------ 79 | 80 | USF_CPP14_CONSTEXPR BasicStringSpan& operator = (const BasicStringSpan&) noexcept = default; 81 | USF_CPP14_CONSTEXPR BasicStringSpan& operator = (BasicStringSpan&&) noexcept = default; 82 | 83 | // -------- ELEMENT ACCESS -------------------------------------------- 84 | 85 | // Returns a reference to the character at specified location `pos`. 86 | // Bounds checking is performed. 87 | inline USF_CPP14_CONSTEXPR const_reference at(const size_type pos) const 88 | { 89 | USF_ENFORCE(pos >= 0 && pos < size(), std::out_of_range); 90 | return m_begin[pos]; 91 | } 92 | 93 | inline USF_CPP14_CONSTEXPR reference at(const size_type pos) 94 | { 95 | USF_ENFORCE(pos >= 0 && pos < size(), std::out_of_range); 96 | return m_begin[pos]; 97 | } 98 | 99 | // Returns a reference to the character at specified location `pos`. 100 | // No bounds checking is performed. 101 | inline USF_CPP14_CONSTEXPR const_reference operator [] (const size_type pos) const noexcept { return m_begin[pos]; } 102 | inline USF_CPP14_CONSTEXPR reference operator [] (const size_type pos) noexcept { return m_begin[pos]; } 103 | 104 | // Returns reference to the first character of the sequence. 105 | inline USF_CPP14_CONSTEXPR const_reference front() const noexcept { assert(!empty()); return m_begin[0]; } 106 | inline USF_CPP14_CONSTEXPR reference front() noexcept { assert(!empty()); return m_begin[0]; } 107 | 108 | // Returns reference to the last character of the sequence. 109 | inline USF_CPP14_CONSTEXPR const_reference back() const noexcept { assert(!empty()); return *(m_end - 1); } 110 | inline USF_CPP14_CONSTEXPR reference back() noexcept { assert(!empty()); return *(m_end - 1); } 111 | 112 | // Returns a pointer to the beginning of the sequence. 113 | inline USF_CPP14_CONSTEXPR const_pointer data() const noexcept { return m_begin; } 114 | inline USF_CPP14_CONSTEXPR pointer data() noexcept { return m_begin; } 115 | 116 | #if defined(USF_STD_BASIC_STRING_VIEW) 117 | // Conversion to std::basic_string view. 118 | template > 119 | inline USF_CPP14_CONSTEXPR operator USF_STD_BASIC_STRING_VIEW() const noexcept 120 | { 121 | return USF_STD_BASIC_STRING_VIEW{data(), static_cast(size())}; 122 | } 123 | #endif 124 | // -------- ITERATORS ------------------------------------------------- 125 | 126 | // Returns an iterator to the first character of the sequence. 127 | inline USF_CPP14_CONSTEXPR const_iterator cbegin() const noexcept { return m_begin; } 128 | inline USF_CPP14_CONSTEXPR const_iterator begin() const noexcept { return m_begin; } 129 | inline USF_CPP14_CONSTEXPR iterator begin() noexcept { return m_begin; } 130 | 131 | // Returns an iterator to the character following the last character of the sequence. 132 | // This character acts as a placeholder, attempting to access it results in undefined behavior. 133 | inline USF_CPP14_CONSTEXPR const_iterator cend() const noexcept { return m_end; } 134 | inline USF_CPP14_CONSTEXPR const_iterator end() const noexcept { return m_end; } 135 | inline USF_CPP14_CONSTEXPR iterator end() noexcept { return m_end; } 136 | 137 | // -------- CAPACITY -------------------------------------------------- 138 | 139 | // Checks if the sequence has no characters, i.e. whether begin() == end(). 140 | inline USF_CPP14_CONSTEXPR bool empty() const noexcept { return (size() == 0); } 141 | 142 | // Returns the number of characters in the sequence, i.e. the distance between begin() and end(). 143 | inline USF_CPP14_CONSTEXPR size_type size() const noexcept { return static_cast(m_end - m_begin); } 144 | inline USF_CPP14_CONSTEXPR size_type length() const noexcept { return static_cast(m_end - m_begin); } 145 | 146 | // -------- MODIFIERS ------------------------------------------------- 147 | 148 | // Moves the start of the sequence forward by `n` characters. 149 | // The behavior is undefined if n > size(). 150 | inline USF_CPP14_CONSTEXPR void remove_prefix(const size_type n) noexcept 151 | { 152 | m_begin += n; 153 | } 154 | 155 | // Moves the end of the sequence back by `n` characters. 156 | // The behavior is undefined if n > size(). 157 | inline USF_CPP14_CONSTEXPR void remove_suffix(const size_type n) noexcept 158 | { 159 | m_end -= n; 160 | } 161 | 162 | private: 163 | 164 | // -------------------------------------------------------------------- 165 | // PRIVATE MEMBER VARIABLES 166 | // -------------------------------------------------------------------- 167 | 168 | iterator m_begin{nullptr}; 169 | iterator m_end {nullptr}; 170 | }; 171 | 172 | using StringSpan = BasicStringSpan; 173 | using WStringSpan = BasicStringSpan; 174 | 175 | #if defined(USF_CPP20_CHAR8_T_SUPPORT) 176 | using U8StringSpan = BasicStringSpan; 177 | #endif 178 | using U16StringSpan = BasicStringSpan; 179 | using U32StringSpan = BasicStringSpan; 180 | 181 | using ByteStringSpan = BasicStringSpan; 182 | 183 | } // namespace usf 184 | 185 | #endif // USF_STRING_SPAN_HPP 186 | -------------------------------------------------------------------------------- /include/usf/develop/usf_string_view.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_string_view.hpp 3 | // @brief String view class (minimal implementation). 4 | // Intended to replace basic functionality of std::string_view 5 | // throughout the library. It uses a begin / end iterator approach 6 | // instead of the standard data pointer and size implementations. 7 | // @date 14 January 2019 8 | // ---------------------------------------------------------------------------- 9 | 10 | #ifndef USF_STRING_VIEW_HPP 11 | #define USF_STRING_VIEW_HPP 12 | 13 | namespace usf 14 | { 15 | 16 | template 17 | class BasicStringView 18 | { 19 | public: 20 | 21 | // -------------------------------------------------------------------- 22 | // PUBLIC TYPE ALIASES 23 | // -------------------------------------------------------------------- 24 | 25 | using char_type = CharT; 26 | using size_type = std::ptrdiff_t; // Not std::size_t 27 | using reference = char_type&; 28 | using const_reference = const char_type&; 29 | using pointer = char_type*; 30 | using const_pointer = const char_type*; 31 | using iterator = pointer; 32 | using const_iterator = const_pointer; 33 | 34 | // -------------------------------------------------------------------- 35 | // TEMPLATE PARAMETERS VALIDATION 36 | // -------------------------------------------------------------------- 37 | 38 | static_assert(std::is_trivial::value && std::is_standard_layout::value, 39 | "usf::BasicStringView: CharT must be a POD type (both trivial and standard-layout)."); 40 | 41 | // -------------------------------------------------------------------- 42 | // PUBLIC MEMBER FUNCTIONS 43 | // -------------------------------------------------------------------- 44 | 45 | // -------- CONSTRUCTORS ---------------------------------------------- 46 | 47 | USF_CPP14_CONSTEXPR BasicStringView() noexcept = delete; 48 | 49 | USF_CPP14_CONSTEXPR BasicStringView(const BasicStringView&) noexcept = default; 50 | USF_CPP14_CONSTEXPR BasicStringView(BasicStringView&&) noexcept = default; 51 | #if 0 52 | template // N includes the null terminator! 53 | USF_CPP14_CONSTEXPR BasicStringView(const char_type (&str)[N]) noexcept 54 | : m_begin{str}, m_end{str + N - 1} 55 | {} 56 | #endif 57 | USF_CPP14_CONSTEXPR BasicStringView(const_pointer str) noexcept 58 | : m_begin{str}, m_end{str + internal::CharTraits::length(str)} 59 | {} 60 | 61 | USF_CPP14_CONSTEXPR BasicStringView(const_pointer str, const size_type str_count) 62 | : m_begin{str}, m_end{str + str_count} 63 | { 64 | USF_ENFORCE(str_count >= 0, std::runtime_error); 65 | } 66 | 67 | USF_CPP14_CONSTEXPR BasicStringView(const_iterator first, const_iterator last) 68 | : m_begin{first}, m_end{last} 69 | { 70 | USF_ENFORCE(first <= last, std::runtime_error); 71 | } 72 | 73 | #if defined(USF_STD_BASIC_STRING_VIEW) 74 | template > 75 | USF_CPP14_CONSTEXPR BasicStringView(const USF_STD_BASIC_STRING_VIEW view) 76 | : m_begin{view.cbegin()}, m_end{view.cend()} 77 | {} 78 | #endif 79 | // -------- ASSIGNMENT ------------------------------------------------ 80 | 81 | USF_CPP14_CONSTEXPR BasicStringView& operator = (const BasicStringView&) noexcept = default; 82 | USF_CPP14_CONSTEXPR BasicStringView& operator = (BasicStringView&&) noexcept = default; 83 | 84 | // -------- ELEMENT ACCESS -------------------------------------------- 85 | 86 | // Returns a reference to the character at specified location `pos`. 87 | // Bounds checking is performed. 88 | inline USF_CPP14_CONSTEXPR const_reference at(const size_type pos) const 89 | { 90 | USF_ENFORCE(pos >= 0 && pos < size(), std::out_of_range); 91 | return m_begin[pos]; 92 | } 93 | 94 | // Returns a reference to the character at specified location `pos`. 95 | // No bounds checking is performed. 96 | inline USF_CPP14_CONSTEXPR const_reference operator [] (const size_type pos) const noexcept { return m_begin[pos]; } 97 | 98 | // Returns reference to the first character of the sequence. 99 | inline USF_CPP14_CONSTEXPR const_reference front() const noexcept { assert(!empty()); return m_begin[0]; } 100 | 101 | // Returns reference to the last character of the sequence. 102 | inline USF_CPP14_CONSTEXPR const_reference back() const noexcept { assert(!empty()); return *(m_end - 1); } 103 | 104 | // Returns a pointer to the beginning of the sequence. 105 | inline USF_CPP14_CONSTEXPR const_pointer data() const noexcept { return m_begin; } 106 | 107 | #if defined(USF_STD_BASIC_STRING_VIEW) 108 | // Conversion to std::basic_string view. 109 | template > 110 | inline USF_CPP14_CONSTEXPR operator USF_STD_BASIC_STRING_VIEW() const noexcept 111 | { 112 | return USF_STD_BASIC_STRING_VIEW{data(), static_cast(size())}; 113 | } 114 | #endif 115 | // -------- ITERATORS ------------------------------------------------- 116 | 117 | // Returns an iterator to the first character of the sequence. 118 | inline USF_CPP14_CONSTEXPR const_iterator cbegin() const noexcept { return m_begin; } 119 | inline USF_CPP14_CONSTEXPR const_iterator begin() const noexcept { return m_begin; } 120 | 121 | // Returns an iterator to the character following the last character of the sequence. 122 | // This character acts as a placeholder, attempting to access it results in undefined behavior. 123 | inline USF_CPP14_CONSTEXPR const_iterator cend() const noexcept { return m_end; } 124 | inline USF_CPP14_CONSTEXPR const_iterator end() const noexcept { return m_end; } 125 | 126 | // -------- CAPACITY -------------------------------------------------- 127 | 128 | // Checks if the sequence has no characters, i.e. whether begin() == end(). 129 | inline USF_CPP14_CONSTEXPR bool empty() const noexcept { return (size() == 0); } 130 | 131 | // Returns the number of characters in the sequence, i.e. the distance between begin() and end(). 132 | inline USF_CPP14_CONSTEXPR size_type size() const noexcept { return static_cast(m_end - m_begin); } 133 | inline USF_CPP14_CONSTEXPR size_type length() const noexcept { return static_cast(m_end - m_begin); } 134 | 135 | // -------- MODIFIERS ------------------------------------------------- 136 | 137 | // Moves the start of the sequence forward by `n` characters. 138 | // The behavior is undefined if n > size(). 139 | inline USF_CPP14_CONSTEXPR void remove_prefix(const size_type n) noexcept 140 | { 141 | m_begin += n; 142 | } 143 | 144 | // Moves the end of the sequence back by `n` characters. 145 | // The behavior is undefined if n > size(). 146 | inline USF_CPP14_CONSTEXPR void remove_suffix(const size_type n) noexcept 147 | { 148 | m_end -= n; 149 | } 150 | 151 | private: 152 | 153 | // -------------------------------------------------------------------- 154 | // PRIVATE MEMBER VARIABLES 155 | // -------------------------------------------------------------------- 156 | 157 | const_iterator m_begin{nullptr}; 158 | const_iterator m_end {nullptr}; 159 | }; 160 | 161 | using StringView = BasicStringView; 162 | using WStringView = BasicStringView; 163 | 164 | #if defined(USF_CPP20_CHAR8_T_SUPPORT) 165 | using U8StringView = BasicStringView; 166 | #endif 167 | using U16StringView = BasicStringView; 168 | using U32StringView = BasicStringView; 169 | 170 | using ByteStringView = BasicStringView; 171 | 172 | } // namespace usf 173 | 174 | #endif // USF_STRING_VIEW_HPP 175 | -------------------------------------------------------------------------------- /include/usf/develop/usf_traits.hpp: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // @file usf_traits.hpp 3 | // @brief Traits classes. 4 | // NB: usf::CharTraits class has the same purpose of the 5 | // std::char_traits class but is not compatible and cannot be 6 | // interchanged. Different interface and different implementation. 7 | // Intended for internal use only! 8 | // @date 14 January 2019 9 | // ---------------------------------------------------------------------------- 10 | 11 | #ifndef USF_TRAITS_HPP 12 | #define USF_TRAITS_HPP 13 | 14 | namespace usf 15 | { 16 | namespace internal 17 | { 18 | 19 | // ---------------------------------------------------------------------------- 20 | // Custom char traits 21 | // ---------------------------------------------------------------------------- 22 | 23 | class CharTraits 24 | { 25 | public: 26 | 27 | // -------------------------------------------------------------------- 28 | // PUBLIC STATIC FUNCTIONS 29 | // -------------------------------------------------------------------- 30 | 31 | template ::value, bool>::type = true> 33 | USF_ALWAYS_INLINE static USF_CPP14_CONSTEXPR 34 | void assign(CharDst*& dst, CharSrc ch, std::ptrdiff_t count) noexcept 35 | { 36 | while((count--) > 0) { *dst++ = static_cast(ch); } 37 | } 38 | 39 | template ::value, bool>::type = true> 41 | USF_ALWAYS_INLINE static USF_CPP14_CONSTEXPR 42 | void copy(CharDst*& dst, const CharSrc* src, std::ptrdiff_t count) noexcept 43 | { 44 | while((count--) > 0) { *dst++ = static_cast(*src++); } 45 | } 46 | 47 | template USF_ALWAYS_INLINE static USF_CPP14_CONSTEXPR 48 | std::ptrdiff_t length(const CharT* str) noexcept 49 | { 50 | const CharT* str_begin = str; 51 | 52 | while(*str != CharT{}) { ++str; } 53 | 54 | return str - str_begin; 55 | } 56 | }; 57 | 58 | 59 | // ---------------------------------------------------------------------------- 60 | // Custom type traits 61 | // ---------------------------------------------------------------------------- 62 | 63 | template 64 | struct always_false : std::false_type {}; 65 | 66 | } // namespace internal 67 | } // namespace usf 68 | 69 | #endif // USF_TRAITS_HPP 70 | -------------------------------------------------------------------------------- /unit_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(unit_tests VERSION 1.0.0) 3 | 4 | macro(add_compiler_flags) 5 | foreach(flag ${ARGV}) 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") 7 | endforeach() 8 | endmacro() 9 | 10 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 11 | add_compiler_flags(-std=c++17) 12 | 13 | add_compiler_flags(-fstrict-aliasing) 14 | add_compiler_flags(-fvisibility=hidden) 15 | add_compiler_flags(-pedantic) 16 | add_compiler_flags(-pedantic-errors) 17 | add_compiler_flags(-Werror) 18 | endif() 19 | 20 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") 21 | add_compiler_flags(-Wall) 22 | add_compiler_flags(-Warray-bounds) 23 | add_compiler_flags(-Wcast-align) 24 | add_compiler_flags(-Wcast-qual) 25 | add_compiler_flags(-Wconversion) 26 | add_compiler_flags(-Wctor-dtor-privacy) 27 | add_compiler_flags(-Wdisabled-optimization) 28 | add_compiler_flags(-Weffc++) 29 | add_compiler_flags(-Wextra) 30 | add_compiler_flags(-fdiagnostics-show-option) 31 | add_compiler_flags(-Wfloat-equal) 32 | add_compiler_flags(-Wformat=2) 33 | ## add_compiler_flags(-Winline) 34 | add_compiler_flags(-Wlogical-op) 35 | add_compiler_flags(-Wmissing-declarations) 36 | add_compiler_flags(-Wmissing-include-dirs) 37 | add_compiler_flags(-Wnoexcept) 38 | add_compiler_flags(-Wnon-virtual-dtor) 39 | add_compiler_flags(-Wold-style-cast) 40 | add_compiler_flags(-Woverloaded-virtual) 41 | add_compiler_flags(-Wpointer-arith) 42 | add_compiler_flags(-Wredundant-decls) 43 | add_compiler_flags(-Wshadow) 44 | add_compiler_flags(-Wsign-conversion) 45 | ## add_compiler_flags(-Wstrict-overflow=5) 46 | ## add_compiler_flags(-Wswitch-default) 47 | add_compiler_flags(-Wswitch-enum) 48 | add_compiler_flags(-Wwrite-strings) 49 | add_compiler_flags(-Wundef) 50 | add_compiler_flags(-Wunreachable-code) 51 | add_compiler_flags(-Wunused) 52 | 53 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) 54 | add_compiler_flags(-Wdouble-promotion) 55 | add_compiler_flags(-Wtrampolines) 56 | add_compiler_flags(-Wuseless-cast) 57 | add_compiler_flags(-Wvector-operation-performance) 58 | add_compiler_flags(-Wzero-as-null-pointer-constant) 59 | endif() 60 | 61 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) 62 | add_compiler_flags(-Wduplicated-cond) 63 | add_compiler_flags(-Wnull-dereference) 64 | add_compiler_flags(-Wshift-overflow=2) 65 | endif() 66 | 67 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) 68 | add_compiler_flags(-Walloc-zero) 69 | add_compiler_flags(-Walloca) 70 | add_compiler_flags(-Wduplicated-branches) 71 | endif() 72 | 73 | if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) 74 | add_compiler_flags(-Wcast-align=strict) 75 | endif() 76 | endif() 77 | 78 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 79 | add_compiler_flags(-Weverything) 80 | add_compiler_flags(-Wno-c++98-compat) 81 | add_compiler_flags(-Wno-c++98-compat-pedantic) 82 | add_compiler_flags(-Wno-padded) 83 | add_compiler_flags(-Wno-unused-macros) 84 | endif() 85 | 86 | if(MSVC) 87 | add_compiler_flags(/std:c++latest) 88 | add_compiler_flags(/permissive-) 89 | add_compiler_flags(/Wall) 90 | add_compiler_flags(/WX) 91 | endif() 92 | 93 | set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG" CACHE STRING "" FORCE) 94 | set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG" CACHE STRING "" FORCE) 95 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-Og -g -DDEBUG" CACHE STRING "" FORCE) 96 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG" CACHE STRING "" FORCE) 97 | 98 | include_directories("../include") ## usflib include directory 99 | include_directories("include") ## unit_tests include directory 100 | 101 | file(GLOB_RECURSE user_source "source/*.cpp" "../include/*.hpp" "include/*.hpp") 102 | 103 | enable_testing() 104 | add_executable(unit_tests ${user_source}) 105 | add_test(NAME unit_tests COMMAND unit_tests) 106 | -------------------------------------------------------------------------------- /unit_tests/include/unit_tests_config.hpp: -------------------------------------------------------------------------------- 1 | 2 | #define DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING 3 | #include "doctest.h" 4 | 5 | 6 | #define USF_THROW_ON_CONTRACT_VIOLATION 7 | //#define USF_DISABLE_FLOAT_SUPPORT 8 | //#define USF_SINGLE_HEADER 9 | 10 | #ifdef USF_SINGLE_HEADER 11 | #include "usf/usf.hpp" 12 | #else 13 | #include "usf/develop/usf_config.hpp" 14 | #include "usf/develop/usf_traits.hpp" 15 | #include "usf/develop/usf_string_span.hpp" 16 | #include "usf/develop/usf_string_view.hpp" 17 | #include "usf/develop/usf_integer.hpp" 18 | #include "usf/develop/usf_float.hpp" 19 | #include "usf/develop/usf_arg_format.hpp" 20 | #include "usf/develop/usf_arg_custom_type.hpp" 21 | #include "usf/develop/usf_argument.hpp" 22 | #include "usf/develop/usf_main.hpp" 23 | #endif 24 | 25 | #if USF_CPP14_OR_GREATER 26 | # define TEST_CONSTEXPR constexpr 27 | # define TEST_STATIC_ASSERT(cond) static_assert((cond), "") 28 | #else 29 | # define TEST_CONSTEXPR 30 | # define TEST_STATIC_ASSERT(cond) assert(cond) 31 | #endif 32 | 33 | #define USF_TEST_BASIC_TYPES 34 | #define USF_TEST_FORMAT_SPEC 35 | #define USF_TEST_POSITIONAL_ARGS 36 | #define USF_TEST_CUSTOM_TYPES 37 | //#define USF_TEST_FLOATING_POINT 38 | #define USF_TEST_BENCHMARKS 39 | #define USF_TEST_BENCHMARK_PRINTF 40 | //#define USF_TEST_BENCHMARK_FMT 41 | 42 | #include 43 | #include 44 | -------------------------------------------------------------------------------- /unit_tests/source/unit_tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 3 | #include "unit_tests_config.hpp" 4 | -------------------------------------------------------------------------------- /unit_tests/source/unit_tests_basic_types.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "unit_tests_config.hpp" 3 | 4 | #if defined(USF_TEST_BASIC_TYPES) 5 | 6 | // ---------------------------------------------------------------------------- 7 | // BASIC TYPES 8 | // ---------------------------------------------------------------------------- 9 | TEST_CASE("usf::format_to, basic types") 10 | { 11 | char str[128]{}; 12 | 13 | // Boolean type 14 | { 15 | usf::format_to(str, 128, "{}", false); CHECK_EQ(str, "false"); 16 | usf::format_to(str, 128, "{}", true ); CHECK_EQ(str, "true"); 17 | 18 | usf::format_to(str, 128, "{:d}", false); CHECK_EQ(str, "0"); 19 | usf::format_to(str, 128, "{:d}", true ); CHECK_EQ(str, "1"); 20 | 21 | usf::format_to(str, 128, "{:x}", false); CHECK_EQ(str, "0"); 22 | usf::format_to(str, 128, "{:x}", true ); CHECK_EQ(str, "1"); 23 | 24 | usf::format_to(str, 128, "{:X}", false); CHECK_EQ(str, "0"); 25 | usf::format_to(str, 128, "{:X}", true ); CHECK_EQ(str, "1"); 26 | 27 | usf::format_to(str, 128, "{:o}", false); CHECK_EQ(str, "0"); 28 | usf::format_to(str, 128, "{:o}", true ); CHECK_EQ(str, "1"); 29 | 30 | usf::format_to(str, 128, "{:b}", false); CHECK_EQ(str, "0"); 31 | usf::format_to(str, 128, "{:b}", true ); CHECK_EQ(str, "1"); 32 | 33 | usf::format_to(str, 128, "{:B}", false); CHECK_EQ(str, "0"); 34 | usf::format_to(str, 128, "{:B}", true ); CHECK_EQ(str, "1"); 35 | 36 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", true), std::runtime_error); 37 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", true), std::runtime_error); 38 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", true), std::runtime_error); 39 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", true), std::runtime_error); 40 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", true), std::runtime_error); 41 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", true), std::runtime_error); 42 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", true), std::runtime_error); 43 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", true), std::runtime_error); 44 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", true), std::runtime_error); 45 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", true), std::runtime_error); 46 | } 47 | 48 | // Character (char) type 49 | { 50 | usf::format_to(str, 128, "{}", 'N'); CHECK_EQ(str, "N"); 51 | usf::format_to(str, 128, "{:c}", 'N'); CHECK_EQ(str, "N"); 52 | usf::format_to(str, 128, "{:d}", 'N'); CHECK_EQ(str, "78"); 53 | usf::format_to(str, 128, "{:x}", 'N'); CHECK_EQ(str, "4e"); 54 | usf::format_to(str, 128, "{:X}", 'N'); CHECK_EQ(str, "4E"); 55 | usf::format_to(str, 128, "{:o}", 'N'); CHECK_EQ(str, "116"); 56 | usf::format_to(str, 128, "{:b}", 'N'); CHECK_EQ(str, "1001110"); 57 | usf::format_to(str, 128, "{:B}", 'N'); CHECK_EQ(str, "1001110"); 58 | 59 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", 'N'), std::runtime_error); 60 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", 'N'), std::runtime_error); 61 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", 'N'), std::runtime_error); 62 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", 'N'), std::runtime_error); 63 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", 'N'), std::runtime_error); 64 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", 'N'), std::runtime_error); 65 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", 'N'), std::runtime_error); 66 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", 'N'), std::runtime_error); 67 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", 'N'), std::runtime_error); 68 | } 69 | 70 | // Integer (int) type 71 | { 72 | int value = 123; 73 | 74 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 75 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 76 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 77 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 78 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 79 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 80 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 81 | 82 | value = -123; 83 | 84 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "-123"); 85 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "-123"); 86 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "-7b"); 87 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "-7B"); 88 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "-173"); 89 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "-1111011"); 90 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "-1111011"); 91 | 92 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 93 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 94 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 95 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 96 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 97 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 98 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 99 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 100 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 101 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 102 | } 103 | 104 | // Integer (unsigned int) type 105 | { 106 | unsigned int value = 123; 107 | 108 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 109 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 110 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 111 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 112 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 113 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 114 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 115 | 116 | value = static_cast(-123); 117 | 118 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "4294967173"); 119 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "4294967173"); 120 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "ffffff85"); 121 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "FFFFFF85"); 122 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "37777777605"); 123 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "11111111111111111111111110000101"); 124 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "11111111111111111111111110000101"); 125 | 126 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 127 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 128 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 129 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 130 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 131 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 132 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 133 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 134 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 135 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 136 | } 137 | 138 | // Integer (int8_t) type 139 | { 140 | int8_t value = 123; 141 | 142 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 143 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 144 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 145 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 146 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 147 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 148 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 149 | 150 | value = -123; 151 | 152 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "-123"); 153 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "-123"); 154 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "-7b"); 155 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "-7B"); 156 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "-173"); 157 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "-1111011"); 158 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "-1111011"); 159 | 160 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 161 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 162 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 163 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 164 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 165 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 166 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 167 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 168 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 169 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 170 | } 171 | 172 | // Integer (uint8_t) type 173 | { 174 | uint8_t value = 123; 175 | 176 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 177 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 178 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 179 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 180 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 181 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 182 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 183 | 184 | value = static_cast(-123); 185 | 186 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "133"); 187 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "133"); 188 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "85"); 189 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "85"); 190 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "205"); 191 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "10000101"); 192 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "10000101"); 193 | 194 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 195 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 196 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 197 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 198 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 199 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 200 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 201 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 202 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 203 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 204 | } 205 | 206 | // Integer (int16_t) type 207 | { 208 | int16_t value = 123; 209 | 210 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 211 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 212 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 213 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 214 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 215 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 216 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 217 | 218 | value = -123; 219 | 220 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "-123"); 221 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "-123"); 222 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "-7b"); 223 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "-7B"); 224 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "-173"); 225 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "-1111011"); 226 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "-1111011"); 227 | 228 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 229 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 230 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 231 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 232 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 233 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 234 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 235 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 236 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 237 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 238 | } 239 | 240 | // Integer (uint16_t) type 241 | { 242 | uint16_t value = 123; 243 | 244 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 245 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 246 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 247 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 248 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 249 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 250 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 251 | 252 | value = static_cast(-123); 253 | 254 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "65413"); 255 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "65413"); 256 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "ff85"); 257 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "FF85"); 258 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "177605"); 259 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111111110000101"); 260 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111111110000101"); 261 | 262 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 263 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 264 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 265 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 266 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 267 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 268 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 269 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 270 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 271 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 272 | } 273 | 274 | // Integer (int32_t) type 275 | { 276 | int32_t value = 123; 277 | 278 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 279 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 280 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 281 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 282 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 283 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 284 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 285 | 286 | value = -123; 287 | 288 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "-123"); 289 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "-123"); 290 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "-7b"); 291 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "-7B"); 292 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "-173"); 293 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "-1111011"); 294 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "-1111011"); 295 | 296 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 297 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 298 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 299 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 300 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 301 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 302 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 303 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 304 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 305 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 306 | } 307 | 308 | // Integer (uint32_t) type 309 | { 310 | uint32_t value = 123; 311 | 312 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 313 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 314 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 315 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 316 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 317 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 318 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 319 | 320 | value = static_cast(-123); 321 | 322 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "4294967173"); 323 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "4294967173"); 324 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "ffffff85"); 325 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "FFFFFF85"); 326 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "37777777605"); 327 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "11111111111111111111111110000101"); 328 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "11111111111111111111111110000101"); 329 | 330 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 331 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 332 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 333 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 334 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 335 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 336 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 337 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 338 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 339 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 340 | } 341 | 342 | // Integer (int64_t) type 343 | { 344 | int64_t value = 123; 345 | 346 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 347 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 348 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 349 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 350 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 351 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 352 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 353 | 354 | value = -123; 355 | 356 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "-123"); 357 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "-123"); 358 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "-7b"); 359 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "-7B"); 360 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "-173"); 361 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "-1111011"); 362 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "-1111011"); 363 | 364 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 365 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 366 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 367 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 368 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 369 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 370 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 371 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 372 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 373 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 374 | } 375 | 376 | // Integer (uint64_t) type 377 | { 378 | uint64_t value = 123; 379 | 380 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "123"); 381 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "123"); 382 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "7b"); 383 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "7B"); 384 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "173"); 385 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111011"); 386 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111011"); 387 | 388 | value = static_cast(-123); 389 | 390 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "18446744073709551493"); 391 | usf::format_to(str, 128, "{:d}", value); CHECK_EQ(str, "18446744073709551493"); 392 | usf::format_to(str, 128, "{:x}", value); CHECK_EQ(str, "ffffffffffffff85"); 393 | usf::format_to(str, 128, "{:X}", value); CHECK_EQ(str, "FFFFFFFFFFFFFF85"); 394 | usf::format_to(str, 128, "{:o}", value); CHECK_EQ(str, "1777777777777777777605"); 395 | usf::format_to(str, 128, "{:b}", value); CHECK_EQ(str, "1111111111111111111111111111111111111111111111111111111110000101"); 396 | usf::format_to(str, 128, "{:B}", value); CHECK_EQ(str, "1111111111111111111111111111111111111111111111111111111110000101"); 397 | 398 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 399 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 400 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 401 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 402 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 403 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 404 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 405 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 406 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 407 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 408 | } 409 | 410 | // Floating point (float) type 411 | { 412 | float value = 123.456789f; 413 | 414 | char std_str[32]{}; 415 | 416 | usf::format_to(str, 128, "{}", value); sprintf(std_str, "%g", static_cast(value)); CHECK_EQ(str, std_str); 417 | usf::format_to(str, 128, "{:f}", value); sprintf(std_str, "%f", static_cast(value)); CHECK_EQ(str, std_str); 418 | usf::format_to(str, 128, "{:F}", value); sprintf(std_str, "%F", static_cast(value)); CHECK_EQ(str, std_str); 419 | usf::format_to(str, 128, "{:e}", value); sprintf(std_str, "%e", static_cast(value)); CHECK_EQ(str, std_str); 420 | usf::format_to(str, 128, "{:E}", value); sprintf(std_str, "%E", static_cast(value)); CHECK_EQ(str, std_str); 421 | usf::format_to(str, 128, "{:g}", value); sprintf(std_str, "%g", static_cast(value)); CHECK_EQ(str, std_str); 422 | usf::format_to(str, 128, "{:G}", value); sprintf(std_str, "%G", static_cast(value)); CHECK_EQ(str, std_str); 423 | 424 | value = -123.456789f; 425 | 426 | usf::format_to(str, 128, "{}", value); sprintf(std_str, "%g", static_cast(value)); CHECK_EQ(str, std_str); 427 | usf::format_to(str, 128, "{:f}", value); sprintf(std_str, "%f", static_cast(value)); CHECK_EQ(str, std_str); 428 | usf::format_to(str, 128, "{:F}", value); sprintf(std_str, "%F", static_cast(value)); CHECK_EQ(str, std_str); 429 | usf::format_to(str, 128, "{:e}", value); sprintf(std_str, "%e", static_cast(value)); CHECK_EQ(str, std_str); 430 | usf::format_to(str, 128, "{:E}", value); sprintf(std_str, "%E", static_cast(value)); CHECK_EQ(str, std_str); 431 | usf::format_to(str, 128, "{:g}", value); sprintf(std_str, "%g", static_cast(value)); CHECK_EQ(str, std_str); 432 | usf::format_to(str, 128, "{:G}", value); sprintf(std_str, "%G", static_cast(value)); CHECK_EQ(str, std_str); 433 | 434 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 435 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:d}", value), std::runtime_error); 436 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:x}", value), std::runtime_error); 437 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:X}", value), std::runtime_error); 438 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:o}", value), std::runtime_error); 439 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:b}", value), std::runtime_error); 440 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:B}", value), std::runtime_error); 441 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 442 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 443 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 444 | } 445 | 446 | // Floating point (double) type 447 | { 448 | double value = 123.456789; 449 | 450 | char std_str[32]{}; 451 | 452 | usf::format_to(str, 128, "{}", value); sprintf(std_str, "%g", value); CHECK_EQ(str, std_str); 453 | usf::format_to(str, 128, "{:f}", value); sprintf(std_str, "%f", value); CHECK_EQ(str, std_str); 454 | usf::format_to(str, 128, "{:F}", value); sprintf(std_str, "%F", value); CHECK_EQ(str, std_str); 455 | usf::format_to(str, 128, "{:e}", value); sprintf(std_str, "%e", value); CHECK_EQ(str, std_str); 456 | usf::format_to(str, 128, "{:E}", value); sprintf(std_str, "%E", value); CHECK_EQ(str, std_str); 457 | usf::format_to(str, 128, "{:g}", value); sprintf(std_str, "%g", value); CHECK_EQ(str, std_str); 458 | usf::format_to(str, 128, "{:G}", value); sprintf(std_str, "%G", value); CHECK_EQ(str, std_str); 459 | 460 | value = -123.456789; 461 | 462 | usf::format_to(str, 128, "{}", value); sprintf(std_str, "%g", value); CHECK_EQ(str, std_str); 463 | usf::format_to(str, 128, "{:f}", value); sprintf(std_str, "%f", value); CHECK_EQ(str, std_str); 464 | usf::format_to(str, 128, "{:F}", value); sprintf(std_str, "%F", value); CHECK_EQ(str, std_str); 465 | usf::format_to(str, 128, "{:e}", value); sprintf(std_str, "%e", value); CHECK_EQ(str, std_str); 466 | usf::format_to(str, 128, "{:E}", value); sprintf(std_str, "%E", value); CHECK_EQ(str, std_str); 467 | usf::format_to(str, 128, "{:g}", value); sprintf(std_str, "%g", value); CHECK_EQ(str, std_str); 468 | usf::format_to(str, 128, "{:G}", value); sprintf(std_str, "%G", value); CHECK_EQ(str, std_str); 469 | 470 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 471 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:d}", value), std::runtime_error); 472 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:x}", value), std::runtime_error); 473 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:X}", value), std::runtime_error); 474 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:o}", value), std::runtime_error); 475 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:b}", value), std::runtime_error); 476 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:B}", value), std::runtime_error); 477 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:p}", value), std::runtime_error); 478 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:P}", value), std::runtime_error); 479 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 480 | } 481 | 482 | // Pointer (void*) type 483 | { 484 | void* value = reinterpret_cast(1000); 485 | 486 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "3e8"); 487 | usf::format_to(str, 128, "{:p}", value); CHECK_EQ(str, "3e8"); 488 | usf::format_to(str, 128, "{:P}", value); CHECK_EQ(str, "3E8"); 489 | 490 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 491 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:d}", value), std::runtime_error); 492 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:x}", value), std::runtime_error); 493 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:X}", value), std::runtime_error); 494 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:o}", value), std::runtime_error); 495 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:b}", value), std::runtime_error); 496 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:B}", value), std::runtime_error); 497 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 498 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 499 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 500 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 501 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 502 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 503 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 504 | } 505 | 506 | // Constant pointer (const void*) type 507 | { 508 | const void* value = reinterpret_cast(1000); 509 | 510 | usf::format_to(str, 128, "{}", value); CHECK_EQ(str, "3e8"); 511 | usf::format_to(str, 128, "{:p}", value); CHECK_EQ(str, "3e8"); 512 | usf::format_to(str, 128, "{:P}", value); CHECK_EQ(str, "3E8"); 513 | 514 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", value), std::runtime_error); 515 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:d}", value), std::runtime_error); 516 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:x}", value), std::runtime_error); 517 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:X}", value), std::runtime_error); 518 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:o}", value), std::runtime_error); 519 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:b}", value), std::runtime_error); 520 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:B}", value), std::runtime_error); 521 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", value), std::runtime_error); 522 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", value), std::runtime_error); 523 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", value), std::runtime_error); 524 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", value), std::runtime_error); 525 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", value), std::runtime_error); 526 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", value), std::runtime_error); 527 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:s}", value), std::runtime_error); 528 | } 529 | 530 | // String (convertible to usf::StringView) type 531 | { 532 | char s1[]{"str"}; 533 | usf::format_to(str, 128, "{}", s1); CHECK_EQ(str, "str"); 534 | usf::format_to(str, 128, "{:s}", s1); CHECK_EQ(str, "str"); 535 | 536 | const char s2[]{"str"}; 537 | usf::format_to(str, 128, "{}", s2); CHECK_EQ(str, "str"); 538 | usf::format_to(str, 128, "{:s}", s2); CHECK_EQ(str, "str"); 539 | 540 | char* s3 = s1; 541 | usf::format_to(str, 128, "{}", s3); CHECK_EQ(str, "str"); 542 | usf::format_to(str, 128, "{:s}", s3); CHECK_EQ(str, "str"); 543 | 544 | const char* s4 = s2; 545 | usf::format_to(str, 128, "{}", s4); CHECK_EQ(str, "str"); 546 | usf::format_to(str, 128, "{:s}", s4); CHECK_EQ(str, "str"); 547 | 548 | usf::StringView sv(s1); 549 | usf::format_to(str, 128, "{}", sv); CHECK_EQ(str, "str"); 550 | usf::format_to(str, 128, "{:s}", sv); CHECK_EQ(str, "str"); 551 | 552 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:c}", sv), std::runtime_error); 553 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:d}", sv), std::runtime_error); 554 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:x}", sv), std::runtime_error); 555 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:X}", sv), std::runtime_error); 556 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:o}", sv), std::runtime_error); 557 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:b}", sv), std::runtime_error); 558 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:B}", sv), std::runtime_error); 559 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:f}", sv), std::runtime_error); 560 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:F}", sv), std::runtime_error); 561 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:e}", sv), std::runtime_error); 562 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:E}", sv), std::runtime_error); 563 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:g}", sv), std::runtime_error); 564 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:G}", sv), std::runtime_error); 565 | } 566 | } 567 | 568 | #endif // #if defined(USF_TEST_BASIC_TYPES) 569 | -------------------------------------------------------------------------------- /unit_tests/source/unit_tests_benchmarks.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "unit_tests_config.hpp" 3 | 4 | #if defined(USF_TEST_BENCHMARKS) 5 | 6 | #include 7 | 8 | #if defined(USF_TEST_BENCHMARK_FMT) 9 | #define FMT_HEADER_ONLY 10 | #define FMT_STATIC_THOUSANDS_SEPARATOR '\'' 11 | #include "fmt/format.h" 12 | #endif 13 | 14 | // ---------------------------------------------------------------------------- 15 | // BENCHMARK 16 | // ---------------------------------------------------------------------------- 17 | TEST_CASE("usf::format_to, benchmark") 18 | { 19 | auto ms_min_usf1 = std::numeric_limits::max(); 20 | auto ms_min_usf2 = std::numeric_limits::max(); 21 | auto ms_min_usf3 = std::numeric_limits::max(); 22 | 23 | #if defined(USF_TEST_BENCHMARK_PRINTF) 24 | auto ms_min_std1 = std::numeric_limits::max(); 25 | auto ms_min_std2 = std::numeric_limits::max(); 26 | auto ms_min_std3 = std::numeric_limits::max(); 27 | #endif 28 | 29 | #if defined(USF_TEST_BENCHMARK_FMT) 30 | auto ms_min_fmt1 = std::numeric_limits::max(); 31 | auto ms_min_fmt2 = std::numeric_limits::max(); 32 | auto ms_min_fmt3 = std::numeric_limits::max(); 33 | #endif 34 | 35 | const int max_runs = 10; 36 | const int max_iterations = 2000000; 37 | 38 | for(int r = 0; r < max_runs; ++r) 39 | { 40 | std::cout << "TESTRUN: " << r << '\n'; 41 | 42 | // uSF usf::format_to 43 | { 44 | char str[128]{}; 45 | auto start = std::chrono::steady_clock::now(); 46 | for(int i = 0; i < max_iterations; ++i) 47 | { 48 | usf::format_to(str, 128, "{:f}|{:08x}|{:e}|{}|{:016x}|{:c}|%|{{|}}", 49 | 1.234, 56789, -0.00393333, "str", 1000, 'X'); 50 | } 51 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 52 | std::cout << "usf:: " << str << " -> " << ms << "ms\n"; 53 | ms_min_usf1 = std::min(ms_min_usf1, ms); 54 | } 55 | #if defined(USF_TEST_BENCHMARK_PRINTF) 56 | // Standard sprintf 57 | { 58 | char str[128]{}; 59 | auto start = std::chrono::steady_clock::now(); 60 | for(int i = 0; i < max_iterations; ++i) 61 | { 62 | sprintf(str, "%f|%08x|%e|%s|%016x|%c|%%|{|}", 63 | 1.234, 56789, -0.00393333, "str", 1000, 'X'); 64 | } 65 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 66 | std::cout << "std:: " << str << " -> " << ms << "ms\n"; 67 | ms_min_std1 = std::min(ms_min_std1, ms); 68 | } 69 | #endif 70 | #if defined(USF_TEST_BENCHMARK_FMT) 71 | // {fmt} fmt::format_to 72 | { 73 | char str[128]{}; 74 | auto start = std::chrono::steady_clock::now(); 75 | for(int i = 0; i < max_iterations; ++i) 76 | { 77 | auto res = fmt::format_to(str, "{:f}|{:08x}|{:e}|{}|{:016x}|{:c}|%|{{|}}", 78 | 1.234, 56789, -0.00393333, "str", 1000, 'X'); 79 | *res = '\0'; 80 | } 81 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 82 | std::cout << "fmt:: " << str << " -> " << ms << "ms\n"; 83 | ms_min_fmt1 = std::min(ms_min_fmt1, ms); 84 | } 85 | #endif 86 | 87 | // uSF usf::format_to 88 | { 89 | char str[128]{}; 90 | auto start = std::chrono::steady_clock::now(); 91 | for(int i = 0; i < max_iterations; ++i) 92 | { 93 | usf::format_to(str, 128, "{:d}|{:x}|{:o}", __UINT32_MAX__, __UINT32_MAX__, __UINT32_MAX__); 94 | } 95 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 96 | std::cout << "usf:: " << str << " -> " << ms << "ms\n"; 97 | ms_min_usf2 = std::min(ms_min_usf2, ms); 98 | } 99 | #if defined(USF_TEST_BENCHMARK_PRINTF) 100 | // Standard sprintf 101 | { 102 | char str[128]{}; 103 | auto start = std::chrono::steady_clock::now(); 104 | for(int i = 0; i < max_iterations; ++i) 105 | { 106 | sprintf(str, "%u|%x|%o", __UINT32_MAX__, __UINT32_MAX__, __UINT32_MAX__); 107 | } 108 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 109 | std::cout << "std:: " << str << " -> " << ms << "ms\n"; 110 | ms_min_std2 = std::min(ms_min_std2, ms); 111 | } 112 | #endif 113 | #if defined(USF_TEST_BENCHMARK_FMT) 114 | // {fmt} fmt::format_to 115 | { 116 | char str[128]{}; 117 | auto start = std::chrono::steady_clock::now(); 118 | for(int i = 0; i < max_iterations; ++i) 119 | { 120 | auto res = fmt::format_to(str, "{:d}|{:x}|{:o}", __UINT32_MAX__, __UINT32_MAX__, __UINT32_MAX__); 121 | *res = '\0'; 122 | } 123 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 124 | std::cout << "fmt:: " << str << " -> " << ms << "ms\n"; 125 | ms_min_fmt2 = std::min(ms_min_fmt2, ms); 126 | } 127 | #endif 128 | 129 | // uSF usf::format_to 130 | { 131 | char str[128]{}; 132 | auto start = std::chrono::steady_clock::now(); 133 | for(int i = 0; i < max_iterations; ++i) 134 | { 135 | usf::format_to(str, 128, "{:d}|{:x}|{:o}", __UINT64_MAX__, __UINT64_MAX__, __UINT64_MAX__); 136 | } 137 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 138 | std::cout << "usf:: " << str << " -> " << ms << "ms\n"; 139 | ms_min_usf3 = std::min(ms_min_usf3, ms); 140 | } 141 | #if defined(USF_TEST_BENCHMARK_PRINTF) 142 | // Standard sprintf 143 | { 144 | char str[128]{}; 145 | auto start = std::chrono::steady_clock::now(); 146 | for(int i = 0; i < max_iterations; ++i) 147 | { 148 | #if (__LONG_MAX__ != __LONG_LONG_MAX__) 149 | sprintf(str, "%llu|%llx|%llo", __UINT64_MAX__, __UINT64_MAX__, __UINT64_MAX__); 150 | #else 151 | sprintf(str, "%lu|%lx|%lo", __UINT64_MAX__, __UINT64_MAX__, __UINT64_MAX__); 152 | #endif 153 | } 154 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 155 | std::cout << "std:: " << str << " -> " << ms << "ms\n"; 156 | ms_min_std3 = std::min(ms_min_std3, ms); 157 | } 158 | #endif 159 | #if defined(USF_TEST_BENCHMARK_FMT) 160 | // {fmt} fmt::format_to 161 | { 162 | char str[128]{}; 163 | auto start = std::chrono::steady_clock::now(); 164 | for(int i = 0; i < max_iterations; ++i) 165 | { 166 | auto res = fmt::format_to(str, "{:d}|{:x}|{:o}", __UINT64_MAX__, __UINT64_MAX__, __UINT64_MAX__); 167 | *res = '\0'; 168 | } 169 | auto ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); 170 | std::cout << "fmt:: " << str << " -> " << ms << "ms\n"; 171 | ms_min_fmt3 = std::min(ms_min_fmt3, ms); 172 | } 173 | #endif 174 | } 175 | 176 | std::cout << "BEST RESULTS:\n"; 177 | std::cout << "USF: " << ms_min_usf1 << "ms / " << ms_min_usf2 << "ms / " << ms_min_usf3 << "ms\n"; 178 | #if defined(USF_TEST_BENCHMARK_PRINTF) 179 | std::cout << "STD: " << ms_min_std1 << "ms / " << ms_min_std2 << "ms / " << ms_min_std3 << "ms\n"; 180 | #endif 181 | #if defined(USF_TEST_BENCHMARK_FMT) 182 | std::cout << "FMT: " << ms_min_fmt1 << "ms / " << ms_min_fmt2 << "ms / " << ms_min_fmt3 << "ms\n"; 183 | #endif 184 | } 185 | 186 | #endif //defined(USF_TEST_BENCHMARKS) 187 | -------------------------------------------------------------------------------- /unit_tests/source/unit_tests_custom_types.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "unit_tests_config.hpp" 3 | 4 | #if defined(USF_TEST_CUSTOM_TYPES) 5 | 6 | // ---------------------------------------------------------------------------- 7 | // USER DEFINED CUSTOM TYPES DEMO 1 - DATE / TIME 8 | // ---------------------------------------------------------------------------- 9 | enum class DateTime 10 | { 11 | kCurrentDate, 12 | kCurrentTime, 13 | kIsDst 14 | }; 15 | 16 | namespace usf 17 | { 18 | template 19 | struct Formatter 20 | { 21 | // Gets current date and time. 22 | // Or... Insert your thread safe favorite variant here! 23 | static const std::tm* now() 24 | { 25 | std::time_t t = std::time(nullptr); 26 | return std::localtime(&t); 27 | } 28 | 29 | static BasicStringSpan format_to(BasicStringSpan dst, const DateTime& date_time) 30 | { 31 | const std::tm* dt = now(); 32 | 33 | switch(date_time) 34 | { 35 | case DateTime::kCurrentDate: return basic_format_to(dst, "{}/{:02d}/{:02d}", 36 | 1900 + dt->tm_year, 1 + dt->tm_mon, dt->tm_mday); 37 | break; 38 | case DateTime::kCurrentTime: return basic_format_to(dst, "{:02d}:{:02d}:{:02d}", 39 | dt->tm_hour, dt->tm_min, dt->tm_sec); 40 | break; 41 | case DateTime::kIsDst: return basic_format_to(dst, "{}", 42 | dt->tm_isdst ? "Yes" : "No"); 43 | break; 44 | } 45 | 46 | return basic_format_to(dst, "invalid param"); 47 | } 48 | }; 49 | } // namespace usf 50 | 51 | TEST_CASE("usf::format_to, custom type DateTime") 52 | { 53 | { 54 | char str[32]{}; 55 | 56 | usf::format_to(str, 32, "Current Date: {}", DateTime::kCurrentDate); 57 | std::cout << '\n' << str << '\n'; 58 | 59 | usf::format_to(str, 32, "Current Time: {}", DateTime::kCurrentTime); 60 | std::cout << str << '\n'; 61 | 62 | usf::format_to(str, 32, "Daylight Saving Time: {}", DateTime::kIsDst); 63 | std::cout << str << "\n\n"; 64 | } 65 | } 66 | 67 | 68 | // ---------------------------------------------------------------------------- 69 | // USER DEFINED CUSTOM TYPE DEMO 2 - RGB COLOR 70 | // ---------------------------------------------------------------------------- 71 | 72 | struct Color 73 | { 74 | // Constructor needed in C++11 75 | Color(const uint8_t _r, const uint8_t _g, const uint8_t _b) 76 | : r(_r), g(_g), b(_b) {} 77 | 78 | uint8_t r{0}; 79 | uint8_t g{0}; 80 | uint8_t b{0}; 81 | }; 82 | 83 | namespace usf 84 | { 85 | template 86 | struct Formatter 87 | { 88 | static BasicStringSpan format_to(BasicStringSpan dst, const Color& color) 89 | { 90 | return basic_format_to(dst, "{:#02x}{:02x}{:02x}", color.r, color.g, color.b); 91 | } 92 | }; 93 | } // namespace usf 94 | 95 | TEST_CASE("usf::format_to, custom type Color") 96 | { 97 | { 98 | char str[32]{}; 99 | 100 | usf::format_to(str, 32, "RGB: {}", Color{35,128,255}); 101 | CHECK_EQ(str, "RGB: 0x2380ff"); 102 | } 103 | } 104 | 105 | #endif // defined(USF_TEST_CUSTOM_TYPES) 106 | -------------------------------------------------------------------------------- /unit_tests/source/unit_tests_floating_point.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "unit_tests_config.hpp" 3 | 4 | #if defined(USF_TEST_FLOATING_POINT) 5 | 6 | // ---------------------------------------------------------------------------- 7 | // FLOATING POINT CONVERSION 8 | // ---------------------------------------------------------------------------- 9 | void test_float_convertion(const double value) 10 | { 11 | const char types[]{'f', 'e', 'g'}; 12 | 13 | for(int t = 0; t < 3; ++t) // Test fixed, scientific and general format 14 | { 15 | for(int p = 0; p <= 9; ++p) // Test precision from 0 to 9 16 | { 17 | char std_str[64]{}; 18 | char usf_str[64]{}; 19 | 20 | char std_fmt[8]{}; 21 | char usf_fmt[8]{}; 22 | 23 | sprintf(std_fmt, "%%.%d%c", p, types[t]); 24 | sprintf(std_str, std_fmt, value); 25 | 26 | usf::format_to(usf_fmt, 8, "{{:.{:d}{:c}}}", p, types[t]); 27 | usf::format_to(usf_str, 64, usf_fmt, value); 28 | 29 | const auto result_ok = strcmp(usf_str, std_str) == 0; 30 | 31 | WARN_UNARY(result_ok); 32 | #if 0 33 | if(!result_ok) 34 | { 35 | printf("ERROR: %.20f, %s usf: %s || std %s\n", value, std_fmt, usf_str, std_str); 36 | usf::format_to(usf_str, 64, usf_fmt, value); 37 | } 38 | #endif 39 | } 40 | } 41 | } 42 | 43 | TEST_CASE("usf::format_to, floating point conversion") 44 | { 45 | #if 0 46 | { 47 | for(int v = 0; v <= 10000; ++v) 48 | { 49 | test_float_convertion(static_cast(v)); 50 | test_float_convertion(static_cast(v) * 1e-1); 51 | test_float_convertion(static_cast(v) * 1e-2); 52 | test_float_convertion(static_cast(v) * 1e-3); 53 | test_float_convertion(static_cast(v) * 1e-4); 54 | test_float_convertion(static_cast(v) * 1e-5); 55 | test_float_convertion(static_cast(v) * 1e-6); 56 | test_float_convertion(static_cast(v) * 1e-7); 57 | test_float_convertion(static_cast(v) * 1e-8); 58 | test_float_convertion(static_cast(v) * 1e-9); 59 | } 60 | } 61 | #endif 62 | { 63 | // Some random floating point numbers to test... 64 | constexpr double test_values_fp[] 65 | { 66 | 0.00085499999999999997, 67 | 1.065, 68 | 1.345499999999999918287585387588478624820709228515625, 69 | 2.7144439999999949719722280860878527164459228515625, 70 | 4.71333299999994981277495753602124750614166259765625, 71 | 18.4755549999999999499777914024889469146728515625, 72 | 4.82444399999994999461705447174608707427978515625, 73 | 9.1811109999998503639062619186006486415863037109375, 74 | 1e-12, 75 | -1e-12, 76 | 9.1811109999998503639062619186006486415863037109375e-11, 77 | 1.8446743E19, 78 | -1.8446743E-9, 79 | 1e-12, 80 | 1.8446743E-12, 81 | -1.8446743E-12, 82 | 5.55555, 83 | 5.0, 84 | 4.9999, 85 | 5.000111, 86 | 5.005, 87 | 5.5, 88 | 5.50, 89 | 5.501, 90 | 0.000254, 91 | 0.0005004, 92 | 0.99e-13, 93 | 0.9888, 94 | 1.3455, 95 | 0.9, 96 | -123.5, 97 | -123.51, 98 | -122.5, 99 | -122.51, 100 | -123.49, 101 | -122.49, 102 | 0, 103 | 0.0001, 104 | -9999.99, 105 | 9.9e-13, 106 | 9.9e-12, 107 | 18446742974197923840.00001, 108 | 1.84467429741979238400000, 109 | 1844.67429741979238400000, 110 | 1.8446743E19, 111 | 1.8446743E18, 112 | -1.8446743E19, 113 | -1.8446743E18, 114 | 4528212345678.946638528859811704183484516925, 115 | 1e-14, 116 | 0.9999e-14, 117 | 2.98023223876953125E-8, 118 | }; 119 | 120 | for(const auto value : test_values_fp) 121 | { 122 | test_float_convertion(value); 123 | } 124 | } 125 | } 126 | 127 | #endif // defined(USF_TEST_FLOATING_POINT) 128 | -------------------------------------------------------------------------------- /unit_tests/source/unit_tests_format_spec.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "unit_tests_config.hpp" 3 | 4 | #if defined(USF_TEST_FORMAT_SPEC) 5 | 6 | // ---------------------------------------------------------------------------- 7 | // FORMAT SPEC 8 | // ---------------------------------------------------------------------------- 9 | TEST_CASE("usf::format_to, format spec") 10 | { 11 | char str[128]{}; 12 | 13 | const void* ptr = reinterpret_cast(1000); 14 | 15 | { 16 | // WIDTH WITHOUT TYPE 17 | usf::format_to(str, 128, "{:14}", false); CHECK_EQ(str, " false"); 18 | usf::format_to(str, 128, "{:14}", 'N' ); CHECK_EQ(str, "N "); 19 | usf::format_to(str, 128, "{:14}", 123 ); CHECK_EQ(str, " 123"); 20 | usf::format_to(str, 128, "{:14}", 1.234); CHECK_EQ(str, " 1.234"); 21 | usf::format_to(str, 128, "{:14}", ptr ); CHECK_EQ(str, " 3e8"); 22 | usf::format_to(str, 128, "{:14}", "str"); CHECK_EQ(str, "str "); 23 | } 24 | { 25 | // WIDTH + TYPE 26 | usf::format_to(str, 128, "{:14c}", 'N' ); CHECK_EQ(str, "N "); 27 | usf::format_to(str, 128, "{:14d}", 123 ); CHECK_EQ(str, " 123"); 28 | usf::format_to(str, 128, "{:14x}", 123 ); CHECK_EQ(str, " 7b"); 29 | usf::format_to(str, 128, "{:14o}", 123 ); CHECK_EQ(str, " 173"); 30 | usf::format_to(str, 128, "{:14b}", 123 ); CHECK_EQ(str, " 1111011"); 31 | usf::format_to(str, 128, "{:14f}", 1.234); CHECK_EQ(str, " 1.234000"); 32 | usf::format_to(str, 128, "{:14e}", 1.234); CHECK_EQ(str, " 1.234000e+00"); 33 | usf::format_to(str, 128, "{:14g}", 1.234); CHECK_EQ(str, " 1.234"); 34 | usf::format_to(str, 128, "{:14p}", ptr ); CHECK_EQ(str, " 3e8"); 35 | usf::format_to(str, 128, "{:14s}", "str"); CHECK_EQ(str, "str "); 36 | } 37 | { 38 | // PRECISION + TYPE 39 | usf::format_to(str, 128, "{:.1f}", 1.234); CHECK_EQ(str, "1.2"); 40 | usf::format_to(str, 128, "{:.1e}", 1.234); CHECK_EQ(str, "1.2e+00"); 41 | usf::format_to(str, 128, "{:.1g}", 1.234); CHECK_EQ(str, "1"); 42 | usf::format_to(str, 128, "{:.1s}", "str"); CHECK_EQ(str, "s"); 43 | 44 | // PRECISION + INVALID TYPE 45 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1c}", 'N'), std::runtime_error); 46 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1d}", 123), std::runtime_error); 47 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1x}", 123), std::runtime_error); 48 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1o}", 123), std::runtime_error); 49 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1b}", 123), std::runtime_error); 50 | 51 | // PRECISION WITHOUT TYPE 52 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", false), std::runtime_error); 53 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", 'N' ), std::runtime_error); 54 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", 123 ), std::runtime_error); 55 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", 123 ), std::runtime_error); 56 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", 123 ), std::runtime_error); 57 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", 123 ), std::runtime_error); 58 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", 1.234), std::runtime_error); 59 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", 1.234), std::runtime_error); 60 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", 1.234), std::runtime_error); 61 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", ptr ), std::runtime_error); 62 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:.1}", "str"), std::runtime_error); 63 | } 64 | { 65 | // LEFT ALIGNMENT + WIDTH 66 | usf::format_to(str, 128, "{:<14c}", 'N' ); CHECK_EQ(str, "N "); 67 | usf::format_to(str, 128, "{:<14d}", 123 ); CHECK_EQ(str, "123 "); 68 | usf::format_to(str, 128, "{:<14x}", 123 ); CHECK_EQ(str, "7b "); 69 | usf::format_to(str, 128, "{:<14o}", 123 ); CHECK_EQ(str, "173 "); 70 | usf::format_to(str, 128, "{:<14b}", 123 ); CHECK_EQ(str, "1111011 "); 71 | usf::format_to(str, 128, "{:<14f}", 1.234); CHECK_EQ(str, "1.234000 "); 72 | usf::format_to(str, 128, "{:<14e}", 1.234); CHECK_EQ(str, "1.234000e+00 "); 73 | usf::format_to(str, 128, "{:<14g}", 1.234); CHECK_EQ(str, "1.234 "); 74 | usf::format_to(str, 128, "{:<14p}", ptr ); CHECK_EQ(str, "3e8 "); 75 | usf::format_to(str, 128, "{:<14s}", "str"); CHECK_EQ(str, "str "); 76 | } 77 | { 78 | // RIGHT ALIGNMENT + WIDTH 79 | usf::format_to(str, 128, "{:>14c}", 'N' ); CHECK_EQ(str, " N"); 80 | usf::format_to(str, 128, "{:>14d}", 123 ); CHECK_EQ(str, " 123"); 81 | usf::format_to(str, 128, "{:>14x}", 123 ); CHECK_EQ(str, " 7b"); 82 | usf::format_to(str, 128, "{:>14o}", 123 ); CHECK_EQ(str, " 173"); 83 | usf::format_to(str, 128, "{:>14b}", 123 ); CHECK_EQ(str, " 1111011"); 84 | usf::format_to(str, 128, "{:>14f}", 1.234); CHECK_EQ(str, " 1.234000"); 85 | usf::format_to(str, 128, "{:>14e}", 1.234); CHECK_EQ(str, " 1.234000e+00"); 86 | usf::format_to(str, 128, "{:>14g}", 1.234); CHECK_EQ(str, " 1.234"); 87 | usf::format_to(str, 128, "{:>14p}", ptr ); CHECK_EQ(str, " 3e8"); 88 | usf::format_to(str, 128, "{:>14s}", "str"); CHECK_EQ(str, " str"); 89 | } 90 | { 91 | // CENTER ALIGNMENT + WIDTH 92 | usf::format_to(str, 128, "{:^14c}", 'N' ); CHECK_EQ(str, " N "); 93 | usf::format_to(str, 128, "{:^14d}", 123 ); CHECK_EQ(str, " 123 "); 94 | usf::format_to(str, 128, "{:^14x}", 123 ); CHECK_EQ(str, " 7b "); 95 | usf::format_to(str, 128, "{:^14o}", 123 ); CHECK_EQ(str, " 173 "); 96 | usf::format_to(str, 128, "{:^14b}", 123 ); CHECK_EQ(str, " 1111011 "); 97 | usf::format_to(str, 128, "{:^14f}", 1.234); CHECK_EQ(str, " 1.234000 "); 98 | usf::format_to(str, 128, "{:^14e}", 1.234); CHECK_EQ(str, " 1.234000e+00 "); 99 | usf::format_to(str, 128, "{:^14g}", 1.234); CHECK_EQ(str, " 1.234 "); 100 | usf::format_to(str, 128, "{:^14p}", ptr ); CHECK_EQ(str, " 3e8 "); 101 | usf::format_to(str, 128, "{:^14s}", "str"); CHECK_EQ(str, " str "); 102 | } 103 | { 104 | // NUMERIC ALIGNMENT + WIDTH 105 | usf::format_to(str, 128, "{:=14d}", 123 ); CHECK_EQ(str, " 123"); 106 | usf::format_to(str, 128, "{:=14x}", 123 ); CHECK_EQ(str, " 7b"); 107 | usf::format_to(str, 128, "{:=14o}", 123 ); CHECK_EQ(str, " 173"); 108 | usf::format_to(str, 128, "{:=14b}", 123 ); CHECK_EQ(str, " 1111011"); 109 | usf::format_to(str, 128, "{:=14f}", 1.234); CHECK_EQ(str, " 1.234000"); 110 | usf::format_to(str, 128, "{:=14e}", 1.234); CHECK_EQ(str, " 1.234000e+00"); 111 | usf::format_to(str, 128, "{:=14g}", 1.234); CHECK_EQ(str, " 1.234"); 112 | usf::format_to(str, 128, "{:=14p}", ptr ); CHECK_EQ(str, " 3e8"); 113 | 114 | // NUMERIC ALIGNMENT + WIDTH + INVALID TYPE 115 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:=14c}", 'N' ), std::runtime_error); 116 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:=14s}", "str"), std::runtime_error); 117 | } 118 | { 119 | // FILL CHAR + NUMERIC ALIGNMENT + WIDTH 120 | usf::format_to(str, 128, "{:0=14d}", 123 ); CHECK_EQ(str, "00000000000123"); 121 | usf::format_to(str, 128, "{:0=14x}", 123 ); CHECK_EQ(str, "0000000000007b"); 122 | usf::format_to(str, 128, "{:0=14o}", 123 ); CHECK_EQ(str, "00000000000173"); 123 | usf::format_to(str, 128, "{:0=14b}", 123 ); CHECK_EQ(str, "00000001111011"); 124 | usf::format_to(str, 128, "{:0=14f}", 1.234); CHECK_EQ(str, "0000001.234000"); 125 | usf::format_to(str, 128, "{:0=14e}", 1.234); CHECK_EQ(str, "001.234000e+00"); 126 | usf::format_to(str, 128, "{:0=14g}", 1.234); CHECK_EQ(str, "0000000001.234"); 127 | usf::format_to(str, 128, "{:0=14p}", ptr ); CHECK_EQ(str, "000000000003e8"); 128 | 129 | usf::format_to(str, 128, "{:0=14d}", -123 ); CHECK_EQ(str, "-0000000000123"); 130 | usf::format_to(str, 128, "{:0=14x}", -123 ); CHECK_EQ(str, "-000000000007b"); 131 | usf::format_to(str, 128, "{:0=14o}", -123 ); CHECK_EQ(str, "-0000000000173"); 132 | usf::format_to(str, 128, "{:0=14b}", -123 ); CHECK_EQ(str, "-0000001111011"); 133 | usf::format_to(str, 128, "{:0=14f}", -1.234); CHECK_EQ(str, "-000001.234000"); 134 | usf::format_to(str, 128, "{:0=14e}", -1.234); CHECK_EQ(str, "-01.234000e+00"); 135 | usf::format_to(str, 128, "{:0=14g}", -1.234); CHECK_EQ(str, "-000000001.234"); 136 | } 137 | { 138 | // ZERO FILL + WIDTH 139 | usf::format_to(str, 128, "{:014d}", 123 ); CHECK_EQ(str, "00000000000123"); 140 | usf::format_to(str, 128, "{:014x}", 123 ); CHECK_EQ(str, "0000000000007b"); 141 | usf::format_to(str, 128, "{:014o}", 123 ); CHECK_EQ(str, "00000000000173"); 142 | usf::format_to(str, 128, "{:014b}", 123 ); CHECK_EQ(str, "00000001111011"); 143 | usf::format_to(str, 128, "{:014f}", 1.234); CHECK_EQ(str, "0000001.234000"); 144 | usf::format_to(str, 128, "{:014e}", 1.234); CHECK_EQ(str, "001.234000e+00"); 145 | usf::format_to(str, 128, "{:014g}", 1.234); CHECK_EQ(str, "0000000001.234"); 146 | usf::format_to(str, 128, "{:014p}", ptr ); CHECK_EQ(str, "000000000003e8"); 147 | 148 | usf::format_to(str, 128, "{:014d}", -123 ); CHECK_EQ(str, "-0000000000123"); 149 | usf::format_to(str, 128, "{:014x}", -123 ); CHECK_EQ(str, "-000000000007b"); 150 | usf::format_to(str, 128, "{:014o}", -123 ); CHECK_EQ(str, "-0000000000173"); 151 | usf::format_to(str, 128, "{:014b}", -123 ); CHECK_EQ(str, "-0000001111011"); 152 | usf::format_to(str, 128, "{:014f}", -1.234); CHECK_EQ(str, "-000001.234000"); 153 | usf::format_to(str, 128, "{:014e}", -1.234); CHECK_EQ(str, "-01.234000e+00"); 154 | usf::format_to(str, 128, "{:014g}", -1.234); CHECK_EQ(str, "-000000001.234"); 155 | } 156 | { 157 | // HASH (integers) 158 | usf::format_to(str, 128, "{:#x}", 123); CHECK_EQ(str, "0x7b"); 159 | usf::format_to(str, 128, "{:#X}", 123); CHECK_EQ(str, "0X7B"); 160 | usf::format_to(str, 128, "{:#o}", 123); CHECK_EQ(str, "0173"); 161 | usf::format_to(str, 128, "{:#b}", 123); CHECK_EQ(str, "0b1111011"); 162 | usf::format_to(str, 128, "{:#B}", 123); CHECK_EQ(str, "0B1111011"); 163 | usf::format_to(str, 128, "{:#p}", ptr); CHECK_EQ(str, "0x3e8"); 164 | usf::format_to(str, 128, "{:#P}", ptr); CHECK_EQ(str, "0X3E8"); 165 | 166 | // HASH (floating point) 167 | usf::format_to(str, 128, "{:#f}", 1.234); CHECK_EQ(str, "1.234000"); 168 | usf::format_to(str, 128, "{:#.0f}", 1.0); CHECK_EQ(str, "1."); 169 | usf::format_to(str, 128, "{:#e}", 1.234); CHECK_EQ(str, "1.234000e+00"); 170 | usf::format_to(str, 128, "{:#.0e}", 1.0); CHECK_EQ(str, "1.e+00"); 171 | usf::format_to(str, 128, "{:#g}", 1.234); CHECK_EQ(str, "1.23400"); 172 | usf::format_to(str, 128, "{:#.2g}", 1.0); CHECK_EQ(str, "1.0"); 173 | usf::format_to(str, 128, "{:#.0g}", 1.0); CHECK_EQ(str, "1."); 174 | 175 | // HASH (negative floating point) 176 | usf::format_to(str, 128, "{:#f}", -1.234); CHECK_EQ(str, "-1.234000"); 177 | usf::format_to(str, 128, "{:#.0f}", -1.0); CHECK_EQ(str, "-1."); 178 | usf::format_to(str, 128, "{:#e}", -1.234); CHECK_EQ(str, "-1.234000e+00"); 179 | usf::format_to(str, 128, "{:#.0e}", -1.0); CHECK_EQ(str, "-1.e+00"); 180 | usf::format_to(str, 128, "{:#g}", -1.234); CHECK_EQ(str, "-1.23400"); 181 | usf::format_to(str, 128, "{:#.2g}", -1.0); CHECK_EQ(str, "-1.0"); 182 | usf::format_to(str, 128, "{:#.0g}", -1.0); CHECK_EQ(str, "-1."); 183 | 184 | // HASH + INVALID TYPES 185 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:#c}", 'N' ), std::runtime_error); 186 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:#d}", 123 ), std::runtime_error); 187 | CHECK_THROWS_AS(usf::format_to(str, 128, "{:#s}", "str"), std::runtime_error); 188 | } 189 | { 190 | // FILL CHAR + ALIGNMENT + HASH (integers) 191 | usf::format_to(str, 128, "{:*>#14x}", 123); CHECK_EQ(str, "**********0x7b"); 192 | usf::format_to(str, 128, "{:*>#14X}", 123); CHECK_EQ(str, "**********0X7B"); 193 | usf::format_to(str, 128, "{:*>#14o}", 123); CHECK_EQ(str, "**********0173"); 194 | usf::format_to(str, 128, "{:*>#14b}", 123); CHECK_EQ(str, "*****0b1111011"); 195 | usf::format_to(str, 128, "{:*>#14B}", 123); CHECK_EQ(str, "*****0B1111011"); 196 | usf::format_to(str, 128, "{:*>#14p}", ptr); CHECK_EQ(str, "*********0x3e8"); 197 | usf::format_to(str, 128, "{:*>#14P}", ptr); CHECK_EQ(str, "*********0X3E8"); 198 | 199 | // FILL CHAR + ALIGNMENT + HASH (negative floating point) 200 | usf::format_to(str, 128, "{:*>#14f}", -1.234); CHECK_EQ(str, "*****-1.234000"); 201 | usf::format_to(str, 128, "{:*>#14.0f}", -1.0); CHECK_EQ(str, "***********-1."); 202 | usf::format_to(str, 128, "{:*>#14e}", -1.234); CHECK_EQ(str, "*-1.234000e+00"); 203 | usf::format_to(str, 128, "{:*>#14.0e}", -1.0); CHECK_EQ(str, "*******-1.e+00"); 204 | usf::format_to(str, 128, "{:*>#14g}", -1.234); CHECK_EQ(str, "******-1.23400"); 205 | usf::format_to(str, 128, "{:*>#14.2g}", -1.0); CHECK_EQ(str, "**********-1.0"); 206 | usf::format_to(str, 128, "{:*>#14.0g}", -1.0); CHECK_EQ(str, "***********-1."); 207 | } 208 | { 209 | // FILL CHAR + ALIGNMENT + HASH + ZERO FILL (integers) 210 | // ZERO FILL HAS PRECEDENCE OVER ALIGNMENT + FILL CHAR! 211 | usf::format_to(str, 128, "{:*>#014x}", 123); CHECK_EQ(str, "0x00000000007b"); 212 | usf::format_to(str, 128, "{:*>#014X}", 123); CHECK_EQ(str, "0X00000000007B"); 213 | usf::format_to(str, 128, "{:*>#014o}", 123); CHECK_EQ(str, "00000000000173"); 214 | usf::format_to(str, 128, "{:*>#014b}", 123); CHECK_EQ(str, "0b000001111011"); 215 | usf::format_to(str, 128, "{:*>#014B}", 123); CHECK_EQ(str, "0B000001111011"); 216 | usf::format_to(str, 128, "{:*>#014p}", ptr); CHECK_EQ(str, "0x0000000003e8"); 217 | usf::format_to(str, 128, "{:*>#014P}", ptr); CHECK_EQ(str, "0X0000000003E8"); 218 | 219 | // FILL CHAR + ALIGNMENT + HASH (negative floating point) 220 | // ZERO FILL HAS PRECEDENCE OVER ALIGNMENT + FILL CHAR! 221 | usf::format_to(str, 128, "{:*>#014f}", -1.234); CHECK_EQ(str, "-000001.234000"); 222 | usf::format_to(str, 128, "{:*>#014.0f}", -1.0); CHECK_EQ(str, "-000000000001."); 223 | usf::format_to(str, 128, "{:*>#014e}", -1.234); CHECK_EQ(str, "-01.234000e+00"); 224 | usf::format_to(str, 128, "{:*>#014.0e}", -1.0); CHECK_EQ(str, "-00000001.e+00"); 225 | usf::format_to(str, 128, "{:*>#014g}", -1.234); CHECK_EQ(str, "-0000001.23400"); 226 | usf::format_to(str, 128, "{:*>#014.2g}", -1.0); CHECK_EQ(str, "-00000000001.0"); 227 | usf::format_to(str, 128, "{:*>#014.0g}", -1.0); CHECK_EQ(str, "-000000000001."); 228 | } 229 | { 230 | // FILL CHAR + ALIGNMENT + SIGN ('+') + HASH (integers) 231 | usf::format_to(str, 128, "{:*>+#14x}", 123); CHECK_EQ(str, "*********+0x7b"); 232 | usf::format_to(str, 128, "{:*>+#14X}", 123); CHECK_EQ(str, "*********+0X7B"); 233 | usf::format_to(str, 128, "{:*>+#14o}", 123); CHECK_EQ(str, "*********+0173"); 234 | usf::format_to(str, 128, "{:*>+#14b}", 123); CHECK_EQ(str, "****+0b1111011"); 235 | usf::format_to(str, 128, "{:*>+#14B}", 123); CHECK_EQ(str, "****+0B1111011"); 236 | 237 | // FILL CHAR + ALIGNMENT + SIGN ('+') + HASH (negative floating point) 238 | usf::format_to(str, 128, "{:*>+#14f}", -1.234); CHECK_EQ(str, "*****-1.234000"); 239 | usf::format_to(str, 128, "{:*>+#14.0f}", -1.0); CHECK_EQ(str, "***********-1."); 240 | usf::format_to(str, 128, "{:*>+#14e}", -1.234); CHECK_EQ(str, "*-1.234000e+00"); 241 | usf::format_to(str, 128, "{:*>+#14.0e}", -1.0); CHECK_EQ(str, "*******-1.e+00"); 242 | usf::format_to(str, 128, "{:*>+#14g}", -1.234); CHECK_EQ(str, "******-1.23400"); 243 | usf::format_to(str, 128, "{:*>+#14.2g}", -1.0); CHECK_EQ(str, "**********-1.0"); 244 | usf::format_to(str, 128, "{:*>+#14.0g}", -1.0); CHECK_EQ(str, "***********-1."); 245 | } 246 | { 247 | // FILL CHAR + ALIGNMENT + SIGN ('+') + HASH + ZERO FILL (integers) 248 | // ZERO FILL HAS PRECEDENCE OVER ALIGNMENT + FILL CHAR! 249 | usf::format_to(str, 128, "{:*>+#014x}", 123); CHECK_EQ(str, "+0x0000000007b"); 250 | usf::format_to(str, 128, "{:*>+#014X}", 123); CHECK_EQ(str, "+0X0000000007B"); 251 | usf::format_to(str, 128, "{:*>+#014o}", 123); CHECK_EQ(str, "+0000000000173"); 252 | usf::format_to(str, 128, "{:*>+#014b}", 123); CHECK_EQ(str, "+0b00001111011"); 253 | usf::format_to(str, 128, "{:*>+#014B}", 123); CHECK_EQ(str, "+0B00001111011"); 254 | 255 | // FILL CHAR + ALIGNMENT + SIGN ('+') + HASH (negative floating point) 256 | // ZERO FILL HAS PRECEDENCE OVER ALIGNMENT + FILL CHAR! 257 | usf::format_to(str, 128, "{:*>+#014f}", -1.234); CHECK_EQ(str, "-000001.234000"); 258 | usf::format_to(str, 128, "{:*>+#014.0f}", -1.0); CHECK_EQ(str, "-000000000001."); 259 | usf::format_to(str, 128, "{:*>+#014e}", -1.234); CHECK_EQ(str, "-01.234000e+00"); 260 | usf::format_to(str, 128, "{:*>+#014.0e}", -1.0); CHECK_EQ(str, "-00000001.e+00"); 261 | usf::format_to(str, 128, "{:*>+#014g}", -1.234); CHECK_EQ(str, "-0000001.23400"); 262 | usf::format_to(str, 128, "{:*>+#014.2g}", -1.0); CHECK_EQ(str, "-00000000001.0"); 263 | usf::format_to(str, 128, "{:*>+#014.0g}", -1.0); CHECK_EQ(str, "-000000000001."); 264 | } 265 | { 266 | // FILL CHAR + ALIGNMENT + SIGN (' ') + HASH (integers) 267 | usf::format_to(str, 128, "{:*> #14x}", 123); CHECK_EQ(str, "********* 0x7b"); 268 | usf::format_to(str, 128, "{:*> #14X}", 123); CHECK_EQ(str, "********* 0X7B"); 269 | usf::format_to(str, 128, "{:*> #14o}", 123); CHECK_EQ(str, "********* 0173"); 270 | usf::format_to(str, 128, "{:*> #14b}", 123); CHECK_EQ(str, "**** 0b1111011"); 271 | usf::format_to(str, 128, "{:*> #14B}", 123); CHECK_EQ(str, "**** 0B1111011"); 272 | 273 | // FILL CHAR + ALIGNMENT + SIGN (' ') + HASH (negative floating point) 274 | usf::format_to(str, 128, "{:*> #14f}", -1.234); CHECK_EQ(str, "*****-1.234000"); 275 | usf::format_to(str, 128, "{:*> #14.0f}", -1.0); CHECK_EQ(str, "***********-1."); 276 | usf::format_to(str, 128, "{:*> #14e}", -1.234); CHECK_EQ(str, "*-1.234000e+00"); 277 | usf::format_to(str, 128, "{:*> #14.0e}", -1.0); CHECK_EQ(str, "*******-1.e+00"); 278 | usf::format_to(str, 128, "{:*> #14g}", -1.234); CHECK_EQ(str, "******-1.23400"); 279 | usf::format_to(str, 128, "{:*> #14.2g}", -1.0); CHECK_EQ(str, "**********-1.0"); 280 | usf::format_to(str, 128, "{:*> #14.0g}", -1.0); CHECK_EQ(str, "***********-1."); 281 | } 282 | { 283 | // FILL CHAR + ALIGNMENT + SIGN (' ') + HASH + ZERO FILL (integers) 284 | // ZERO FILL HAS PRECEDENCE OVER ALIGNMENT + FILL CHAR! 285 | usf::format_to(str, 128, "{:*> #014x}", 123); CHECK_EQ(str, " 0x0000000007b"); 286 | usf::format_to(str, 128, "{:*> #014X}", 123); CHECK_EQ(str, " 0X0000000007B"); 287 | usf::format_to(str, 128, "{:*> #014o}", 123); CHECK_EQ(str, " 0000000000173"); 288 | usf::format_to(str, 128, "{:*> #014b}", 123); CHECK_EQ(str, " 0b00001111011"); 289 | usf::format_to(str, 128, "{:*> #014B}", 123); CHECK_EQ(str, " 0B00001111011"); 290 | 291 | // FILL CHAR + ALIGNMENT + SIGN (' ') + HASH (negative floating point) 292 | // ZERO FILL HAS PRECEDENCE OVER ALIGNMENT + FILL CHAR! 293 | usf::format_to(str, 128, "{:*> #014f}", -1.234); CHECK_EQ(str, "-000001.234000"); 294 | usf::format_to(str, 128, "{:*> #014.0f}", -1.0); CHECK_EQ(str, "-000000000001."); 295 | usf::format_to(str, 128, "{:*> #014e}", -1.234); CHECK_EQ(str, "-01.234000e+00"); 296 | usf::format_to(str, 128, "{:*> #014.0e}", -1.0); CHECK_EQ(str, "-00000001.e+00"); 297 | usf::format_to(str, 128, "{:*> #014g}", -1.234); CHECK_EQ(str, "-0000001.23400"); 298 | usf::format_to(str, 128, "{:*> #014.2g}", -1.0); CHECK_EQ(str, "-00000000001.0"); 299 | usf::format_to(str, 128, "{:*> #014.0g}", -1.0); CHECK_EQ(str, "-000000000001."); 300 | } 301 | } 302 | 303 | #endif // #if defined(USF_TEST_FORMAT_SPEC) 304 | -------------------------------------------------------------------------------- /unit_tests/source/unit_tests_positional_args.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "unit_tests_config.hpp" 3 | 4 | #if defined(USF_TEST_POSITIONAL_ARGS) 5 | 6 | // ---------------------------------------------------------------------------- 7 | // POSITIONAL ARGUMENTS 8 | // ---------------------------------------------------------------------------- 9 | TEST_CASE("usf::format_to, positional indices") 10 | { 11 | char str[32]{}; 12 | 13 | usf::format_to(str, 32, "{0}{1}{0}", "abra", "cad"); 14 | CHECK_EQ(str, "abracadabra"); 15 | 16 | // The following test shows both the positional and 17 | // sequencial arguments working seamlessly together. 18 | 19 | usf::format_to(str, 32, "{}{}{0}", "abra", "cad"); 20 | CHECK_EQ(str, "abracadabra"); 21 | 22 | usf::format_to(str, 32, "{0}{1}{}", "abra", "cad"); 23 | CHECK_EQ(str, "abracadabra"); 24 | 25 | usf::format_to(str, 32, "{}{1}{0}", "abra", "cad"); 26 | CHECK_EQ(str, "abracadabra"); 27 | } 28 | 29 | #endif // #if defined(USF_TEST_POSITIONAL_ARGS) 30 | --------------------------------------------------------------------------------