22 Commits

Author SHA1 Message Date
ec70a5bba1 Merge branch 'master' into feature/no_std_lib 2022-02-19 19:21:34 +01:00
1263dfee98 Merge pull request 'Added cmake-build-relwithdebinfo to gitignore' (#8) from fix/gitignore_builddir into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/8
2022-02-19 18:18:51 +00:00
52992e255e Added cmake-build-relwithdebinfo to gitignore 2022-02-19 19:13:54 +01:00
8526ae1d91 First version of std::array 2022-02-18 23:31:15 +01:00
b8f59710d3 Merge pull request 'Updated README.md' (#7) from fix/README_wording into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/7
2022-02-15 13:54:04 +00:00
184815c75b Updated README.md 2022-02-15 13:53:47 +00:00
74c4a4d582 Merge pull request 'feature/int_hex' (#6) from feature/int_hex into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/6
2022-02-15 10:35:17 +00:00
2933676ea2 Fixed bug in hex formatting, wrote tests for hex int formatting 2022-02-15 11:34:49 +01:00
7115e09aca Implemented count_digits for hex and started implementing digits2_base 2022-02-15 11:13:15 +01:00
f9099ce6ee Added tests for count_digits_base, decimal, binary and hex 2022-02-15 11:12:19 +01:00
4c3024e978 Merge pull request 'Reintroduced previously erroneously removed memcpy' (#5) from fix/memcpy into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/5
2022-02-15 09:29:55 +00:00
a921676b77 Reintroduced previously erroneously removed memcpy 2022-02-15 10:29:23 +01:00
3b9d9775bf Merge pull request 'Replaced <cstdint> with <stdint.h>' (#4) from fix/stdint into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/4
2022-02-15 09:17:58 +00:00
39c1636858 Replaced <cstdint> with <stdint.h> 2022-02-15 10:01:55 +01:00
fc080a8ba0 Merge pull request 'Removed memcpy and <cstring>' (#3) from feature/no_cstring into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/3
2022-02-15 08:59:58 +00:00
e4ebd0163b Removed memcpy and <cstring> 2022-02-15 09:58:47 +01:00
1d2dab93bd Merge pull request 'feature/int_binary' (#2) from feature/int_float_binary into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/2
2022-02-15 08:50:35 +00:00
5e4f242796 Renamed functions dedicated to decimal to better reflect their function 2022-02-15 09:47:28 +01:00
fa0fef37db Rewrote format_impl.h functions to work with both decimal and binary 2022-02-15 09:37:41 +01:00
c3eb1e2909 Wrote tests for binary int formatting 2022-02-15 09:35:36 +01:00
313ca5e981 Merge pull request 'Added static_assert to check for string validity' (#1) from feature/compile_time_string_validity into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/1
2022-02-14 22:25:16 +00:00
8241045158 Added static_assert to check for string validity 2022-02-14 22:36:43 +01:00
11 changed files with 351 additions and 57 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
build
cmake-build-debug
cmake-build-release
cmake-build-relwithdebinfo
.idea

View File

@@ -11,8 +11,7 @@ During compile-time, the string to be formatted is preprocessed to the point onl
have to be written (If they are not available at compile time).
For example `One number: {:03}; And another one: {:05.3}` is preprocessed into `One number: 000; And another one: 00.000`.
This is returned as a `std::array<char, N>`, where `N` is automatically evaluated. The only code executed at compile
time then formats the numbers and writes them into their place in the array.
This is returned as a `std::array<char, N>`, where `N` is automatically evaluated. The only code executed at runtime then formats the numbers and writes them into their places in the array.
Disclaimer: The actual formatting code is largely shamelessly stolen from `fmtlib`.
@@ -48,8 +47,11 @@ $ ctest --test-dir build/
## Limitations
For the compile time preprocessing of format strings non non-type template parameters are heavily relied upon,
which means C++20 is required.
Only a relatively limited subset of the `fmtlib` syntax is recognized (for now anyway). In particular,
there is no support for positional arguments, alignment, chrono format specs and custom const_format specifications.
float formatting in decimal and integer formatting in decimal, binary and hexadecimal are supported.
By nature of the library design, which forces compile-time preprocessing of the const_format string, no dynamic width or
dynamic precision can be implemented.

View File

@@ -2,8 +2,6 @@
#define LOGGER_FORMAT_H
#include <cstring>
#include "format_impl.h"
#include "parse.h"
#include "utility.h"
@@ -42,12 +40,12 @@ constexpr inline void check_fmt_params() {
template <fmt_data_t fmt_data, std::integral arg_t>
constexpr inline void format_arg(char* dest, arg_t arg) {
const_fmt_detail::format_int<arg_t, fmt_data>(dest, arg);
const_fmt_detail::format_int<fmt_data>(dest, arg);
};
template <fmt_data_t fmt_data, std::floating_point arg_t>
constexpr inline void format_arg(char* dest, arg_t arg){
const_fmt_detail::format_float<arg_t, fmt_data>(dest, arg);
const_fmt_detail::format_float<fmt_data>(dest, arg);
};
// TODO: Error handling
@@ -115,10 +113,12 @@ namespace const_fmt {
template <const_fmt_detail::ConstString s, typename... args_t>
constexpr inline auto const_format(args_t... args) {
constexpr auto ast = const_fmt_detail::parse_string<s>().value;
constexpr auto fmt_data = const_fmt_detail::get_fmt_data<ast>();
constexpr auto ast = const_fmt_detail::parse_string<s>();
constexpr auto fmt_data = const_fmt_detail::get_fmt_data<ast.value>();
auto result = const_fmt_detail::get_preproc_string<ast>();
static_assert(ast.is_valid, "Invalid format string");
auto result = const_fmt_detail::get_preproc_string<ast.value>();
const_fmt_detail::format_args<fmt_data>(result.begin(), args...);

View File

@@ -10,8 +10,7 @@
*
*/
#include <cstdint>
#include <stdint.h>
#include "utility.h"
@@ -32,7 +31,7 @@ namespace const_fmt { namespace const_fmt_detail {
(factor)*1000000000
template <typename T>
constexpr int count_digits_fallback(T n) {
constexpr int count_digits_decimal_fallback(T n) {
int count = 1;
for (;;) {
if (n < 10) return count;
@@ -44,7 +43,7 @@ constexpr int count_digits_fallback(T n) {
}
}
inline int do_count_digits(uint64_t n) {
inline int do_count_digits_decimal(uint64_t n) {
// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
static constexpr uint8_t bsr2log10[] = {
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
@@ -58,22 +57,63 @@ inline int do_count_digits(uint64_t n) {
return t - (n < zero_or_powers_of_10[t]);
}
constexpr inline auto count_digits(uint64_t n) -> int {
if (!std::is_constant_evaluated()) {
return do_count_digits(n);
template <FormatType t_format_type>
constexpr inline auto count_digits_base(uint64_t n) -> int {
if constexpr (t_format_type == FormatType::b) {
int result = 0;
while (n) {
n = n >> 1;
result += 1;
}
return result;
} else {
if constexpr (t_format_type == FormatType::x) {
int result = 0;
while (n) {
n = n >> 4;
result += 1;
}
return (result + count_digits_base<FormatType::b>(n));
} else {
if (!std::is_constant_evaluated()) {
return do_count_digits_decimal(n);
}
return count_digits_decimal_fallback(n);
}
}
return count_digits_fallback(n);
}
// Converts value in the range [0, 100) to a string.
constexpr inline const char* digits2(size_t value) {
// Converts value in the range [0, base^2) to a string.
template <FormatType t_format_type>
constexpr inline const char* digits2_base(size_t value) {
// GCC generates slightly better code when value is pointer-size.
return &"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"[value * 2];
if constexpr (t_format_type == FormatType::b) {
return &"00011011"[value * 2];
} else {
if constexpr (t_format_type == FormatType::x) {
// clang-format off
return &"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"
"202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F"
"404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"
"606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F"
"808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F"
"A0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
"E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"[value * 2];
// clang-format on
} else {
return &"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"[value * 2];
}
}
}
@@ -86,9 +126,24 @@ constexpr inline void copy2(char* dst, const char* src) {
*dst = static_cast<char>(*src);
}
template <typename uint_t>
constexpr inline void format_decimal(char* out, uint_t value, int n_digits,
int size) {
template <FormatType t_format_type>
consteval inline unsigned get_base_divisor() {
switch (t_format_type) {
case FormatType::b:
return 2;
case FormatType::x:
return 16;
default:
return 10;
}
}
template <FormatType t_format_type, typename uint_t>
constexpr inline void format_base(char* out, uint_t value, int n_digits,
int size) {
constexpr unsigned divisor = get_base_divisor<t_format_type>();
constexpr unsigned square_divisor = const_pow(divisor, 2);
if (n_digits > size) {
for (int i = 0; i < size; ++i) {
*(out++) = 'f';
@@ -97,21 +152,23 @@ constexpr inline void format_decimal(char* out, uint_t value, int n_digits,
}
out += size;
while (value >= 100) {
while (value >= square_divisor) {
out -= 2;
copy2(out, digits2(static_cast<size_t>(value % 100)));
value /= 100;
copy2(out, digits2_base<t_format_type>(
static_cast<size_t>(value % square_divisor)));
value /= square_divisor;
}
if (value < 10) {
*--out = static_cast<char>('0' + value);
if (value < divisor) {
*--out = digits2_base<t_format_type>(value*divisor)[0];
return;
}
out -= 2;
copy2(out, digits2(static_cast<size_t>(value)));
copy2(out, digits2_base<t_format_type>(static_cast<size_t>(value)));
}
// returns {abs_value, was_negative}
template <std::signed_integral int_t>
constexpr std::pair<typename std::make_unsigned<int_t>::type, bool>
@@ -139,19 +196,22 @@ constexpr std::pair<int_t, bool> get_abs_value(int_t value) {
*/
template <std::unsigned_integral uint_t, fmt_data_t t_fmt_node>
template <fmt_data_t t_fmt_node, std::unsigned_integral uint_t>
constexpr inline void format_int(char* out, uint_t value) {
format_decimal(out, value, count_digits(value), t_fmt_node.length);
format_base<t_fmt_node.type>(out, value,
count_digits_base<t_fmt_node.type>(value),
t_fmt_node.length);
}
template <std::signed_integral int_t, fmt_data_t t_fmt_node>
template <fmt_data_t t_fmt_node, std::signed_integral int_t>
constexpr inline void format_int(char* out, int_t value) {
const auto [abs_value, negative] = get_abs_value(value);
const std::size_t n_digits = count_digits(abs_value);
const std::size_t n_digits = count_digits_base<t_fmt_node.type>(abs_value);
format_decimal(out + 1 * (negative), abs_value, n_digits,
t_fmt_node.length - 1 * (negative));
format_base<t_fmt_node.type>(out + 1 * (negative), abs_value, n_digits,
t_fmt_node.length - 1 * (negative));
if constexpr (t_fmt_node.has_zero_padding) {
if (negative) *(out) = '-';
@@ -169,21 +229,21 @@ constexpr inline void format_int(char* out, int_t value) {
*/
template <std::floating_point float_t, fmt_data_t t_fmt_node>
template <fmt_data_t t_fmt_node, std::floating_point float_t>
constexpr inline void format_float(char* out, float_t value) {
// clang-format off
constexpr fmt_data_t fmt_node_integral = {
t_fmt_node.has_zero_padding, // has_zero_padding
t_fmt_node.length - t_fmt_node.precision - 1, // length
t_fmt_node.precision, // ignored
t_fmt_node.type, // ignored
FormatType::d, // type
t_fmt_node.position // ignored
};
constexpr fmt_data_t fmt_node_fractional = {
true, // has_zero_padding
t_fmt_node.precision, // length
t_fmt_node.precision, // ignored
t_fmt_node.type, // ignored
FormatType::d, // type
t_fmt_node.position // ignored
};
// clang-format on
@@ -198,8 +258,8 @@ constexpr inline void format_float(char* out, float_t value) {
const auto [fractional_abs, fractional_negative] =
get_abs_value(fractional);
format_int<int, fmt_node_integral>(out, integral);
format_int<uint16_t, fmt_node_fractional>(
format_int<fmt_node_integral, int>(out, integral);
format_int<fmt_node_fractional, uint16_t>(
out + t_fmt_node.length - t_fmt_node.precision, fractional_abs);
}

71
const_fmt/std_lib.h Normal file
View File

@@ -0,0 +1,71 @@
#ifndef CONST_FMT_STD_LIB_H
#define CONST_FMT_STD_LIB_H
#ifdef CONST_FMT_NO_STD_LIB
#include <stdint.h>
namespace std {
using size_t = uint16_t;
// TODO: Is std::size_t really the best bet here?
template <typename data_t, std::size_t t_size>
class array {
public:
// Constructors
array() = default;
array(const array& other) = default;
array(array&& other) = default;
// Operators
array operator=(const array& other) = default;
array operator=(array&& other) = default;
// Element access
data_t& operator[](std::size_t index) {
return m_data[index];
}
const data_t& operator[](std::size_t index) const {
return m_data[index];
}
// Iterators
using iterator = data_t*;
using const_iterator = const data_t*;
iterator begin() {
return &(m_data[0]);
}
iterator end() {
return (&(m_data[t_size-1]) + 1);
}
const iterator cbegin() const {
return &(m_data[0]);
}
const iterator cend() const {
return (&(m_data[t_size-1]) + 1);
}
private:
data_t m_data[t_size];
};
} // namespace std
#endif
#endif // CONST_FMT_STD_LIB_H

View File

@@ -5,7 +5,7 @@
using namespace const_fmt;
int main() {
constexpr auto s = "This is an integer: {:08}, and this is a float: {:09.4}"_const_fmt(12345, -86.2);
constexpr auto s = "This is an integer: {:08x}, and this is a float: {:09.4b}"_const_fmt(122u, -86.2);
// Convert s (with a type of 'std::array<char, N>') into something
// writable to std::cout

View File

@@ -25,5 +25,8 @@ endmacro()
package_add_test(utility_test src/utility.cpp)
package_add_test(parse_test src/parse.cpp)
package_add_test(format_test src/format.cpp)
package_add_test(format_utility_test src/format_utility.cpp)
package_add_test(format_decimal_test src/format_decimal.cpp)
package_add_test(format_binary_test src/format_binary.cpp)
package_add_test(format_hex_test src/format_hex.cpp)

View File

@@ -0,0 +1,59 @@
#include <const_fmt/format.h>
#include <gtest/gtest.h>
using namespace const_fmt;
using namespace const_fmt::const_fmt_detail;
TEST(FormatBinary, positive_int) {
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
'0', '0', '1', '0'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08b}">(0b10);
constexpr std::array<char, 8> control2 = {' ', ' ', '1', '0',
'1', '0', '1', '0'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8b}">(0b101010);
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1',
'1', '0', '0', '1'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4b}">(0b11001);
constexpr std::array<char, 4> control4 = {'1', '0', '1', '1'};
constexpr std::array<char, 4> formatted4 = const_format<"{:4b}">(0b1011);
constexpr std::array<char, 4> control5 = {'f', 'f', 'f', 'f'};
constexpr std::array<char, 4> formatted5 = const_format<"{:4b}">(0b10011);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}
TEST(FormatBinary, negative_int) {
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
'0', '0', '1', '0'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08b}">(-0b10);
constexpr std::array<char, 8> control2 = {' ', '-', '1', '0',
'1', '0', '1', '0'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8b}">(-0b101010);
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1',
'0', '0', '1', '1'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4b}">(-0b10011);
constexpr std::array<char, 5> control4 = {'-', '1', '1', '0', '1'};
constexpr std::array<char, 5> formatted4 = const_format<"{:5b}">(-0b1101);
constexpr std::array<char, 5> control5 = {'-', 'f', 'f', 'f', 'f'};
constexpr std::array<char, 5> formatted5 = const_format<"{:05b}">(-0b10101);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}

View File

@@ -6,7 +6,7 @@ using namespace const_fmt;
using namespace const_fmt::const_fmt_detail;
TEST(Format, positive_int) {
TEST(FormatDecimal, positive_int) {
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
'0', '0', '0', '2'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(2);
@@ -32,7 +32,7 @@ TEST(Format, positive_int) {
EXPECT_EQ(control5, formatted5);
}
TEST(Format, negative_int) {
TEST(FormatDecimal, negative_int) {
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
'0', '0', '0', '2'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(-2);
@@ -58,7 +58,7 @@ TEST(Format, negative_int) {
EXPECT_EQ(control5, formatted5);
}
TEST(Format, positive_float) {
TEST(FormatDecimal, positive_float) {
constexpr std::array<char, 8> control1 = {'0', '0', '2', '.',
'3', '4', '5', '6'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08.4}">(2.3456);
@@ -82,7 +82,7 @@ TEST(Format, positive_float) {
EXPECT_EQ(control4, formatted4);
}
TEST(Format, negative_float) {
TEST(FormatDecimal, negative_float) {
constexpr std::array<char, 8> control1 = {'-', '0', '2', '.',
'3', '4', '5', '6'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08.4}">(-2.3456);
@@ -100,9 +100,4 @@ TEST(Format, negative_float) {
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
}
//
// TEST(Format, string) {
// // TODO
// EXPECT_EQ(true, false);
//}
}

59
test/src/format_hex.cpp Normal file
View File

@@ -0,0 +1,59 @@
#include <const_fmt/format.h>
#include <gtest/gtest.h>
using namespace const_fmt;
using namespace const_fmt::const_fmt_detail;
TEST(FormatHex, positive_int) {
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
'0', '0', '1', '0'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08x}">(0x10);
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', 'F',
'F', 'A', '7', '6'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8x}">(0xffa76);
constexpr std::array<char, 8> control3 = {'0', '0', '0', '0',
'B', 'C', 'E', 'F'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4x}">(0xbcef);
constexpr std::array<char, 4> control4 = {'A', 'D', '0', '1'};
constexpr std::array<char, 4> formatted4 = const_format<"{:4x}">(0xad01);
constexpr std::array<char, 4> control5 = {'f', 'f', 'f', 'f'};
constexpr std::array<char, 4> formatted5 = const_format<"{:4x}">(0x12345);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}
TEST(FormatHex, negative_int) {
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
'0', '0', '1', '0'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08x}">(-0x10);
constexpr std::array<char, 8> control2 = {' ', ' ', '-', 'F',
'F', 'A', '7', '6'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8x}">(-0xffa76);
constexpr std::array<char, 8> control3 = {'-', '0', '0', '0',
'B', 'C', 'E', 'F'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4x}">(-0xbcef);
constexpr std::array<char, 4> control4 = {'-', 'A', 'D', '1'};
constexpr std::array<char, 4> formatted4 = const_format<"{:4x}">(-0xad1);
constexpr std::array<char, 4> control5 = {'-', 'f', 'f', 'f'};
constexpr std::array<char, 4> formatted5 = const_format<"{:04x}">(-0x1234);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}

View File

@@ -0,0 +1,44 @@
#include <const_fmt/format_impl.h>
#include <limits>
#include <gtest/gtest.h>
using namespace const_fmt;
using namespace const_fmt::const_fmt_detail;
TEST(FormatUtility, count_digits_base_decimal) {
constexpr unsigned length1 = count_digits_base<FormatType::d>(123);
constexpr unsigned length2 = count_digits_base<FormatType::d>(std::numeric_limits<uint64_t>::max());
constexpr unsigned length3 = count_digits_base<FormatType::d>(10000011);
constexpr unsigned length4 = count_digits_base<FormatType::d>(1);
EXPECT_EQ(length1, 3);
EXPECT_EQ(length2, 20);
EXPECT_EQ(length3, 8);
EXPECT_EQ(length4, 1);
}
TEST(FormatUtility, count_digits_base_binary) {
constexpr unsigned length1 = count_digits_base<FormatType::b>(0b1001);
constexpr unsigned length2 = count_digits_base<FormatType::b>(std::numeric_limits<uint64_t>::max());
constexpr unsigned length3 = count_digits_base<FormatType::b>(0b10000001);
constexpr unsigned length4 = count_digits_base<FormatType::b>(0b01);
EXPECT_EQ(length1, 4);
EXPECT_EQ(length2, 64);
EXPECT_EQ(length3, 8);
EXPECT_EQ(length4, 1);
}
TEST(FormatUtility, count_digits_base_hex) {
constexpr unsigned length1 = count_digits_base<FormatType::x>(0x123);
constexpr unsigned length2 = count_digits_base<FormatType::x>(std::numeric_limits<uint64_t>::max());
constexpr unsigned length3 = count_digits_base<FormatType::x>(0x1000000f);
constexpr unsigned length4 = count_digits_base<FormatType::x>(0x01);
EXPECT_EQ(length1, 3);
EXPECT_EQ(length2, 16);
EXPECT_EQ(length3, 8);
EXPECT_EQ(length4, 1);
}