Compare commits
44 Commits
f47a23ffef
...
feature/sm
| Author | SHA1 | Date | |
|---|---|---|---|
| 045363c733 | |||
| 4d05a9a8fa | |||
| ee1153c393 | |||
| b29695e148 | |||
| 86930fd8b0 | |||
| 7796be7cd4 | |||
| 73a50cae0f | |||
| 91398ef23d | |||
| b5ecd73b18 | |||
| 826f3a54b3 | |||
| ea722ec5bf | |||
| 5ba1723295 | |||
| 8e51facd10 | |||
| 9bd1104e5f | |||
| 6ba83fd582 | |||
| 8f7b1cd4e3 | |||
| ec70a5bba1 | |||
| 1263dfee98 | |||
| 52992e255e | |||
| 8526ae1d91 | |||
| b8f59710d3 | |||
| 184815c75b | |||
| 74c4a4d582 | |||
| 2933676ea2 | |||
| 7115e09aca | |||
| f9099ce6ee | |||
| 4c3024e978 | |||
| a921676b77 | |||
| 3b9d9775bf | |||
| 39c1636858 | |||
| fc080a8ba0 | |||
| e4ebd0163b | |||
| 1d2dab93bd | |||
| 5e4f242796 | |||
| fa0fef37db | |||
| c3eb1e2909 | |||
| 313ca5e981 | |||
| 8241045158 | |||
| 6666a25562 | |||
| 008c71381c | |||
| 9c22b2a084 | |||
| 9bf3e63058 | |||
| 4666b4e737 | |||
| 1338888e0b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
build
|
build
|
||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
cmake-build-release
|
cmake-build-release
|
||||||
|
cmake-build-relwithdebinfo
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
19
LICENSE
Normal file
19
LICENSE
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright 2022 Andreas Tsouchlos
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -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).
|
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`.
|
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
|
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.
|
||||||
time then formats the numbers and writes them into their place in the array.
|
|
||||||
|
|
||||||
Disclaimer: The actual formatting code is largely shamelessly stolen from `fmtlib`.
|
Disclaimer: The actual formatting code is largely shamelessly stolen from `fmtlib`.
|
||||||
|
|
||||||
@@ -48,8 +47,11 @@ $ ctest --test-dir build/
|
|||||||
|
|
||||||
## Limitations
|
## 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,
|
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
|
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.
|
dynamic precision can be implemented.
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
#define LOGGER_FORMAT_H
|
#define LOGGER_FORMAT_H
|
||||||
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "format_impl.h"
|
#include "format_impl.h"
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
@@ -42,12 +40,12 @@ constexpr inline void check_fmt_params() {
|
|||||||
|
|
||||||
template <fmt_data_t fmt_data, std::integral arg_t>
|
template <fmt_data_t fmt_data, std::integral arg_t>
|
||||||
constexpr inline void format_arg(char* dest, arg_t arg) {
|
constexpr inline void format_arg(char* dest, arg_t arg) {
|
||||||
const_fmt_detail::format_int(dest, arg, fmt_data);
|
const_fmt_detail::format_int<fmt_data>(dest, arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <fmt_data_t fmt_data, std::floating_point arg_t>
|
template <fmt_data_t fmt_data, std::floating_point arg_t>
|
||||||
constexpr inline void format_arg(char* dest, arg_t arg){
|
constexpr inline void format_arg(char* dest, arg_t arg){
|
||||||
// const_fmt_detail::format_float(dest, arg, fmt_data);
|
const_fmt_detail::format_float<fmt_data>(dest, arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Error handling
|
// TODO: Error handling
|
||||||
@@ -59,7 +57,7 @@ constexpr inline void format_arg(char* dest, const char* arg) {
|
|||||||
dest = dest + fmt_data.length - len;
|
dest = dest + fmt_data.length - len;
|
||||||
|
|
||||||
if (!std::is_constant_evaluated()) {
|
if (!std::is_constant_evaluated()) {
|
||||||
std::memcpy(dest, arg, len);
|
memcpy(dest, arg, len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (std::size_t i = 0; i < len; ++i) {
|
for (std::size_t i = 0; i < len; ++i) {
|
||||||
@@ -93,7 +91,6 @@ consteval inline auto get_preproc_string() {
|
|||||||
} else {
|
} else {
|
||||||
for (int j = 0; j < ast_node.get_node().length; ++j)
|
for (int j = 0; j < ast_node.get_node().length; ++j)
|
||||||
result[i++] = ast_node.get_node().has_zero_padding ? '0' : ' ';
|
result[i++] = ast_node.get_node().has_zero_padding ? '0' : ' ';
|
||||||
//i += ast_node.get_node().length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,10 +113,12 @@ namespace const_fmt {
|
|||||||
|
|
||||||
template <const_fmt_detail::ConstString s, typename... args_t>
|
template <const_fmt_detail::ConstString s, typename... args_t>
|
||||||
constexpr inline auto const_format(args_t... args) {
|
constexpr inline auto const_format(args_t... args) {
|
||||||
constexpr auto ast = const_fmt_detail::parse_string<s>().value;
|
constexpr auto ast = const_fmt_detail::parse_string<s>();
|
||||||
constexpr auto fmt_data = const_fmt_detail::get_fmt_data<ast>();
|
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...);
|
const_fmt_detail::format_args<fmt_data>(result.begin(), args...);
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include "stdlib.h"
|
||||||
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ namespace const_fmt { namespace const_fmt_detail {
|
|||||||
(factor)*1000000000
|
(factor)*1000000000
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr int count_digits_fallback(T n) {
|
constexpr int count_digits_decimal_fallback(T n) {
|
||||||
int count = 1;
|
int count = 1;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (n < 10) return count;
|
if (n < 10) return count;
|
||||||
@@ -44,7 +44,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)).
|
// Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)).
|
||||||
static constexpr uint8_t bsr2log10[] = {
|
static constexpr uint8_t bsr2log10[] = {
|
||||||
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
|
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
|
||||||
@@ -58,52 +58,135 @@ inline int do_count_digits(uint64_t n) {
|
|||||||
return t - (n < zero_or_powers_of_10[t]);
|
return t - (n < zero_or_powers_of_10[t]);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr inline auto count_digits(uint64_t n) -> int {
|
template <FormatType t_format_type>
|
||||||
if (!std::is_constant_evaluated()) {
|
constexpr inline auto count_digits_base(uint64_t n) -> int {
|
||||||
return do_count_digits(n);
|
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.
|
// Converts value in the range [0, base^2) to a string.
|
||||||
constexpr inline const char* digits2(size_t value) {
|
template <FormatType t_format_type>
|
||||||
|
constexpr inline const char* digits2_base(size_t value) {
|
||||||
// GCC generates slightly better code when value is pointer-size.
|
// GCC generates slightly better code when value is pointer-size.
|
||||||
return &"0001020304050607080910111213141516171819"
|
if constexpr (t_format_type == FormatType::b) {
|
||||||
"2021222324252627282930313233343536373839"
|
return &"00011011"[value * 2];
|
||||||
"4041424344454647484950515253545556575859"
|
} else {
|
||||||
"6061626364656667686970717273747576777879"
|
if constexpr (t_format_type == FormatType::x) {
|
||||||
"8081828384858687888990919293949596979899"[value * 2];
|
// 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
constexpr inline void copy2(char* dst, const char* src) {
|
constexpr inline void copy2(char* dst, const char* src) {
|
||||||
if (!std::is_constant_evaluated()) {
|
if (!std::is_constant_evaluated()) {
|
||||||
std::memcpy(dst, src, 2);
|
memcpy(dst, src, 2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*dst++ = static_cast<char>(*src++);
|
*dst++ = static_cast<char>(*src++);
|
||||||
*dst = static_cast<char>(*src);
|
*dst = static_cast<char>(*src);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename uint_t>
|
template <FormatType t_format_type>
|
||||||
constexpr inline void format_decimal(char* out, uint_t value, int size) {
|
consteval inline unsigned get_base_divisor() {
|
||||||
if (count_digits(value) > size) return;
|
switch (t_format_type) {
|
||||||
|
case FormatType::b:
|
||||||
|
return 2;
|
||||||
|
case FormatType::x:
|
||||||
|
return 16;
|
||||||
|
default:
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out += size;
|
template <FormatType t_format_type, typename uint_t>
|
||||||
while (value >= 100) {
|
constexpr inline void format_base(char* out, uint_t value, int n_digits,
|
||||||
out -= 2;
|
int size) {
|
||||||
copy2(out, digits2(static_cast<size_t>(value % 100)));
|
constexpr unsigned divisor = get_base_divisor<t_format_type>();
|
||||||
value /= 100;
|
constexpr unsigned square_divisor = const_pow(divisor, 2);
|
||||||
|
|
||||||
|
if (n_digits > size) {
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
*(out++) = 'f';
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value < 10) {
|
out += size;
|
||||||
*--out = static_cast<char>('0' + value);
|
while (value >= square_divisor) {
|
||||||
|
out -= 2;
|
||||||
|
copy2(out, digits2_base<t_format_type>(
|
||||||
|
static_cast<size_t>(value % square_divisor)));
|
||||||
|
value /= square_divisor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < divisor) {
|
||||||
|
*--out = digits2_base<t_format_type>(value*divisor)[0];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
out -= 2;
|
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>
|
||||||
|
get_abs_value(int_t value) {
|
||||||
|
using uint_t = typename std::make_unsigned<int_t>::type;
|
||||||
|
|
||||||
|
uint_t abs_value = static_cast<uint_t>(value);
|
||||||
|
|
||||||
|
const bool negative = value < 0;
|
||||||
|
if (negative) abs_value = 0 - abs_value;
|
||||||
|
|
||||||
|
return {abs_value, negative};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::unsigned_integral int_t>
|
||||||
|
constexpr std::pair<int_t, bool> get_abs_value(int_t value) {
|
||||||
|
return {value, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -114,21 +197,29 @@ constexpr inline void format_decimal(char* out, uint_t value, int size) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
template <std::unsigned_integral uint_t>
|
template <fmt_data_t t_fmt_node, std::unsigned_integral uint_t>
|
||||||
constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
|
constexpr inline void format_int(char* out, uint_t value) {
|
||||||
format_decimal(out, value, 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 uint_t>
|
|
||||||
constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
|
|
||||||
auto abs_value = static_cast<uint64_t>(value);
|
|
||||||
const bool negative = value < 0;
|
|
||||||
|
|
||||||
if (negative) abs_value = 0 - abs_value;
|
template <fmt_data_t t_fmt_node, std::signed_integral int_t>
|
||||||
format_decimal(out + 1 * (negative), abs_value,
|
constexpr inline void format_int(char* out, int_t value) {
|
||||||
fmt_node.length - 1 * (negative));
|
const auto [abs_value, negative] = get_abs_value(value);
|
||||||
|
|
||||||
if (negative) *out = '-';
|
const std::size_t n_digits = count_digits_base<t_fmt_node.type>(abs_value);
|
||||||
|
|
||||||
|
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) = '-';
|
||||||
|
} else {
|
||||||
|
if (n_digits < t_fmt_node.length)
|
||||||
|
if (negative) *(out + t_fmt_node.length - n_digits - 1) = '-';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -139,12 +230,38 @@ constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
template <std::floating_point float_t>
|
template <fmt_data_t t_fmt_node, std::floating_point float_t>
|
||||||
constexpr inline void format_float(char* out, float_t value,
|
constexpr inline void format_float(char* out, float_t value) {
|
||||||
fmt_data_t fmt_data) {
|
// 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
|
||||||
|
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
|
||||||
|
FormatType::d, // type
|
||||||
|
t_fmt_node.position // ignored
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
*(out) = 'f';
|
*(out + t_fmt_node.length - t_fmt_node.precision - 1) = '.';
|
||||||
*(out + fmt_data.length - fmt_data.precision - 1) = '.';
|
|
||||||
|
const int integral = static_cast<int>(value);
|
||||||
|
|
||||||
|
constexpr std::size_t factor = const_pow(10, t_fmt_node.precision);
|
||||||
|
const int fractional = static_cast<int>((value - integral) * factor);
|
||||||
|
|
||||||
|
const auto [fractional_abs, fractional_negative] =
|
||||||
|
get_abs_value(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
// clang-const_format off
|
// clang-format off
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// clang-const_format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
namespace const_fmt { namespace const_fmt_detail {
|
namespace const_fmt { namespace const_fmt_detail {
|
||||||
|
|||||||
300
const_fmt/stdlib.h
Normal file
300
const_fmt/stdlib.h
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
#ifndef CONST_FMT_STDLIB_H
|
||||||
|
#define CONST_FMT_STDLIB_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Disclaimer: Very bad (partially at least) implementation of some features of
|
||||||
|
* the C++ Standard Library. Not meant as a full-on stdlib implementation, only
|
||||||
|
* for usage in this project (Underlined by the fact, that in this case the
|
||||||
|
* namespace std is actually const_fmt::std)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CONST_FMT_NO_CPP_STDLIB
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace const_fmt { namespace std {
|
||||||
|
|
||||||
|
|
||||||
|
using size_t = uint16_t;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* type_traits
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// various
|
||||||
|
|
||||||
|
|
||||||
|
constexpr inline bool is_constant_evaluated() noexcept {
|
||||||
|
return __builtin_is_constant_evaluated();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct true_type {
|
||||||
|
constexpr static bool value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct false_type {
|
||||||
|
constexpr static bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// is_same
|
||||||
|
|
||||||
|
|
||||||
|
template <typename fist_t, typename second_t>
|
||||||
|
struct is_same : public false_type {};
|
||||||
|
|
||||||
|
template <typename type_t>
|
||||||
|
struct is_same<type_t, type_t> : public true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_one_of
|
||||||
|
|
||||||
|
|
||||||
|
template <typename...>
|
||||||
|
struct is_one_of;
|
||||||
|
template <typename type_t>
|
||||||
|
struct is_one_of<type_t> {
|
||||||
|
constexpr static bool value = false;
|
||||||
|
};
|
||||||
|
template <typename type_t, typename first_t, typename... rest_t>
|
||||||
|
struct is_one_of<type_t, first_t, rest_t...> {
|
||||||
|
constexpr static bool value = std::is_same<type_t, first_t>::value ||
|
||||||
|
is_one_of<type_t, rest_t...>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// remove_x
|
||||||
|
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
template <typename type_t> struct remove_cv { typedef type_t type; };
|
||||||
|
template <typename type_t> struct remove_cv<const type_t> { typedef type_t type; };
|
||||||
|
template <typename type_t> struct remove_cv<volatile type_t> { typedef type_t type; };
|
||||||
|
template <typename type_t> struct remove_cv<const volatile type_t> { typedef type_t type; };
|
||||||
|
|
||||||
|
template <typename type_t> struct remove_const { typedef type_t type; };
|
||||||
|
template <typename type_t> struct remove_const<const type_t> { typedef type_t type; };
|
||||||
|
|
||||||
|
template <typename type_t> struct remove_volatile { typedef type_t type; };
|
||||||
|
template <typename type_t> struct remove_volatile<volatile type_t> { typedef type_t type; };
|
||||||
|
|
||||||
|
template <typename type_t> struct remove_reference { using type = type_t; };
|
||||||
|
template <typename type_t> struct remove_reference<type_t &> { using type = type_t; };
|
||||||
|
template <typename type_t> struct remove_reference<type_t &&> { using type = type_t; };
|
||||||
|
|
||||||
|
template <typename type_t> using remove_reference_t = typename std::remove_reference<type_t>::type;
|
||||||
|
template <typename type_t> using remove_cv_t = typename std::remove_cv<type_t>::type;
|
||||||
|
template <typename type_t> using remove_const_t = typename std::remove_const<type_t>::type;
|
||||||
|
template <typename type_t> using remove_volatile_t = typename std::remove_volatile<type_t>::type;
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
|
// is_integral
|
||||||
|
|
||||||
|
|
||||||
|
template <typename type_t>
|
||||||
|
using is_integral =
|
||||||
|
is_one_of<remove_cv_t<type_t>, bool, char, signed char, unsigned char,
|
||||||
|
wchar_t, char16_t, char32_t, short, unsigned short, int,
|
||||||
|
unsigned int, long, unsigned long, long long, unsigned long long>;
|
||||||
|
|
||||||
|
|
||||||
|
// is_signed
|
||||||
|
|
||||||
|
|
||||||
|
template <typename _Tp>
|
||||||
|
using is_signed_integer = is_one_of<remove_cv_t<_Tp>, signed char, signed short,
|
||||||
|
signed int, signed long, signed long long>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// is_floating_point
|
||||||
|
|
||||||
|
|
||||||
|
template <typename type_t>
|
||||||
|
using is_floating_point =
|
||||||
|
is_one_of<remove_cv_t<type_t>, float, double, long double>;
|
||||||
|
|
||||||
|
|
||||||
|
// make_unsigned
|
||||||
|
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
template <typename type_t> struct make_unsigned { using type = type_t; };
|
||||||
|
|
||||||
|
template <> struct make_unsigned<signed char> { using type = char; };
|
||||||
|
template <> struct make_unsigned<unsigned short> { using type = unsigned short; };
|
||||||
|
template <> struct make_unsigned<signed int> { using type = unsigned int; };
|
||||||
|
template <> struct make_unsigned<signed long> { using type = unsigned long; };
|
||||||
|
template <> struct make_unsigned<signed long long> { using type = unsigned long long; };
|
||||||
|
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
|
// value definitions
|
||||||
|
|
||||||
|
template <typename type_t>
|
||||||
|
inline constexpr bool is_integral_v = is_integral<type_t>::value;
|
||||||
|
template <typename type_t>
|
||||||
|
inline constexpr bool is_signed_integer_v = is_signed_integer<type_t>::value;
|
||||||
|
template <typename type_t>
|
||||||
|
inline constexpr bool is_floating_point_v = is_floating_point<type_t>::value;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* concepts
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
template <typename type_t>
|
||||||
|
concept integral = is_integral_v<type_t>;
|
||||||
|
|
||||||
|
template <typename type_t>
|
||||||
|
concept signed_integral = is_signed_integer_v<type_t>;
|
||||||
|
|
||||||
|
template <typename type_t>
|
||||||
|
concept unsigned_integral = integral<type_t> && !signed_integral<type_t>;
|
||||||
|
|
||||||
|
template <typename type_t>
|
||||||
|
concept floating_point = is_floating_point_v<type_t>;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* algorithm
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
template <typename input_t, typename output_t>
|
||||||
|
constexpr inline void copy(const input_t* start, const input_t* end, output_t* dest_start) {
|
||||||
|
auto temp = start;
|
||||||
|
while (temp != end)
|
||||||
|
*(dest_start++) = *(temp++);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* utility
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::remove_reference_t<T>&& move(T&& arg) noexcept {
|
||||||
|
return reinterpret_cast<std::remove_reference_t<T>&&>(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline void swap(T& t1, T& t2) {
|
||||||
|
T temp = std::move(t1);
|
||||||
|
t1 = std::move(t2);
|
||||||
|
t2 = std::move(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename first_t, typename second_t>
|
||||||
|
struct pair {
|
||||||
|
first_t first;
|
||||||
|
second_t second;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* array
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Is std::size_t really the best bet here?
|
||||||
|
template <typename data_t, std::size_t t_size>
|
||||||
|
class array {
|
||||||
|
public:
|
||||||
|
template <typename... args_t>
|
||||||
|
constexpr array(args_t... args) noexcept : m_data{args...} {
|
||||||
|
static_assert(sizeof...(args) == t_size, "Invalid number of arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr array() noexcept = default;
|
||||||
|
constexpr array(const array&) = default;
|
||||||
|
constexpr array(array&&) = default;
|
||||||
|
|
||||||
|
constexpr array& operator=(const array& other) = default;
|
||||||
|
constexpr array& operator=(array&& other) = default;
|
||||||
|
|
||||||
|
constexpr void swap(array<data_t, t_size>& other) noexcept {
|
||||||
|
for (int i = 0; i < t_size; ++i) {
|
||||||
|
using std::swap;
|
||||||
|
swap(m_data[i], other.m_data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr data_t& operator[](std::size_t index) noexcept {
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const data_t& operator[](std::size_t index) const noexcept {
|
||||||
|
return m_data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
using iterator = data_t*;
|
||||||
|
using const_iterator = const data_t*;
|
||||||
|
|
||||||
|
constexpr iterator begin() noexcept {
|
||||||
|
return &(m_data[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr iterator end() noexcept {
|
||||||
|
return (&(m_data[t_size - 1]) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator begin() const noexcept {
|
||||||
|
return &(m_data[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator end() const noexcept {
|
||||||
|
return (&(m_data[t_size - 1]) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator cbegin() const noexcept {
|
||||||
|
return &(m_data[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const_iterator cend() const noexcept {
|
||||||
|
return (&(m_data[t_size - 1]) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_t m_data[t_size];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}} // namespace const_fmt::std
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CONST_FMT_STDLIB_H
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
#define LOGGER_TYPES_H
|
#define LOGGER_TYPES_H
|
||||||
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
|
#include "stdlib.h"
|
||||||
|
|
||||||
|
|
||||||
namespace const_fmt { namespace const_fmt_detail {
|
namespace const_fmt { namespace const_fmt_detail {
|
||||||
@@ -19,8 +20,7 @@ template <std::size_t N>
|
|||||||
class ConstString {
|
class ConstString {
|
||||||
public:
|
public:
|
||||||
constexpr ConstString(const char (&content)[N]) noexcept {
|
constexpr ConstString(const char (&content)[N]) noexcept {
|
||||||
std::copy(std::begin(content), std::end(content),
|
std::copy(&content[0], (&content[N - 1] + 1), m_content.begin());
|
||||||
std::begin(m_content));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char operator[](std::size_t index) const noexcept {
|
constexpr char operator[](std::size_t index) const noexcept {
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
#define LOGGER_UTILITY_H
|
#define LOGGER_UTILITY_H
|
||||||
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "stdlib.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
|
#define CONST_FMT_NO_CPP_STDLIB
|
||||||
|
|
||||||
#include <const_fmt/format.h>
|
#include <const_fmt/format.h>
|
||||||
#include <iostream>
|
//#include <iostream>
|
||||||
#include <string_view>
|
//#include <string_view>
|
||||||
|
|
||||||
using namespace const_fmt;
|
using namespace const_fmt;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
constexpr auto s = "abcdef {:04}"_const_fmt(123);
|
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
|
// Convert s (with a type of 'std::array<char, N>') into something
|
||||||
// writable to std::cout
|
// writable to std::cout
|
||||||
std::string_view sv{&s[0], s.size()};
|
// std::string_view sv{&s[0], s.size()};
|
||||||
|
//
|
||||||
std::cout << sv << std::endl;
|
// std::cout << sv << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -25,5 +25,8 @@ endmacro()
|
|||||||
|
|
||||||
package_add_test(utility_test src/utility.cpp)
|
package_add_test(utility_test src/utility.cpp)
|
||||||
package_add_test(parse_test src/parse.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)
|
||||||
|
|
||||||
|
|||||||
59
test/src/format_binary.cpp
Normal file
59
test/src/format_binary.cpp
Normal 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);
|
||||||
|
}
|
||||||
103
test/src/format_decimal.cpp
Normal file
103
test/src/format_decimal.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#include <const_fmt/format.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace const_fmt;
|
||||||
|
using namespace const_fmt::const_fmt_detail;
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', '2',
|
||||||
|
'2', '2', '2', '2'};
|
||||||
|
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(22222);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1',
|
||||||
|
'2', '3', '4', '5'};
|
||||||
|
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4}">(12345);
|
||||||
|
|
||||||
|
constexpr std::array<char, 4> control4 = {'6', '7', '8', '9'};
|
||||||
|
constexpr std::array<char, 4> formatted4 = const_format<"{:4}">(6789);
|
||||||
|
|
||||||
|
constexpr std::array<char, 4> control5 = {'f', 'f', 'f', 'f'};
|
||||||
|
constexpr std::array<char, 4> formatted5 = const_format<"{:4}">(67895);
|
||||||
|
|
||||||
|
EXPECT_EQ(control1, formatted1);
|
||||||
|
EXPECT_EQ(control2, formatted2);
|
||||||
|
EXPECT_EQ(control3, formatted3);
|
||||||
|
EXPECT_EQ(control4, formatted4);
|
||||||
|
EXPECT_EQ(control5, formatted5);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control2 = {' ', ' ', '-', '2',
|
||||||
|
'2', '2', '2', '2'};
|
||||||
|
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(-22222);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1',
|
||||||
|
'2', '3', '4', '5'};
|
||||||
|
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4}">(-12345);
|
||||||
|
|
||||||
|
constexpr std::array<char, 5> control4 = {'-', '6', '7', '8', '9'};
|
||||||
|
constexpr std::array<char, 5> formatted4 = const_format<"{:5}">(-6789);
|
||||||
|
|
||||||
|
constexpr std::array<char, 5> control5 = {'-', 'f', 'f', 'f', 'f'};
|
||||||
|
constexpr std::array<char, 5> formatted5 = const_format<"{:05}">(-66789);
|
||||||
|
|
||||||
|
EXPECT_EQ(control1, formatted1);
|
||||||
|
EXPECT_EQ(control2, formatted2);
|
||||||
|
EXPECT_EQ(control3, formatted3);
|
||||||
|
EXPECT_EQ(control4, formatted4);
|
||||||
|
EXPECT_EQ(control5, formatted5);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Float error: 2323.2 -> 2323.1
|
||||||
|
constexpr std::array<char, 8> control2 = {' ', ' ', '2', '3',
|
||||||
|
'2', '3', '.', '1'};
|
||||||
|
constexpr std::array<char, 8> formatted2 = const_format<"{:8.1}">(2323.2);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control3 = {'1', '2', '3', '4',
|
||||||
|
'.', '5', '0', '0'};
|
||||||
|
constexpr std::array<char, 8> formatted3 = const_format<"{:08.3}">(1234.5);
|
||||||
|
|
||||||
|
// Float error: .9 -> .8
|
||||||
|
constexpr std::array<char, 4> control4 = {'f', 'f', '.', '8'};
|
||||||
|
constexpr std::array<char, 4> formatted4 = const_format<"{:4.1}">(678.9);
|
||||||
|
|
||||||
|
EXPECT_EQ(control1, formatted1);
|
||||||
|
EXPECT_EQ(control2, formatted2);
|
||||||
|
EXPECT_EQ(control3, formatted3);
|
||||||
|
EXPECT_EQ(control4, formatted4);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Float error: 2323.2 -> 2323.1
|
||||||
|
constexpr std::array<char, 8> control2 = {' ', '-', '2', '3',
|
||||||
|
'2', '3', '.', '1'};
|
||||||
|
constexpr std::array<char, 8> formatted2 = const_format<"{:8.1}">(-2323.2);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control3 = {'-', 'f', 'f', 'f',
|
||||||
|
'.', '5', '0', '0'};
|
||||||
|
constexpr std::array<char, 8> formatted3 = const_format<"{:08.3}">(-1234.5);
|
||||||
|
|
||||||
|
|
||||||
|
EXPECT_EQ(control1, formatted1);
|
||||||
|
EXPECT_EQ(control2, formatted2);
|
||||||
|
EXPECT_EQ(control3, formatted3);
|
||||||
|
}
|
||||||
@@ -6,61 +6,54 @@ using namespace const_fmt;
|
|||||||
using namespace const_fmt::const_fmt_detail;
|
using namespace const_fmt::const_fmt_detail;
|
||||||
|
|
||||||
|
|
||||||
TEST(Format, positive_int) {
|
TEST(FormatHex, positive_int) {
|
||||||
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
|
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
|
||||||
'0', '0', '0', '2'};
|
'0', '0', '1', '0'};
|
||||||
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(2);
|
constexpr std::array<char, 8> formatted1 = const_format<"{:08x}">(0x10);
|
||||||
|
|
||||||
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', '2',
|
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', 'F',
|
||||||
'2', '2', '2', '2'};
|
'F', 'A', '7', '6'};
|
||||||
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(22222);
|
constexpr std::array<char, 8> formatted2 = const_format<"{:8x}">(0xffa76);
|
||||||
|
|
||||||
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1',
|
constexpr std::array<char, 8> control3 = {'0', '0', '0', '0',
|
||||||
'2', '3', '4', '5'};
|
'B', 'C', 'E', 'F'};
|
||||||
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4}">(12345);
|
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4x}">(0xbcef);
|
||||||
|
|
||||||
constexpr std::array<char, 4> control4 = {'6', '7', '8', '9'};
|
constexpr std::array<char, 4> control4 = {'A', 'D', '0', '1'};
|
||||||
constexpr std::array<char, 4> formatted4 = const_format<"{:4}">(6789);
|
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(control1, formatted1);
|
||||||
EXPECT_EQ(control2, formatted2);
|
EXPECT_EQ(control2, formatted2);
|
||||||
EXPECT_EQ(control3, formatted3);
|
EXPECT_EQ(control3, formatted3);
|
||||||
EXPECT_EQ(control4, formatted4);
|
EXPECT_EQ(control4, formatted4);
|
||||||
|
EXPECT_EQ(control5, formatted5);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Format, negative_int) {
|
TEST(FormatHex, negative_int) {
|
||||||
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
|
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
|
||||||
'0', '0', '0', '2'};
|
'0', '0', '1', '0'};
|
||||||
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(-2);
|
constexpr std::array<char, 8> formatted1 = const_format<"{:08x}">(-0x10);
|
||||||
|
|
||||||
constexpr std::array<char, 8> control2 = {' ', ' ', '-', '2',
|
constexpr std::array<char, 8> control2 = {' ', ' ', '-', 'F',
|
||||||
'2', '2', '2', '2'};
|
'F', 'A', '7', '6'};
|
||||||
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(-22222);
|
constexpr std::array<char, 8> formatted2 = const_format<"{:8x}">(-0xffa76);
|
||||||
|
|
||||||
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1',
|
constexpr std::array<char, 8> control3 = {'-', '0', '0', '0',
|
||||||
'2', '3', '4', '5'};
|
'B', 'C', 'E', 'F'};
|
||||||
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4}">(-12345);
|
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4x}">(-0xbcef);
|
||||||
|
|
||||||
constexpr std::array<char, 5> control4 = {'-', '6', '7', '8', '9'};
|
constexpr std::array<char, 4> control4 = {'-', 'A', 'D', '1'};
|
||||||
constexpr std::array<char, 5> formatted4 = const_format<"{:5}">(-6789);
|
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(control1, formatted1);
|
||||||
EXPECT_EQ(control2, formatted2);
|
EXPECT_EQ(control2, formatted2);
|
||||||
EXPECT_EQ(control3, formatted3);
|
EXPECT_EQ(control3, formatted3);
|
||||||
EXPECT_EQ(control4, formatted4);
|
EXPECT_EQ(control4, formatted4);
|
||||||
|
EXPECT_EQ(control5, formatted5);
|
||||||
}
|
}
|
||||||
//
|
|
||||||
//TEST(Format, positive_float) {
|
|
||||||
// // TODO
|
|
||||||
// EXPECT_EQ(true, false);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//TEST(Format, negative_float) {
|
|
||||||
// // TODO
|
|
||||||
// EXPECT_EQ(true, false);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//TEST(Format, string) {
|
|
||||||
// // TODO
|
|
||||||
// EXPECT_EQ(true, false);
|
|
||||||
//}
|
|
||||||
44
test/src/format_utility.cpp
Normal file
44
test/src/format_utility.cpp
Normal 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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user