Compare commits
No commits in common. "master" and "b50d5a7e0e713d207237ce66b34bf22087ee4092" have entirely different histories.
master
...
b50d5a7e0e
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,3 @@
|
|||||||
build
|
|
||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
cmake-build-release
|
cmake-build-release
|
||||||
cmake-build-relwithdebinfo
|
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
cmake_minimum_required(VERSION 3.21)
|
||||||
project(const_fmt)
|
project(logger)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
@ -9,10 +9,16 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
include_directories(.)
|
include_directories(inc)
|
||||||
|
|
||||||
|
add_executable(logger src/main.cpp)
|
||||||
|
|
||||||
|
|
||||||
add_subdirectory(examples)
|
if(MSVC)
|
||||||
|
target_compile_options(logger PRIVATE /W4 /WX)
|
||||||
|
else()
|
||||||
|
target_compile_options(logger PRIVATE -O3 -Wall -Wextra -pedantic -fno-exceptions)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
option(PACKAGE_TESTS "Build the tests" ON)
|
option(PACKAGE_TESTS "Build the tests" ON)
|
||||||
|
|||||||
19
LICENSE
19
LICENSE
@ -1,19 +0,0 @@
|
|||||||
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.
|
|
||||||
57
README.md
57
README.md
@ -1,57 +0,0 @@
|
|||||||
# const_fmt
|
|
||||||
|
|
||||||
An extremely lightweight library, intentionally resembling `fmtlib` as much as possible. This is achieved by moving
|
|
||||||
as much of the formatting process as possible to compile time.
|
|
||||||
|
|
||||||
Meant for systems with very few resources, such as embedded systems.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
During compile-time, the string to be formatted is preprocessed to the point only the actual values to be formatted
|
|
||||||
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 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`.
|
|
||||||
|
|
||||||
## Including in a project
|
|
||||||
|
|
||||||
In order to keep it as lightweight and optimizable as possible, `const_fmt` is implemented as a header-only
|
|
||||||
library.
|
|
||||||
|
|
||||||
This means that using it in a project is as simple as cloning this repo (or e.g. adding it as a submodule)
|
|
||||||
and adding the repository root folder to the compiler include directories.
|
|
||||||
|
|
||||||
## Building and running the tests
|
|
||||||
|
|
||||||
1. Initialize the `googletest` framework submodule
|
|
||||||
```bash
|
|
||||||
$ git submodule update --init
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Create the build directory
|
|
||||||
```bash
|
|
||||||
$ cmake -B build -S .
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Build the project
|
|
||||||
```bash
|
|
||||||
$ cmake --build build/
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Run the tests
|
|
||||||
```bash
|
|
||||||
$ 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,
|
|
||||||
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.
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
#ifndef LOGGER_FORMAT_H
|
|
||||||
#define LOGGER_FORMAT_H
|
|
||||||
|
|
||||||
|
|
||||||
#include "format_impl.h"
|
|
||||||
#include "parse.h"
|
|
||||||
#include "utility.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace const_fmt { namespace const_fmt_detail {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Utility functions
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
template <ConstString s>
|
|
||||||
constexpr inline int get_output_len() {
|
|
||||||
constexpr auto parse_result = parse_string<s>();
|
|
||||||
static_assert(parse_result.is_valid, "Syntax error in const_format string");
|
|
||||||
|
|
||||||
return get_ast_output_len<parse_result.value>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <fmt_node_t fmt_node, typename T>
|
|
||||||
constexpr inline void check_fmt_params() {
|
|
||||||
static_assert(fmt_node.length > fmt_node.precision + 1,
|
|
||||||
"Insufficient length for desired precision");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Formatting wrapper functions
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
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<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<fmt_data>(dest, arg);
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Error handling
|
|
||||||
template <fmt_data_t fmt_data>
|
|
||||||
constexpr inline void format_arg(char* dest, const char* arg) {
|
|
||||||
const std::size_t len = const_strlen(arg);
|
|
||||||
if (len > fmt_data.length) return;
|
|
||||||
|
|
||||||
dest = dest + fmt_data.length - len;
|
|
||||||
|
|
||||||
if (!std::is_constant_evaluated()) {
|
|
||||||
memcpy(dest, arg, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (std::size_t i = 0; i < len; ++i) {
|
|
||||||
*(dest++) = *(arg++);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <auto fmt_data_array, typename first_arg_t, typename... args_t>
|
|
||||||
constexpr inline void format_args(char* dest, first_arg_t first_arg,
|
|
||||||
args_t... args) {
|
|
||||||
format_arg<fmt_data_array[0]>(dest + fmt_data_array[0].position, first_arg);
|
|
||||||
|
|
||||||
if constexpr(fmt_data_array.size() > 1)
|
|
||||||
format_args<drop_first(fmt_data_array)>(dest, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <auto ast>
|
|
||||||
consteval inline auto get_preproc_string() {
|
|
||||||
std::array<char, get_ast_output_len<ast>()> result;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (const auto& ast_node : ast) {
|
|
||||||
if (ast_node.is_char()) {
|
|
||||||
result[i++] = ast_node.get_char();
|
|
||||||
} else {
|
|
||||||
for (int j = 0; j < ast_node.get_node().length; ++j)
|
|
||||||
result[i++] = ast_node.get_node().has_zero_padding ? '0' : ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}} // namespace const_fmt::const_fmt_detail
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Public Interface
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
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>();
|
|
||||||
constexpr auto fmt_data = const_fmt_detail::get_fmt_data<ast.value>();
|
|
||||||
|
|
||||||
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...);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <const_fmt_detail::ConstString t_s>
|
|
||||||
class const_fmt_literal_obj_t {
|
|
||||||
public:
|
|
||||||
template <typename... args_t>
|
|
||||||
constexpr auto operator()(args_t... args) {
|
|
||||||
return const_format<t_s>(args...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <const_fmt_detail::ConstString t_s>
|
|
||||||
constexpr auto operator""_const_fmt() {
|
|
||||||
return const_fmt_literal_obj_t<t_s>{};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace const_fmt
|
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_FORMAT_H
|
|
||||||
@ -1,271 +0,0 @@
|
|||||||
#ifndef LOGGER_FORMAT_IMPL_H
|
|
||||||
#define LOGGER_FORMAT_IMPL_H
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* ****************************************************************
|
|
||||||
* Disclaimer: Most of this code is shamelessly stolen from fmtlib
|
|
||||||
* ****************************************************************
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include "stdlib.h"
|
|
||||||
#include "utility.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace const_fmt { namespace const_fmt_detail {
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Utility functions
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#define FMT_POWERS_OF_10(factor) \
|
|
||||||
factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
|
|
||||||
(factor)*1000000, (factor)*10000000, (factor)*100000000, \
|
|
||||||
(factor)*1000000000
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr int count_digits_decimal_fallback(T n) {
|
|
||||||
int count = 1;
|
|
||||||
for (;;) {
|
|
||||||
if (n < 10) return count;
|
|
||||||
if (n < 100) return count + 1;
|
|
||||||
if (n < 1000) return count + 2;
|
|
||||||
if (n < 10000) return count + 3;
|
|
||||||
n /= 10000u;
|
|
||||||
count += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
|
|
||||||
10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
|
|
||||||
15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
|
|
||||||
auto t = bsr2log10[__builtin_clzll(n | 1) ^ 63];
|
|
||||||
static constexpr const uint64_t zero_or_powers_of_10[] = {
|
|
||||||
0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
|
|
||||||
10000000000000000000ULL};
|
|
||||||
return t - (n < zero_or_powers_of_10[t]);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
constexpr inline void copy2(char* dst, const char* src) {
|
|
||||||
if (!std::is_constant_evaluated()) {
|
|
||||||
memcpy(dst, src, 2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*dst++ = static_cast<char>(*src++);
|
|
||||||
*dst = static_cast<char>(*src);
|
|
||||||
}
|
|
||||||
|
|
||||||
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';
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
out += size;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
out -= 2;
|
|
||||||
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Integral types
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
template <fmt_data_t t_fmt_node, std::unsigned_integral uint_t>
|
|
||||||
constexpr inline void format_int(char* out, uint_t value) {
|
|
||||||
format_base<t_fmt_node.type>(out, value,
|
|
||||||
count_digits_base<t_fmt_node.type>(value),
|
|
||||||
t_fmt_node.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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_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) = '-';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Floating point types
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
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 + t_fmt_node.length - t_fmt_node.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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}} // namespace const_fmt::const_fmt_detail
|
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_FORMAT_IMPL_H
|
|
||||||
@ -1,304 +0,0 @@
|
|||||||
#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 std::size_t size() const noexcept {
|
|
||||||
return t_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
add_executable(const_fmt_example src/examples.cpp)
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
target_compile_options(const_fmt_example PRIVATE /W4 /WX)
|
|
||||||
else()
|
|
||||||
target_compile_options(const_fmt_example PRIVATE -O3 -Wall -Wextra -pedantic -fno-exceptions)
|
|
||||||
endif()
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
#define CONST_FMT_NO_CPP_STDLIB
|
|
||||||
|
|
||||||
#include <const_fmt/format.h>
|
|
||||||
//#include <iostream>
|
|
||||||
//#include <string_view>
|
|
||||||
|
|
||||||
using namespace const_fmt;
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
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
|
|
||||||
// std::string_view sv{&s[0], s.size()};
|
|
||||||
//
|
|
||||||
// std::cout << sv << std::endl;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
52
inc/Logger.h
Normal file
52
inc/Logger.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef LOGGER_LOGGER_H
|
||||||
|
#define LOGGER_LOGGER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <iostream>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include <format.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Logger class
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept output_policy_c = requires(T t) {
|
||||||
|
t.write('c');
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
template <detail::output_policy_c output_policy_t>
|
||||||
|
class Logger {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Logger(output_policy_t output_policy) : m_output_policy(output_policy) {
|
||||||
|
}
|
||||||
|
|
||||||
|
template <detail::ConstString msg, typename... args_t>
|
||||||
|
void log(args_t... args) {
|
||||||
|
const auto formatted_msg = format<msg>(args...);
|
||||||
|
|
||||||
|
for (const auto& c : formatted_msg) {
|
||||||
|
m_output_policy.write(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_output_policy.write('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
output_policy_t& m_output_policy;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LOGGER_LOGGER_H
|
||||||
138
inc/format.h
Normal file
138
inc/format.h
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
#ifndef LOGGER_FORMAT_H
|
||||||
|
#define LOGGER_FORMAT_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "parse.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "format_impl.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Utility functions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
template <ConstString s>
|
||||||
|
constexpr inline int get_output_len() {
|
||||||
|
constexpr auto parse_result = parse_string<s>();
|
||||||
|
static_assert(parse_result.is_valid, "Syntax error in format string");
|
||||||
|
|
||||||
|
return get_ast_output_len<parse_result.value>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <fmt_node_t fmt_node, typename T>
|
||||||
|
constexpr inline void check_fmt_params() {
|
||||||
|
static_assert(fmt_node.length > fmt_node.precision + 1,
|
||||||
|
"Insufficient length for desired precision");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Formatting wrapper functions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <fmt_data_t fmt_data, std::integral arg_t>
|
||||||
|
constexpr inline void format_arg(char* dest, arg_t arg) {
|
||||||
|
detail::format_int(dest, arg, fmt_data);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <fmt_data_t fmt_data, std::floating_point arg_t>
|
||||||
|
constexpr inline void format_arg(char* dest, arg_t arg) {
|
||||||
|
//detail::format_float(dest, arg, fmt_data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Error handling
|
||||||
|
template<fmt_data_t fmt_data>
|
||||||
|
constexpr inline void format_arg(char* dest, const char* arg) {
|
||||||
|
const std::size_t len = const_strlen(arg);
|
||||||
|
if (len > fmt_data.length) return;
|
||||||
|
|
||||||
|
dest = dest + fmt_data.length - len;
|
||||||
|
|
||||||
|
if (!std::is_constant_evaluated()) {
|
||||||
|
std::memcpy(dest, arg, len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < len; ++i) {
|
||||||
|
*(dest++) = *(arg++);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// End of recursion
|
||||||
|
template <auto ast>
|
||||||
|
constexpr inline void format_args(char*) {
|
||||||
|
}
|
||||||
|
|
||||||
|
template <auto fmt_data_array, typename first_arg_t, typename... args_t>
|
||||||
|
constexpr inline void format_args(char* dest, first_arg_t first_arg, args_t... args) {
|
||||||
|
format_arg<fmt_data_array[0]>(dest + fmt_data_array[0].position, first_arg);
|
||||||
|
format_args<drop_first(fmt_data_array)>(dest, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <auto ast>
|
||||||
|
consteval inline std::array<char, get_ast_output_len<ast>()> get_preproc_string() {
|
||||||
|
auto result = get_init_array<get_ast_output_len<ast>()>('f');
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (const auto& ast_node : ast) {
|
||||||
|
if (ast_node.is_char())
|
||||||
|
result[i++] = ast_node.get_char();
|
||||||
|
else
|
||||||
|
i += ast_node.get_node().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Public Interface
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
template <detail::ConstString s, typename... args_t>
|
||||||
|
constexpr inline auto format(args_t... args) {
|
||||||
|
constexpr auto ast = detail::parse_string<s>().value;
|
||||||
|
constexpr auto fmt_data = detail::get_fmt_data<ast>();
|
||||||
|
|
||||||
|
auto result = detail::get_preproc_string<ast>();
|
||||||
|
|
||||||
|
detail::format_args<fmt_data>(result.begin(), args...);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<detail::ConstString t_s>
|
||||||
|
class fmt_literal_obj_t {
|
||||||
|
public:
|
||||||
|
template<typename... args_t>
|
||||||
|
constexpr auto operator()(args_t... args) {
|
||||||
|
return format<t_s>(args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <detail::ConstString t_s>
|
||||||
|
constexpr auto operator""_fmt() {
|
||||||
|
return fmt_literal_obj_t<t_s>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LOGGER_FORMAT_H
|
||||||
154
inc/format_impl.h
Normal file
154
inc/format_impl.h
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#ifndef LOGGER_FORMAT_IMPL_H
|
||||||
|
#define LOGGER_FORMAT_IMPL_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ****************************************************************
|
||||||
|
* Disclaimer: Most of this code is shamelessly stolen from fmtlib
|
||||||
|
* ****************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Utility functions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define FMT_POWERS_OF_10(factor) \
|
||||||
|
factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
|
||||||
|
(factor)*1000000, (factor)*10000000, (factor)*100000000, \
|
||||||
|
(factor)*1000000000
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr int count_digits_fallback(T n) {
|
||||||
|
int count = 1;
|
||||||
|
for (;;) {
|
||||||
|
if (n < 10) return count;
|
||||||
|
if (n < 100) return count + 1;
|
||||||
|
if (n < 1000) return count + 2;
|
||||||
|
if (n < 10000) return count + 3;
|
||||||
|
n /= 10000u;
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int do_count_digits(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,
|
||||||
|
6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
|
||||||
|
10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
|
||||||
|
15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20};
|
||||||
|
auto t = bsr2log10[__builtin_clzll(n | 1) ^ 63];
|
||||||
|
static constexpr const uint64_t zero_or_powers_of_10[] = {
|
||||||
|
0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL),
|
||||||
|
10000000000000000000ULL};
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return count_digits_fallback(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Converts value in the range [0, 100) to a string.
|
||||||
|
constexpr inline const char* digits2(size_t value) {
|
||||||
|
// GCC generates slightly better code when value is pointer-size.
|
||||||
|
return &"0001020304050607080910111213141516171819"
|
||||||
|
"2021222324252627282930313233343536373839"
|
||||||
|
"4041424344454647484950515253545556575859"
|
||||||
|
"6061626364656667686970717273747576777879"
|
||||||
|
"8081828384858687888990919293949596979899"[value * 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constexpr inline void copy2(char* dst, const char* src) {
|
||||||
|
if (!std::is_constant_evaluated()) {
|
||||||
|
std::memcpy(dst, src, 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*dst++ = static_cast<char>(*src++);
|
||||||
|
*dst = static_cast<char>(*src);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename uint_t>
|
||||||
|
constexpr inline void format_decimal(char* out, uint_t value, int size) {
|
||||||
|
if (count_digits(value) > size) return;
|
||||||
|
|
||||||
|
out += size;
|
||||||
|
while (value >= 100) {
|
||||||
|
out -= 2;
|
||||||
|
copy2(out, digits2(static_cast<size_t>(value % 100)));
|
||||||
|
value /= 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < 10) {
|
||||||
|
*--out = static_cast<char>('0' + value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
out -= 2;
|
||||||
|
copy2(out, digits2(static_cast<size_t>(value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Integral types
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
template <std::unsigned_integral uint_t>
|
||||||
|
constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
|
||||||
|
format_decimal(out, value, 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;
|
||||||
|
format_decimal(out + 1 * (negative), abs_value,
|
||||||
|
fmt_node.length - 1 * (negative));
|
||||||
|
|
||||||
|
if (negative) *out = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Floating point types
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
template <std::floating_point float_t>
|
||||||
|
constexpr inline void format_float(char* out, float_t value,
|
||||||
|
fmt_data_t fmt_data) {
|
||||||
|
|
||||||
|
*(out) = 'f';
|
||||||
|
*(out + fmt_data.length - fmt_data.precision - 1) = '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
#endif // LOGGER_FORMAT_IMPL_H
|
||||||
@ -37,7 +37,7 @@
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
|
||||||
namespace const_fmt { namespace const_fmt_detail {
|
namespace detail {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -119,42 +119,42 @@ constexpr inline parse_result_t<unsigned> parse_number(unsigned i) {
|
|||||||
template <ConstString s>
|
template <ConstString s>
|
||||||
constexpr inline parse_result_t<FormatType> parse_type(unsigned i) {
|
constexpr inline parse_result_t<FormatType> parse_type(unsigned i) {
|
||||||
switch (s[i]) {
|
switch (s[i]) {
|
||||||
case 's':
|
case 's':
|
||||||
return {true, ++i, FormatType::s};
|
return {true, ++i, FormatType::s};
|
||||||
case 'c':
|
case 'c':
|
||||||
return {true, ++i, FormatType::c};
|
return {true, ++i, FormatType::c};
|
||||||
case 'b':
|
case 'b':
|
||||||
return {true, ++i, FormatType::b};
|
return {true, ++i, FormatType::b};
|
||||||
case 'B':
|
case 'B':
|
||||||
return {true, ++i, FormatType::B};
|
return {true, ++i, FormatType::B};
|
||||||
case 'd':
|
case 'd':
|
||||||
return {true, ++i, FormatType::d};
|
return {true, ++i, FormatType::d};
|
||||||
case 'o':
|
case 'o':
|
||||||
return {true, ++i, FormatType::o};
|
return {true, ++i, FormatType::o};
|
||||||
case 'x':
|
case 'x':
|
||||||
return {true, ++i, FormatType::x};
|
return {true, ++i, FormatType::x};
|
||||||
case 'X':
|
case 'X':
|
||||||
return {true, ++i, FormatType::X};
|
return {true, ++i, FormatType::X};
|
||||||
case 'a':
|
case 'a':
|
||||||
return {true, ++i, FormatType::a};
|
return {true, ++i, FormatType::a};
|
||||||
case 'A':
|
case 'A':
|
||||||
return {true, ++i, FormatType::A};
|
return {true, ++i, FormatType::A};
|
||||||
case 'e':
|
case 'e':
|
||||||
return {true, ++i, FormatType::e};
|
return {true, ++i, FormatType::e};
|
||||||
case 'E':
|
case 'E':
|
||||||
return {true, ++i, FormatType::E};
|
return {true, ++i, FormatType::E};
|
||||||
case 'f':
|
case 'f':
|
||||||
return {true, ++i, FormatType::f};
|
return {true, ++i, FormatType::f};
|
||||||
case 'F':
|
case 'F':
|
||||||
return {true, ++i, FormatType::F};
|
return {true, ++i, FormatType::F};
|
||||||
case 'g':
|
case 'g':
|
||||||
return {true, ++i, FormatType::g};
|
return {true, ++i, FormatType::g};
|
||||||
case 'G':
|
case 'G':
|
||||||
return {true, ++i, FormatType::G};
|
return {true, ++i, FormatType::G};
|
||||||
case 'p':
|
case 'p':
|
||||||
return {true, ++i, FormatType::p};
|
return {true, ++i, FormatType::p};
|
||||||
default:
|
default:
|
||||||
return {false, i, FormatType::s};
|
return {false, i, FormatType::s};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,8 +214,7 @@ constexpr inline parse_result_t<fmt_node_t> parse_braces(unsigned i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <ConstString s>
|
template <ConstString s>
|
||||||
constexpr inline parse_result_t<string_result_t<get_ast_len<s>()>>
|
constexpr inline parse_result_t<string_result_t<get_ast_len<s>()>> parse_string() {
|
||||||
parse_string() {
|
|
||||||
parse_result_t<string_result_t<get_ast_len<s>()>> result;
|
parse_result_t<string_result_t<get_ast_len<s>()>> result;
|
||||||
result.is_valid = true;
|
result.is_valid = true;
|
||||||
|
|
||||||
@ -243,7 +242,7 @@ parse_string() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}} // namespace const_fmt::const_fmt_detail
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2,11 +2,10 @@
|
|||||||
#define LOGGER_TYPES_H
|
#define LOGGER_TYPES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include "stdlib.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace const_fmt { namespace const_fmt_detail {
|
namespace detail {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -20,7 +19,8 @@ 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(&content[0], (&content[N - 1] + 1), m_content.begin());
|
std::copy(std::begin(content), std::end(content),
|
||||||
|
std::begin(m_content));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char operator[](std::size_t index) const noexcept {
|
constexpr char operator[](std::size_t index) const noexcept {
|
||||||
@ -136,7 +136,7 @@ struct fmt_data_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}} // namespace const_fmt::const_fmt_detail
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_TYPES_H
|
#endif // LOGGER_TYPES_H
|
||||||
@ -2,13 +2,12 @@
|
|||||||
#define LOGGER_UTILITY_H
|
#define LOGGER_UTILITY_H
|
||||||
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
|
|
||||||
#include "stdlib.h"
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
namespace const_fmt { namespace const_fmt_detail {
|
namespace detail {
|
||||||
|
|
||||||
|
|
||||||
constexpr inline std::size_t const_pow(std::size_t base, std::size_t pow) {
|
constexpr inline std::size_t const_pow(std::size_t base, std::size_t pow) {
|
||||||
@ -19,6 +18,17 @@ constexpr inline std::size_t const_pow(std::size_t base, std::size_t pow) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <std::size_t t_n>
|
||||||
|
consteval inline std::array<char, t_n> get_init_array(char val) {
|
||||||
|
std::array<char, t_n> result;
|
||||||
|
|
||||||
|
for (auto& c : result)
|
||||||
|
c = val;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template <auto ast>
|
template <auto ast>
|
||||||
consteval inline std::size_t count_ast_format_nodes() {
|
consteval inline std::size_t count_ast_format_nodes() {
|
||||||
std::size_t result = 0;
|
std::size_t result = 0;
|
||||||
@ -31,8 +41,7 @@ consteval inline std::size_t count_ast_format_nodes() {
|
|||||||
|
|
||||||
|
|
||||||
template <auto ast>
|
template <auto ast>
|
||||||
consteval inline std::array<fmt_data_t, count_ast_format_nodes<ast>()>
|
consteval inline std::array<fmt_data_t, count_ast_format_nodes<ast>()> get_fmt_data() {
|
||||||
get_fmt_data() {
|
|
||||||
std::array<fmt_data_t, count_ast_format_nodes<ast>()> result = {};
|
std::array<fmt_data_t, count_ast_format_nodes<ast>()> result = {};
|
||||||
|
|
||||||
std::size_t position = 0;
|
std::size_t position = 0;
|
||||||
@ -87,7 +96,7 @@ consteval inline int get_ast_output_len() {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr inline std::size_t const_strlen(const char* arg) {
|
constexpr inline std::size_t const_strlen(const char* arg) {
|
||||||
if (std::is_constant_evaluated()) {
|
if (std::is_constant_evaluated()) {
|
||||||
return *arg ? 1 + const_strlen(arg + 1) : 0;
|
return *arg ? 1 + const_strlen(arg + 1) : 0;
|
||||||
} else {
|
} else {
|
||||||
@ -96,7 +105,7 @@ constexpr inline std::size_t const_strlen(const char* arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}} // namespace const_fmt::const_fmt_detail
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_UTILITY_H
|
#endif // LOGGER_UTILITY_H
|
||||||
15
src/main.cpp
Normal file
15
src/main.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//#include <iostream>
|
||||||
|
|
||||||
|
#include <Logger.h>
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
auto formatted = "Test: {:12} {:012.5} {:8}"_fmt(argv[0], 123.45, -1234567);
|
||||||
|
|
||||||
|
for (const auto& c : formatted)
|
||||||
|
std::cout << c;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// return formatted[6];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -14,19 +14,9 @@ macro(package_add_test TESTNAME)
|
|||||||
PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_DIR}"
|
PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_DIR}"
|
||||||
)
|
)
|
||||||
set_target_properties(${TESTNAME} PROPERTIES FOLDER tests)
|
set_target_properties(${TESTNAME} PROPERTIES FOLDER tests)
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
target_compile_options(${TESTNAME} PRIVATE /W4 /WX)
|
|
||||||
else()
|
|
||||||
target_compile_options(${TESTNAME} PRIVATE -O3 -Wall -Wextra -pedantic -fno-exceptions)
|
|
||||||
endif()
|
|
||||||
endmacro()
|
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_utility_test src/format_utility.cpp)
|
package_add_test(format_test src/format.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)
|
|
||||||
|
|
||||||
|
|||||||
65
test/src/format.cpp
Normal file
65
test/src/format.cpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#include <format.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace detail;
|
||||||
|
|
||||||
|
|
||||||
|
TEST(Format, positive_int) {
|
||||||
|
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
|
||||||
|
'0', '0', '0', '2'};
|
||||||
|
constexpr std::array<char, 8> formatted1 = format<"{:08}">(2);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', '2',
|
||||||
|
'2', '2', '2', '2'};
|
||||||
|
constexpr std::array<char, 8> formatted2 = format<"{:8}">(22222);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1',
|
||||||
|
'2', '3', '4', '5'};
|
||||||
|
constexpr std::array<char, 8> formatted3 = format<"{:08.4}">(12345);
|
||||||
|
|
||||||
|
constexpr std::array<char, 4> control4 = {'6', '7', '8', '9'};
|
||||||
|
constexpr std::array<char, 4> formatted4 = format<"{:4}">(6789);
|
||||||
|
|
||||||
|
EXPECT_EQ(control1, formatted1);
|
||||||
|
EXPECT_EQ(control2, formatted2);
|
||||||
|
EXPECT_EQ(control3, formatted3);
|
||||||
|
EXPECT_EQ(control4, formatted4);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Format, negative_int) {
|
||||||
|
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
|
||||||
|
'0', '0', '0', '2'};
|
||||||
|
constexpr std::array<char, 8> formatted1 = format<"{:08}">(-2);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control2 = {' ', ' ', '-', '2',
|
||||||
|
'2', '2', '2', '2'};
|
||||||
|
constexpr std::array<char, 8> formatted2 = format<"{:8}">(-22222);
|
||||||
|
|
||||||
|
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1',
|
||||||
|
'2', '3', '4', '5'};
|
||||||
|
constexpr std::array<char, 8> formatted3 = format<"{:08.4}">(-12345);
|
||||||
|
|
||||||
|
constexpr std::array<char, 5> control4 = {'-', '6', '7', '8', '9'};
|
||||||
|
constexpr std::array<char, 5> formatted4 = format<"{:5}">(-6789);
|
||||||
|
|
||||||
|
EXPECT_EQ(control1, formatted1);
|
||||||
|
EXPECT_EQ(control2, formatted2);
|
||||||
|
EXPECT_EQ(control3, formatted3);
|
||||||
|
EXPECT_EQ(control4, formatted4);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
//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);
|
||||||
|
//}
|
||||||
@ -1,59 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@ -1,103 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
#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);
|
|
||||||
}
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <const_fmt/parse.h>
|
#include <parse.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace const_fmt::const_fmt_detail;
|
using namespace detail;
|
||||||
|
|
||||||
|
|
||||||
TEST(Parse, parse_number) {
|
TEST(Parse, parse_number) {
|
||||||
@ -93,4 +93,4 @@ TEST(Parse, ast_chars_and_nodes) {
|
|||||||
' ', ' ', '-'};
|
' ', ' ', '-'};
|
||||||
|
|
||||||
EXPECT_EQ(parse_string<"{:06.3}ab d{:8s}{:.4} -">().value, control);
|
EXPECT_EQ(parse_string<"{:06.3}ab d{:8s}{:.4} -">().value, control);
|
||||||
}
|
}
|
||||||
@ -1,11 +1,9 @@
|
|||||||
#include <const_fmt/utility.h>
|
#include <utility.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
using namespace const_fmt;
|
|
||||||
|
|
||||||
TEST(Utility, const_pow) {
|
TEST(Utility, const_pow) {
|
||||||
EXPECT_EQ(const_fmt_detail::const_pow(10, 0), 1);
|
EXPECT_EQ(detail::const_pow(10, 0), 1);
|
||||||
EXPECT_EQ(const_fmt_detail::const_pow(0, 1), 0);
|
EXPECT_EQ(detail::const_pow(0, 1), 0);
|
||||||
EXPECT_EQ(const_fmt_detail::const_pow(2, 8), 0b1'0000'0000);
|
EXPECT_EQ(detail::const_pow(2, 8), 0b1'0000'0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user