├── LICENSE ├── README.md ├── to_string.hpp └── f_to_string.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # constexpr-to-string 2 | 3 | Requires C++14 or later. 4 | 5 | **Features:** 6 | 7 | * Convert any integral type to a string at compile-time 8 | * Supports converting to bases 2 through 36 9 | * No external dependencies 10 | * Supports custom character types, e.g. `to_string<123, 10, wchar_t>` 11 | * C++20: Supports floating-point-to-string conversion with `f_to_string` 12 | 13 | **How to use:** 14 | 15 | The file `to_string.hpp` provides a `to_string` utility, which may be used as below: 16 | 17 | ```cpp 18 | const char *number = to_string<2147483648999954564, 16>; // produces "1DCD65003B9A1884" 19 | puts(number); 20 | puts(to_string<-42>); // produces "-42" 21 | puts(to_string<30, 2>); // produces "11110" 22 | ``` 23 | 24 | With `to_string`, all that will be found in program disassembly are the resulting string literals, as if you wrote the strings yourself. 25 | 26 | Try it [on Compiler Explorer](https://godbolt.org/z/T-MFoh). 27 | 28 | `f_to_string.hpp`, requiring C++20, provides an `f_to_string` utility for floating-point conversion: 29 | 30 | ```cpp 31 | puts(f_to_string<3.1415926>); // Defaults to 5-point precision: "3.14159" 32 | puts(f_to_string<{3.1415926, 7}>); // Specify precision: "3.1415926" 33 | ``` 34 | 35 | # How it works 36 | 37 | C++14 greatly expanded the capabilities of compile-time code execution through `constexpr`. In particular, it allows for non-trivial constructors to be `constexpr`. 38 | 39 | `to_string` takes advantage of this by providing an object that converts a template-parameter integer to a string using a basic `itoa` implementation in the constructor. Through an additional `constexpr` member function, we can calculate the length of the resulting string; this can be used to size the object's string buffer for a perfect fit. 40 | 41 | Beyond this, `to_string` simply provides familiar member functions that allow for iteration and data access. The expansion of the capabilities of `auto` in C++14 help make these definitions concise. 42 | 43 | The floating-point implementation `f_to_string` takes a similar approach, but requires C++20 as it needs a `double_wrapper` object to capture the `double` value. `double` and `float` cannot directly be template parameters as of C++20, and a non-type template parameter like the `double_wrapper` structure was not allowed before C++20. 44 | 45 | -------------------------------------------------------------------------------- /to_string.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * to_string.hpp - Provides compile-time integer-to-string conversion. 3 | * Written by Clyne Sullivan. 4 | * https://github.com/tcsullivan/constexpr-to-string 5 | */ 6 | 7 | #ifndef TCSULLIVAN_TO_STRING_HPP_ 8 | #define TCSULLIVAN_TO_STRING_HPP_ 9 | 10 | #include 11 | #include 12 | 13 | namespace constexpr_to_string { 14 | 15 | constexpr char digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 16 | 17 | /** 18 | * @struct to_string_t 19 | * @brief Provides the ability to convert any integral to a string at compile-time. 20 | * @tparam N Number to convert 21 | * @tparam base Desired base, can be from 2 to 36 22 | */ 23 | template 1 && base < sizeof(digits)), int> = 0> 25 | class to_string_t { 26 | 27 | constexpr static auto buflen() noexcept { 28 | unsigned int len = N > 0 ? 1 : 2; 29 | for (auto n = N; n; len++, n /= base); 30 | return len; 31 | } 32 | 33 | char_type buf[buflen()] = {}; 34 | 35 | public: 36 | /** 37 | * Constructs the object, filling `buf` with the string representation of N. 38 | */ 39 | constexpr to_string_t() noexcept { 40 | auto ptr = end(); 41 | *--ptr = '\0'; 42 | 43 | if (N != 0) { 44 | for (auto n = N; n; n /= base) 45 | *--ptr = digits[(N < 0 ? -1 : 1) * (n % base)]; 46 | if (N < 0) 47 | *--ptr = '-'; 48 | } else { 49 | buf[0] = '0'; 50 | } 51 | } 52 | 53 | // Support implicit casting to `char *` or `const char *`. 54 | constexpr operator char_type *() noexcept { return buf; } 55 | constexpr operator const char_type *() const noexcept { return buf; } 56 | 57 | constexpr auto size() const noexcept { return sizeof(buf) / sizeof(buf[0]); } 58 | 59 | // Element access 60 | constexpr auto data() noexcept { return buf; } 61 | constexpr auto data() const noexcept { return buf; } 62 | constexpr auto& operator[](unsigned int i) noexcept { return buf[i]; } 63 | constexpr const auto& operator[](unsigned int i) const noexcept { return buf[i]; } 64 | constexpr auto& front() noexcept { return buf[0]; } 65 | constexpr const auto& front() const noexcept { return buf[0]; } 66 | constexpr auto& back() noexcept { return buf[size() - 1]; } 67 | constexpr const auto& back() const noexcept { return buf[size() - 1]; } 68 | 69 | // Iterators 70 | constexpr auto begin() noexcept { return buf; } 71 | constexpr auto begin() const noexcept { return buf; } 72 | constexpr auto end() noexcept { return buf + size(); } 73 | constexpr auto end() const noexcept { return buf + size(); } 74 | }; 75 | 76 | } // namespace constexpr_to_string 77 | 78 | /** 79 | * Simplifies use of `to_string_t` from `to_string_t()` to `to_string`. 80 | */ 81 | template 82 | constexpr constexpr_to_string::to_string_t to_string; 83 | 84 | #endif // TCSULLIVAN_TO_STRING_HPP_ 85 | 86 | -------------------------------------------------------------------------------- /f_to_string.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * f_to_string.hpp - Provides compile-time floating-point-to-string conversion. 3 | * Written by Clyne Sullivan. 4 | * https://github.com/tcsullivan/constexpr-to-string 5 | */ 6 | 7 | #ifndef TCSULLIVAN_F_TO_STRING_HPP_ 8 | #define TCSULLIVAN_F_TO_STRING_HPP_ 9 | 10 | namespace constexpr_to_string { 11 | 12 | struct double_wrapper { 13 | long long int whole = 0; 14 | long long int frac = 0; 15 | 16 | constexpr double_wrapper(double v, int prec = 5) { 17 | whole = static_cast(v); 18 | v -= whole; 19 | for (int i = 0; i < prec; i++) 20 | v *= 10; 21 | frac = static_cast(v); 22 | } 23 | }; 24 | 25 | /** 26 | * @struct f_to_string_t 27 | * @brief Provides the ability to convert a floating-point number to a string at compile-time. 28 | * @tparam N Number to convert 29 | */ 30 | template 31 | class f_to_string_t { 32 | char_type buf[([]() constexpr noexcept { 33 | unsigned int len = 2; 34 | if (N.whole <= 0) len++; 35 | for (auto n = N.whole; n; len++, n /= 10); 36 | if (N.frac == 0 || (N.whole == 0 && N.frac < 0)) len++; 37 | for (auto n = N.frac; n; len++, n /= 10); 38 | return len; 39 | }())] = {}; 40 | 41 | public: 42 | /** 43 | * Constructs the object, filling `buf` with the string representation of N. 44 | */ 45 | constexpr f_to_string_t() noexcept { 46 | auto append = [](auto V, auto& ptr) { 47 | if (V != 0) { 48 | for (auto n = V; n; n /= 10) 49 | *--ptr = (V < 0 ? -1 : 1) * (n % 10) + '0'; 50 | } else { 51 | *--ptr = '0'; 52 | } 53 | }; 54 | 55 | auto ptr = end(); 56 | *--ptr = '\0'; 57 | append(N.frac, ptr); 58 | *--ptr = '.'; 59 | append(N.whole, ptr); 60 | if (N.frac < 0 || N.whole < 0) 61 | *--ptr = '-'; 62 | } 63 | 64 | // Support implicit casting to `char *` or `const char *`. 65 | constexpr operator char_type *() noexcept { return buf; } 66 | constexpr operator const char_type *() const noexcept { return buf; } 67 | 68 | constexpr auto size() const noexcept { return sizeof(buf) / sizeof(buf[0]); } 69 | // Element access 70 | constexpr auto data() noexcept { return buf; } 71 | constexpr auto data() const noexcept { return buf; } 72 | constexpr auto& operator[](unsigned int i) noexcept { return buf[i]; } 73 | constexpr const auto& operator[](unsigned int i) const noexcept { return buf[i]; } 74 | constexpr auto& front() noexcept { return buf[0]; } 75 | constexpr const auto& front() const noexcept { return buf[0]; } 76 | constexpr auto& back() noexcept { return buf[size() - 1]; } 77 | constexpr const auto& back() const noexcept { return buf[size() - 1]; } 78 | // Iterators 79 | constexpr auto begin() noexcept { return buf; } 80 | constexpr auto begin() const noexcept { return buf; } 81 | constexpr auto end() noexcept { return buf + size(); } 82 | constexpr auto end() const noexcept { return buf + size(); } 83 | }; 84 | 85 | } // namespace constexpr_to_string 86 | 87 | /** 88 | * Simplifies use of `f_to_string_t` from `f_to_string_t()` to `f_to_string`. 89 | */ 90 | template 91 | constexpr constexpr_to_string::f_to_string_t f_to_string; 92 | 93 | #endif // TCSULLIVAN_F_TO_STRING_HPP_ 94 | --------------------------------------------------------------------------------