Compare commits
22 Commits
b50d5a7e0e
...
v1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 6666a25562 | |||
| 008c71381c | |||
| 9c22b2a084 | |||
| 9bf3e63058 | |||
| 4666b4e737 | |||
| 1338888e0b | |||
| f47a23ffef | |||
| 4128ede5db | |||
| bb5f4d5272 | |||
| 3db91aebda | |||
| 2ecc001a64 | |||
| 70fd273a70 | |||
| 9626f5efc4 | |||
| 9c5a73e2bd | |||
| 50635a8e2f | |||
| f9cbfdd018 | |||
| 55ff861f67 | |||
| d26cf43b32 | |||
| c9e845b025 | |||
| 6597a3a360 | |||
| 5bf165463c | |||
| 83f7f21635 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
build
|
||||||
cmake-build-debug
|
cmake-build-debug
|
||||||
cmake-build-release
|
cmake-build-release
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
cmake_minimum_required(VERSION 3.21)
|
||||||
project(logger)
|
project(const_fmt)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
@@ -9,16 +9,10 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
include_directories(inc)
|
include_directories(.)
|
||||||
|
|
||||||
add_executable(logger src/main.cpp)
|
|
||||||
|
|
||||||
|
|
||||||
if(MSVC)
|
add_subdirectory(examples)
|
||||||
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
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.
|
||||||
55
README.md
Normal file
55
README.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# 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 compile
|
||||||
|
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`.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -4,12 +4,12 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "format_impl.h"
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "format_impl.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace detail {
|
namespace const_fmt { namespace const_fmt_detail {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -22,7 +22,7 @@ namespace detail {
|
|||||||
template <ConstString s>
|
template <ConstString s>
|
||||||
constexpr inline int get_output_len() {
|
constexpr inline int get_output_len() {
|
||||||
constexpr auto parse_result = parse_string<s>();
|
constexpr auto parse_result = parse_string<s>();
|
||||||
static_assert(parse_result.is_valid, "Syntax error in format string");
|
static_assert(parse_result.is_valid, "Syntax error in const_format string");
|
||||||
|
|
||||||
return get_ast_output_len<parse_result.value>();
|
return get_ast_output_len<parse_result.value>();
|
||||||
}
|
}
|
||||||
@@ -42,12 +42,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) {
|
||||||
detail::format_int(dest, arg, fmt_data);
|
const_fmt_detail::format_int<arg_t, 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){
|
||||||
//detail::format_float(dest, arg, fmt_data);
|
const_fmt_detail::format_float<arg_t, fmt_data>(dest, arg);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Error handling
|
// TODO: Error handling
|
||||||
@@ -74,30 +74,33 @@ constexpr inline void format_args(char*) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <auto fmt_data_array, typename first_arg_t, typename... args_t>
|
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) {
|
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_arg<fmt_data_array[0]>(dest + fmt_data_array[0].position, first_arg);
|
||||||
format_args<drop_first(fmt_data_array)>(dest, args...);
|
format_args<drop_first(fmt_data_array)>(dest, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <auto ast>
|
template <auto ast>
|
||||||
consteval inline std::array<char, get_ast_output_len<ast>()> get_preproc_string() {
|
consteval inline auto get_preproc_string() {
|
||||||
auto result = get_init_array<get_ast_output_len<ast>()>('f');
|
std::array<char, get_ast_output_len<ast>()> result;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
for (const auto& ast_node : ast) {
|
for (const auto& ast_node : ast) {
|
||||||
if (ast_node.is_char())
|
if (ast_node.is_char()) {
|
||||||
result[i++] = ast_node.get_char();
|
result[i++] = ast_node.get_char();
|
||||||
else
|
} else {
|
||||||
i += ast_node.get_node().length;
|
for (int j = 0; j < ast_node.get_node().length; ++j)
|
||||||
|
result[i++] = ast_node.get_node().has_zero_padding ? '0' : ' ';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace detail
|
}} // namespace const_fmt::const_fmt_detail
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -107,32 +110,38 @@ consteval inline std::array<char, get_ast_output_len<ast>()> get_preproc_string(
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
template <detail::ConstString s, typename... args_t>
|
namespace const_fmt {
|
||||||
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...);
|
template <const_fmt_detail::ConstString s, typename... args_t>
|
||||||
|
constexpr inline auto const_format(args_t... args) {
|
||||||
|
constexpr auto ast = const_fmt_detail::parse_string<s>().value;
|
||||||
|
constexpr auto fmt_data = const_fmt_detail::get_fmt_data<ast>();
|
||||||
|
|
||||||
|
auto result = const_fmt_detail::get_preproc_string<ast>();
|
||||||
|
|
||||||
|
const_fmt_detail::format_args<fmt_data>(result.begin(), args...);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<detail::ConstString t_s>
|
template <const_fmt_detail::ConstString t_s>
|
||||||
class fmt_literal_obj_t {
|
class const_fmt_literal_obj_t {
|
||||||
public:
|
public:
|
||||||
template <typename... args_t>
|
template <typename... args_t>
|
||||||
constexpr auto operator()(args_t... args) {
|
constexpr auto operator()(args_t... args) {
|
||||||
return format<t_s>(args...);
|
return const_format<t_s>(args...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <detail::ConstString t_s>
|
template <const_fmt_detail::ConstString t_s>
|
||||||
constexpr auto operator""_fmt() {
|
constexpr auto operator""_const_fmt() {
|
||||||
return fmt_literal_obj_t<t_s>{};
|
return const_fmt_literal_obj_t<t_s>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace const_fmt
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_FORMAT_H
|
#endif // LOGGER_FORMAT_H
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
|
||||||
|
|
||||||
namespace detail {
|
namespace const_fmt { namespace const_fmt_detail {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -87,8 +87,14 @@ constexpr inline void copy2(char* dst, const char* src) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename uint_t>
|
template <typename uint_t>
|
||||||
constexpr inline void format_decimal(char* out, uint_t value, int size) {
|
constexpr inline void format_decimal(char* out, uint_t value, int n_digits,
|
||||||
if (count_digits(value) > size) return;
|
int size) {
|
||||||
|
if (n_digits > size) {
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
*(out++) = 'f';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
out += size;
|
out += size;
|
||||||
while (value >= 100) {
|
while (value >= 100) {
|
||||||
@@ -106,6 +112,25 @@ constexpr inline void format_decimal(char* out, uint_t value, int size) {
|
|||||||
copy2(out, digits2(static_cast<size_t>(value)));
|
copy2(out, digits2(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 +139,26 @@ constexpr inline void format_decimal(char* out, uint_t value, int size) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
template <std::unsigned_integral uint_t>
|
template <std::unsigned_integral uint_t, fmt_data_t t_fmt_node>
|
||||||
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_decimal(out, value, count_digits(value), t_fmt_node.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::signed_integral uint_t>
|
template <std::signed_integral int_t, fmt_data_t t_fmt_node>
|
||||||
constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
|
constexpr inline void format_int(char* out, int_t value) {
|
||||||
auto abs_value = static_cast<uint64_t>(value);
|
const auto [abs_value, negative] = get_abs_value(value);
|
||||||
const bool negative = value < 0;
|
|
||||||
|
|
||||||
if (negative) abs_value = 0 - abs_value;
|
const std::size_t n_digits = count_digits(abs_value);
|
||||||
format_decimal(out + 1 * (negative), abs_value,
|
|
||||||
fmt_node.length - 1 * (negative));
|
|
||||||
|
|
||||||
if (negative) *out = '-';
|
format_decimal(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,16 +169,42 @@ constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
template <std::floating_point float_t>
|
template <std::floating_point float_t, fmt_data_t t_fmt_node>
|
||||||
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
|
||||||
|
t_fmt_node.type, // ignored
|
||||||
|
t_fmt_node.position // ignored
|
||||||
|
};
|
||||||
|
constexpr fmt_data_t fmt_node_fractional = {
|
||||||
|
true, // has_zero_padding
|
||||||
|
t_fmt_node.precision, // length
|
||||||
|
t_fmt_node.precision, // ignored
|
||||||
|
t_fmt_node.type, // ignored
|
||||||
|
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<int, fmt_node_integral>(out, integral);
|
||||||
|
format_int<uint16_t, fmt_node_fractional>(
|
||||||
|
out + t_fmt_node.length - t_fmt_node.precision, fractional_abs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace detail
|
}} // namespace const_fmt::const_fmt_detail
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_FORMAT_IMPL_H
|
#endif // LOGGER_FORMAT_IMPL_H
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-const_format off
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
@@ -34,10 +34,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// clang-format on
|
// clang-const_format on
|
||||||
|
|
||||||
|
|
||||||
namespace detail {
|
namespace const_fmt { namespace const_fmt_detail {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -214,7 +214,8 @@ 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>()>> parse_string() {
|
constexpr inline parse_result_t<string_result_t<get_ast_len<s>()>>
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -242,7 +243,7 @@ constexpr inline parse_result_t<string_result_t<get_ast_len<s>()>> parse_string(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace detail
|
}} // namespace const_fmt::const_fmt_detail
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
|
||||||
namespace detail {
|
namespace const_fmt { namespace const_fmt_detail {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -136,7 +136,7 @@ struct fmt_data_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace detail
|
}} // namespace const_fmt::const_fmt_detail
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_TYPES_H
|
#endif // LOGGER_TYPES_H
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
namespace detail {
|
namespace const_fmt { namespace const_fmt_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) {
|
||||||
@@ -18,17 +18,6 @@ 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;
|
||||||
@@ -41,7 +30,8 @@ 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>()> get_fmt_data() {
|
consteval inline std::array<fmt_data_t, count_ast_format_nodes<ast>()>
|
||||||
|
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;
|
||||||
@@ -105,7 +95,7 @@ constexpr inline std::size_t const_strlen(const char* arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace detail
|
}} // namespace const_fmt::const_fmt_detail
|
||||||
|
|
||||||
|
|
||||||
#endif // LOGGER_UTILITY_H
|
#endif // LOGGER_UTILITY_H
|
||||||
7
examples/CMakeLists.txt
Normal file
7
examples/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
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()
|
||||||
17
examples/src/examples.cpp
Normal file
17
examples/src/examples.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include <const_fmt/format.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
using namespace const_fmt;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
constexpr auto s = "This is an integer: {:08}, and this is a float: {:09.4}"_const_fmt(12345, -86.2);
|
||||||
|
|
||||||
|
// 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
52
inc/Logger.h
@@ -1,52 +0,0 @@
|
|||||||
#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
|
|
||||||
15
src/main.cpp
15
src/main.cpp
@@ -1,15 +0,0 @@
|
|||||||
//#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,8 +14,15 @@ 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_test src/format.cpp)
|
package_add_test(format_test src/format.cpp)
|
||||||
|
|||||||
@@ -1,63 +1,106 @@
|
|||||||
#include <format.h>
|
#include <const_fmt/format.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace detail;
|
using namespace const_fmt;
|
||||||
|
using namespace const_fmt::const_fmt_detail;
|
||||||
|
|
||||||
|
|
||||||
TEST(Format, positive_int) {
|
TEST(Format, 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', '0', '2'};
|
||||||
constexpr std::array<char, 8> formatted1 = format<"{:08}">(2);
|
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(2);
|
||||||
|
|
||||||
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', '2',
|
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', '2',
|
||||||
'2', '2', '2', '2'};
|
'2', '2', '2', '2'};
|
||||||
constexpr std::array<char, 8> formatted2 = format<"{:8}">(22222);
|
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(22222);
|
||||||
|
|
||||||
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1',
|
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1',
|
||||||
'2', '3', '4', '5'};
|
'2', '3', '4', '5'};
|
||||||
constexpr std::array<char, 8> formatted3 = format<"{:08.4}">(12345);
|
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> control4 = {'6', '7', '8', '9'};
|
||||||
constexpr std::array<char, 4> formatted4 = format<"{:4}">(6789);
|
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(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(Format, 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', '0', '2'};
|
||||||
constexpr std::array<char, 8> formatted1 = format<"{:08}">(-2);
|
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(-2);
|
||||||
|
|
||||||
constexpr std::array<char, 8> control2 = {' ', ' ', '-', '2',
|
constexpr std::array<char, 8> control2 = {' ', ' ', '-', '2',
|
||||||
'2', '2', '2', '2'};
|
'2', '2', '2', '2'};
|
||||||
constexpr std::array<char, 8> formatted2 = format<"{:8}">(-22222);
|
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(-22222);
|
||||||
|
|
||||||
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1',
|
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1',
|
||||||
'2', '3', '4', '5'};
|
'2', '3', '4', '5'};
|
||||||
constexpr std::array<char, 8> formatted3 = format<"{:08.4}">(-12345);
|
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> control4 = {'-', '6', '7', '8', '9'};
|
||||||
constexpr std::array<char, 5> formatted4 = format<"{:5}">(-6789);
|
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(Format, 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(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);
|
||||||
}
|
}
|
||||||
//
|
|
||||||
//TEST(Format, positive_float) {
|
TEST(Format, negative_float) {
|
||||||
// // TODO
|
constexpr std::array<char, 8> control1 = {'-', '0', '2', '.',
|
||||||
// EXPECT_EQ(true, false);
|
'3', '4', '5', '6'};
|
||||||
//}
|
constexpr std::array<char, 8> formatted1 = const_format<"{:08.4}">(-2.3456);
|
||||||
//
|
|
||||||
//TEST(Format, negative_float) {
|
// Float error: 2323.2 -> 2323.1
|
||||||
// // TODO
|
constexpr std::array<char, 8> control2 = {' ', '-', '2', '3',
|
||||||
// EXPECT_EQ(true, false);
|
'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);
|
||||||
|
}
|
||||||
//
|
//
|
||||||
// TEST(Format, string) {
|
// TEST(Format, string) {
|
||||||
// // TODO
|
// // TODO
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <parse.h>
|
#include <const_fmt/parse.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace detail;
|
using namespace const_fmt::const_fmt_detail;
|
||||||
|
|
||||||
|
|
||||||
TEST(Parse, parse_number) {
|
TEST(Parse, parse_number) {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
#include <utility.h>
|
#include <const_fmt/utility.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace const_fmt;
|
||||||
|
|
||||||
TEST(Utility, const_pow) {
|
TEST(Utility, const_pow) {
|
||||||
EXPECT_EQ(detail::const_pow(10, 0), 1);
|
EXPECT_EQ(const_fmt_detail::const_pow(10, 0), 1);
|
||||||
EXPECT_EQ(detail::const_pow(0, 1), 0);
|
EXPECT_EQ(const_fmt_detail::const_pow(0, 1), 0);
|
||||||
EXPECT_EQ(detail::const_pow(2, 8), 0b1'0000'0000);
|
EXPECT_EQ(const_fmt_detail::const_pow(2, 8), 0b1'0000'0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user