Removing magic constant from template param check #3
12
README.md
12
README.md
@ -6,10 +6,11 @@
|
|||||||
* Supports converting to any base between 2 and 36 inclusive
|
* Supports converting to any base between 2 and 36 inclusive
|
||||||
* No external dependencies, only includes `type_traits` for template parameter checking
|
* No external dependencies, only includes `type_traits` for template parameter checking
|
||||||
* Supports custom character types, e.g. `to_string<123, 10, wchar_t>`
|
* Supports custom character types, e.g. `to_string<123, 10, wchar_t>`
|
||||||
|
* C++20: Supports floating-point-to-string conversion with `f_to_string`
|
||||||
|
|
||||||
**How to use:**
|
**How to use:**
|
||||||
|
|
||||||
This single header file provides a `to_string` utility, which may be used as below:
|
The file `to_string.hpp` provides a `to_string` utility, which may be used as below:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
const char *number = to_string<2147483648999954564, 16>; // produces "1DCD65003B9A1884"
|
const char *number = to_string<2147483648999954564, 16>; // produces "1DCD65003B9A1884"
|
||||||
@ -22,6 +23,13 @@ With `to_string`, all that will be found in program disassembly are the resultin
|
|||||||
|
|
||||||
Try it [on Compiler Explorer](https://godbolt.org/z/T-MFoh).
|
Try it [on Compiler Explorer](https://godbolt.org/z/T-MFoh).
|
||||||
|
|
||||||
|
`f_to_string.hpp`, requiring C++20, provides an `f_to_string` utility for floating-point conversion:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
puts(f_to_string<3.1415926>); // Defaults to 5-point precision: "3.14159"
|
||||||
|
puts(f_to_string<{3.1415926, 7}>); // Specify precision: "3.1415926"
|
||||||
|
```
|
||||||
|
|
||||||
# How it works
|
# How it works
|
||||||
|
|
||||||
The basic structure of `to_string` is shown below:
|
The basic structure of `to_string` is shown below:
|
||||||
@ -29,7 +37,7 @@ The basic structure of `to_string` is shown below:
|
|||||||
```cpp
|
```cpp
|
||||||
template<auto N, unsigned int base, typename char_type, /* N type-check and base bounds-check */>
|
template<auto N, unsigned int base, typename char_type, /* N type-check and base bounds-check */>
|
||||||
struct to_string_t {
|
struct to_string_t {
|
||||||
char_type buf[]; // Size selection explained later.
|
char_type buf[]; // Array size determination explained later.
|
||||||
constexpr to_string_t() {} // Converts the integer to a string stored in buf.
|
constexpr to_string_t() {} // Converts the integer to a string stored in buf.
|
||||||
constexpr operator char_type *() {} // These allow for the object to be implicitly converted
|
constexpr operator char_type *() {} // These allow for the object to be implicitly converted
|
||||||
constexpr operator const char_type *() {} // to a character pointer.
|
constexpr operator const char_type *() {} // to a character pointer.
|
||||||
|
90
f_to_string.hpp
Normal file
90
f_to_string.hpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* f_to_string.hpp - Provides compile-time floating-point-to-string conversion.
|
||||||
|
* Written by Clyne Sullivan.
|
||||||
|
* https://github.com/tcsullivan/constexpr-to-string
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef TCSULLIVAN_F_TO_STRING_HPP_
|
||||||
|
#define TCSULLIVAN_F_TO_STRING_HPP_
|
||||||
|
|
||||||
|
struct f_to_string_double_wrapper {
|
||||||
|
long long int whole = 0;
|
||||||
|
long long int frac = 0;
|
||||||
|
|
||||||
|
constexpr f_to_string_double_wrapper(double v, int prec = 5) {
|
||||||
|
whole = static_cast<long long int>(v);
|
||||||
|
v -= whole;
|
||||||
|
for (int i = 0; i < prec; i++)
|
||||||
|
v *= 10;
|
||||||
|
frac = static_cast<long long int>(v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @struct f_to_string_t
|
||||||
|
* @brief Provides the ability to convert a floating-point number to a string at compile-time.
|
||||||
|
* @tparam N Number to convert
|
||||||
|
*/
|
||||||
|
template<f_to_string_double_wrapper N, typename char_type>
|
||||||
|
class f_to_string_t {
|
||||||
|
char_type buf[([]() constexpr noexcept {
|
||||||
|
unsigned int len = 2;
|
||||||
|
if (N.whole <= 0) len++;
|
||||||
|
for (auto n = N.whole; n; len++, n /= 10);
|
||||||
|
if (N.frac == 0 || (N.whole == 0 && N.frac < 0)) len++;
|
||||||
|
for (auto n = N.frac; n; len++, n /= 10);
|
||||||
|
return len;
|
||||||
|
}())] = {};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructs the object, filling `buf` with the string representation of N.
|
||||||
|
*/
|
||||||
|
constexpr f_to_string_t() noexcept {
|
||||||
|
auto append = [](auto V, auto& ptr) {
|
||||||
|
if (V != 0) {
|
||||||
|
for (auto n = V; n; n /= 10)
|
||||||
|
*--ptr = (V < 0 ? -1 : 1) * (n % 10) + '0';
|
||||||
|
} else {
|
||||||
|
*--ptr = '0';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto ptr = end();
|
||||||
|
*--ptr = '\0';
|
||||||
|
append(N.frac, ptr);
|
||||||
|
*--ptr = '.';
|
||||||
|
append(N.whole, ptr);
|
||||||
|
if (N.frac < 0 || N.whole < 0)
|
||||||
|
*--ptr = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support implicit casting to `char *` or `const char *`.
|
||||||
|
constexpr operator char_type *() noexcept { return buf; }
|
||||||
|
constexpr operator const char_type *() const noexcept { return buf; }
|
||||||
|
|
||||||
|
constexpr auto size() const noexcept { return sizeof(buf) / sizeof(buf[0]); }
|
||||||
|
// Element access
|
||||||
|
constexpr auto data() noexcept { return buf; }
|
||||||
|
constexpr const auto data() const noexcept { return buf; }
|
||||||
|
constexpr auto& operator[](unsigned int i) noexcept { return buf[i]; }
|
||||||
|
constexpr const auto& operator[](unsigned int i) const noexcept { return buf[i]; }
|
||||||
|
constexpr auto& front() noexcept { return buf[0]; }
|
||||||
|
constexpr const auto& front() const noexcept { return buf[0]; }
|
||||||
|
constexpr auto& back() noexcept { return buf[size() - 1]; }
|
||||||
|
constexpr const auto& back() const noexcept { return buf[size() - 1]; }
|
||||||
|
// Iterators
|
||||||
|
constexpr auto begin() noexcept { return buf; }
|
||||||
|
constexpr const auto begin() const noexcept { return buf; }
|
||||||
|
constexpr auto end() noexcept { return buf + size(); }
|
||||||
|
constexpr const auto end() const noexcept { return buf + size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplifies use of `f_to_string_t` from `f_to_string_t<N>()` to `f_to_string<N>`.
|
||||||
|
*/
|
||||||
|
template<f_to_string_double_wrapper N, typename char_type = char>
|
||||||
|
constexpr f_to_string_t<N, char_type> f_to_string;
|
||||||
|
|
||||||
|
#endif // TCSULLIVAN_F_TO_STRING_HPP_
|
||||||
|
|
@ -18,27 +18,28 @@ constexpr auto digit_count = sizeof(digits) / sizeof(digits[0]);
|
|||||||
* @tparam N Number to convert
|
* @tparam N Number to convert
|
||||||
* @tparam base Desired base, can be from 2 to 36
|
* @tparam base Desired base, can be from 2 to 36
|
||||||
*/
|
*/
|
||||||
template<auto N, unsigned int base, typename char_type,
|
template<auto N, int base, typename char_type,
|
||||||
std::enable_if_t<std::is_integral_v<decltype(N)>, int> = 0,
|
std::enable_if_t<std::is_integral_v<decltype(N)>, int> = 0,
|
||||||
std::enable_if_t<(base > 1 && base < digit_count), int> = 0>
|
std::enable_if_t<(base > 1 && base < digit_count), int> = 0>
|
||||||
struct to_string_t {
|
class to_string_t {
|
||||||
// The lambda calculates what the string length of N will be, so that `buf`
|
// The lambda calculates what the string length of N will be, so that `buf`
|
||||||
// fits to the number perfectly.
|
// fits to the number perfectly.
|
||||||
char_type buf[([] {
|
char_type buf[([]() constexpr noexcept {
|
||||||
unsigned int len = N > 0 ? 1 : 2;
|
unsigned int len = N > 0 ? 1 : 2;
|
||||||
for (auto n = N < 0 ? -N : N; n; len++, n /= base);
|
for (auto n = N; n; len++, n /= base);
|
||||||
return len;
|
return len;
|
||||||
}())] = {};
|
}())] = {};
|
||||||
|
|
||||||
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructs the object, filling `buf` with the string representation of N.
|
* Constructs the object, filling `buf` with the string representation of N.
|
||||||
*/
|
*/
|
||||||
constexpr to_string_t() noexcept {
|
constexpr to_string_t() noexcept {
|
||||||
|
auto ptr = end();
|
||||||
|
*--ptr = '\0';
|
||||||
if (N != 0) {
|
if (N != 0) {
|
||||||
auto ptr = buf + sizeof(buf) / sizeof(buf[0]);
|
for (auto n = N; n; n /= base)
|
||||||
*--ptr = '\0';
|
*--ptr = digits[(N < 0 ? -1 : 1) * (n % base)];
|
||||||
for (auto n = N < 0 ? -N : N; n; n /= base)
|
|
||||||
*--ptr = digits[n % base];
|
|
||||||
if (N < 0)
|
if (N < 0)
|
||||||
*--ptr = '-';
|
*--ptr = '-';
|
||||||
} else {
|
} else {
|
||||||
@ -47,20 +48,30 @@ struct to_string_t {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Support implicit casting to `char *` or `const char *`.
|
// Support implicit casting to `char *` or `const char *`.
|
||||||
constexpr operator char_type *() { return buf; }
|
constexpr operator char_type *() noexcept { return buf; }
|
||||||
constexpr operator const char_type *() const { return buf; }
|
constexpr operator const char_type *() const noexcept { return buf; }
|
||||||
|
|
||||||
// Support range-based for loops
|
constexpr auto size() const noexcept { return sizeof(buf) / sizeof(buf[0]); }
|
||||||
constexpr auto begin() { return buf; }
|
// Element access
|
||||||
constexpr auto begin() const { return buf; }
|
constexpr auto data() noexcept { return buf; }
|
||||||
constexpr auto end() { return buf + sizeof(buf) / sizeof(buf[0]); }
|
constexpr const auto data() const noexcept { return buf; }
|
||||||
constexpr auto end() const { return buf + sizeof(buf) / sizeof(buf[0]); }
|
constexpr auto& operator[](unsigned int i) noexcept { return buf[i]; }
|
||||||
|
constexpr const auto& operator[](unsigned int i) const noexcept { return buf[i]; }
|
||||||
|
constexpr auto& front() noexcept { return buf[0]; }
|
||||||
|
constexpr const auto& front() const noexcept { return buf[0]; }
|
||||||
|
constexpr auto& back() noexcept { return buf[size() - 1]; }
|
||||||
|
constexpr const auto& back() const noexcept { return buf[size() - 1]; }
|
||||||
|
// Iterators
|
||||||
|
constexpr auto begin() noexcept { return buf; }
|
||||||
|
constexpr const auto begin() const noexcept { return buf; }
|
||||||
|
constexpr auto end() noexcept { return buf + size(); }
|
||||||
|
constexpr const auto end() const noexcept { return buf + size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simplifies use of `to_string_t` from `to_string_t<N>()` to `to_string<N>`.
|
* Simplifies use of `to_string_t` from `to_string_t<N>()` to `to_string<N>`.
|
||||||
*/
|
*/
|
||||||
template<auto N, unsigned int base = 10, typename char_type = char>
|
template<auto N, int base = 10, typename char_type = char>
|
||||||
constexpr to_string_t<N, base, char_type> to_string;
|
constexpr to_string_t<N, base, char_type> to_string;
|
||||||
|
|
||||||
#endif // TCSULLIVAN_TO_STRING_HPP_
|
#endif // TCSULLIVAN_TO_STRING_HPP_
|
||||||
|
Loading…
x
Reference in New Issue
Block a user