44 Commits

Author SHA1 Message Date
045363c733 Merge pull request 'feature/no_std_lib' (#9) from feature/no_std_lib into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/9
2022-03-17 22:45:07 +00:00
4d05a9a8fa Added const versions of std::array::begin() and std::array::end() 2022-03-17 22:44:17 +01:00
ee1153c393 Changed copy constructor signature of std::array implementation 2022-03-17 22:11:30 +01:00
b29695e148 Replaced memcpy with manual while loop 2022-03-17 18:04:06 +01:00
86930fd8b0 Added conepts; Fixed bug in const_fmt::std::true_type 2022-02-24 16:14:38 +01:00
7796be7cd4 Reimplemented is_integral and is_floating_point with different approach; Made functions constexpr inline 2022-02-23 23:12:34 +01:00
73a50cae0f Added is_integral and is_floating_point 2022-02-23 20:42:56 +01:00
91398ef23d Added remove_cv, remove_const and remove_volatile 2022-02-23 19:51:55 +01:00
b5ecd73b18 Formatting 2022-02-23 19:47:06 +01:00
826f3a54b3 Changed comment 2022-02-23 19:46:33 +01:00
ea722ec5bf Changed includes; Implemented std::pair and std::make_unsigned; changed usage of std::memcpy to memcpy 2022-02-23 19:45:17 +01:00
5ba1723295 Made array a structural type 2022-02-20 14:29:58 +01:00
8e51facd10 fixed wrong clang-format comments 2022-02-20 00:27:45 +01:00
9bd1104e5f Code compiles and passes tests without NO_STDLIB define 2022-02-20 00:26:48 +01:00
6ba83fd582 Minor changes to make sure some stuff works with no stl headers 2022-02-20 00:15:31 +01:00
8f7b1cd4e3 Implemented std::array and a bunch of other stuff 2022-02-20 00:14:33 +01:00
ec70a5bba1 Merge branch 'master' into feature/no_std_lib 2022-02-19 19:21:34 +01:00
1263dfee98 Merge pull request 'Added cmake-build-relwithdebinfo to gitignore' (#8) from fix/gitignore_builddir into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/8
2022-02-19 18:18:51 +00:00
52992e255e Added cmake-build-relwithdebinfo to gitignore 2022-02-19 19:13:54 +01:00
8526ae1d91 First version of std::array 2022-02-18 23:31:15 +01:00
b8f59710d3 Merge pull request 'Updated README.md' (#7) from fix/README_wording into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/7
2022-02-15 13:54:04 +00:00
184815c75b Updated README.md 2022-02-15 13:53:47 +00:00
74c4a4d582 Merge pull request 'feature/int_hex' (#6) from feature/int_hex into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/6
2022-02-15 10:35:17 +00:00
2933676ea2 Fixed bug in hex formatting, wrote tests for hex int formatting 2022-02-15 11:34:49 +01:00
7115e09aca Implemented count_digits for hex and started implementing digits2_base 2022-02-15 11:13:15 +01:00
f9099ce6ee Added tests for count_digits_base, decimal, binary and hex 2022-02-15 11:12:19 +01:00
4c3024e978 Merge pull request 'Reintroduced previously erroneously removed memcpy' (#5) from fix/memcpy into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/5
2022-02-15 09:29:55 +00:00
a921676b77 Reintroduced previously erroneously removed memcpy 2022-02-15 10:29:23 +01:00
3b9d9775bf Merge pull request 'Replaced <cstdint> with <stdint.h>' (#4) from fix/stdint into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/4
2022-02-15 09:17:58 +00:00
39c1636858 Replaced <cstdint> with <stdint.h> 2022-02-15 10:01:55 +01:00
fc080a8ba0 Merge pull request 'Removed memcpy and <cstring>' (#3) from feature/no_cstring into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/3
2022-02-15 08:59:58 +00:00
e4ebd0163b Removed memcpy and <cstring> 2022-02-15 09:58:47 +01:00
1d2dab93bd Merge pull request 'feature/int_binary' (#2) from feature/int_float_binary into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/2
2022-02-15 08:50:35 +00:00
5e4f242796 Renamed functions dedicated to decimal to better reflect their function 2022-02-15 09:47:28 +01:00
fa0fef37db Rewrote format_impl.h functions to work with both decimal and binary 2022-02-15 09:37:41 +01:00
c3eb1e2909 Wrote tests for binary int formatting 2022-02-15 09:35:36 +01:00
313ca5e981 Merge pull request 'Added static_assert to check for string validity' (#1) from feature/compile_time_string_validity into master
Reviewed-on: http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt/pulls/1
2022-02-14 22:25:16 +00:00
8241045158 Added static_assert to check for string validity 2022-02-14 22:36:43 +01:00
6666a25562 Added license 2022-02-13 23:48:17 +01:00
008c71381c Changed example to show float formatting 2022-02-13 23:43:38 +01:00
9c22b2a084 Added test cases for floats 2022-02-13 23:40:16 +01:00
9bf3e63058 Implemented format_float in terms of format_int 2022-02-13 23:12:25 +01:00
4666b4e737 Added proper error handling for too long ints 2022-02-13 21:57:43 +01:00
1338888e0b Now deciding at compile time how to format minus sign 2022-02-13 21:52:52 +01:00
15 changed files with 748 additions and 105 deletions

1
.gitignore vendored
View File

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

19
LICENSE Normal file
View 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.

View File

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

View File

@@ -2,8 +2,6 @@
#define LOGGER_FORMAT_H #define LOGGER_FORMAT_H
#include <cstring>
#include "format_impl.h" #include "format_impl.h"
#include "parse.h" #include "parse.h"
#include "utility.h" #include "utility.h"
@@ -42,12 +40,12 @@ constexpr inline void check_fmt_params() {
template <fmt_data_t fmt_data, std::integral arg_t> template <fmt_data_t fmt_data, std::integral arg_t>
constexpr inline void format_arg(char* dest, arg_t arg) { constexpr inline void format_arg(char* dest, arg_t arg) {
const_fmt_detail::format_int(dest, arg, fmt_data); const_fmt_detail::format_int<fmt_data>(dest, arg);
}; };
template <fmt_data_t fmt_data, std::floating_point arg_t> template <fmt_data_t fmt_data, std::floating_point arg_t>
constexpr inline void format_arg(char* dest, arg_t arg){ constexpr inline void format_arg(char* dest, arg_t arg){
// const_fmt_detail::format_float(dest, arg, fmt_data); const_fmt_detail::format_float<fmt_data>(dest, arg);
}; };
// TODO: Error handling // TODO: Error handling
@@ -59,7 +57,7 @@ constexpr inline void format_arg(char* dest, const char* arg) {
dest = dest + fmt_data.length - len; dest = dest + fmt_data.length - len;
if (!std::is_constant_evaluated()) { if (!std::is_constant_evaluated()) {
std::memcpy(dest, arg, len); memcpy(dest, arg, len);
return; return;
} }
for (std::size_t i = 0; i < len; ++i) { for (std::size_t i = 0; i < len; ++i) {
@@ -93,7 +91,6 @@ consteval inline auto get_preproc_string() {
} else { } else {
for (int j = 0; j < ast_node.get_node().length; ++j) for (int j = 0; j < ast_node.get_node().length; ++j)
result[i++] = ast_node.get_node().has_zero_padding ? '0' : ' '; result[i++] = ast_node.get_node().has_zero_padding ? '0' : ' ';
//i += ast_node.get_node().length;
} }
} }
@@ -116,10 +113,12 @@ namespace const_fmt {
template <const_fmt_detail::ConstString s, typename... args_t> template <const_fmt_detail::ConstString s, typename... args_t>
constexpr inline auto const_format(args_t... args) { constexpr inline auto const_format(args_t... args) {
constexpr auto ast = const_fmt_detail::parse_string<s>().value; constexpr auto ast = const_fmt_detail::parse_string<s>();
constexpr auto fmt_data = const_fmt_detail::get_fmt_data<ast>(); constexpr auto fmt_data = const_fmt_detail::get_fmt_data<ast.value>();
auto result = const_fmt_detail::get_preproc_string<ast>(); static_assert(ast.is_valid, "Invalid format string");
auto result = const_fmt_detail::get_preproc_string<ast.value>();
const_fmt_detail::format_args<fmt_data>(result.begin(), args...); const_fmt_detail::format_args<fmt_data>(result.begin(), args...);

View File

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

View File

@@ -5,7 +5,7 @@
#include "types.h" #include "types.h"
// clang-const_format off // clang-format off
/* /*
* *
@@ -34,7 +34,7 @@
* *
*/ */
// clang-const_format on // clang-format on
namespace const_fmt { namespace const_fmt_detail { namespace const_fmt { namespace const_fmt_detail {

300
const_fmt/stdlib.h Normal file
View File

@@ -0,0 +1,300 @@
#ifndef CONST_FMT_STDLIB_H
#define CONST_FMT_STDLIB_H
/*
*
* Disclaimer: Very bad (partially at least) implementation of some features of
* the C++ Standard Library. Not meant as a full-on stdlib implementation, only
* for usage in this project (Underlined by the fact, that in this case the
* namespace std is actually const_fmt::std)
*
*/
#ifndef CONST_FMT_NO_CPP_STDLIB
#include <array>
#include <cstring>
#else
#include <stdint.h>
#include <string.h>
namespace const_fmt { namespace std {
using size_t = uint16_t;
/*
*
* type_traits
*
*/
// various
constexpr inline bool is_constant_evaluated() noexcept {
return __builtin_is_constant_evaluated();
}
struct true_type {
constexpr static bool value = true;
};
struct false_type {
constexpr static bool value = false;
};
// is_same
template <typename fist_t, typename second_t>
struct is_same : public false_type {};
template <typename type_t>
struct is_same<type_t, type_t> : public true_type {};
// is_one_of
template <typename...>
struct is_one_of;
template <typename type_t>
struct is_one_of<type_t> {
constexpr static bool value = false;
};
template <typename type_t, typename first_t, typename... rest_t>
struct is_one_of<type_t, first_t, rest_t...> {
constexpr static bool value = std::is_same<type_t, first_t>::value ||
is_one_of<type_t, rest_t...>::value;
};
// remove_x
// clang-format off
template <typename type_t> struct remove_cv { typedef type_t type; };
template <typename type_t> struct remove_cv<const type_t> { typedef type_t type; };
template <typename type_t> struct remove_cv<volatile type_t> { typedef type_t type; };
template <typename type_t> struct remove_cv<const volatile type_t> { typedef type_t type; };
template <typename type_t> struct remove_const { typedef type_t type; };
template <typename type_t> struct remove_const<const type_t> { typedef type_t type; };
template <typename type_t> struct remove_volatile { typedef type_t type; };
template <typename type_t> struct remove_volatile<volatile type_t> { typedef type_t type; };
template <typename type_t> struct remove_reference { using type = type_t; };
template <typename type_t> struct remove_reference<type_t &> { using type = type_t; };
template <typename type_t> struct remove_reference<type_t &&> { using type = type_t; };
template <typename type_t> using remove_reference_t = typename std::remove_reference<type_t>::type;
template <typename type_t> using remove_cv_t = typename std::remove_cv<type_t>::type;
template <typename type_t> using remove_const_t = typename std::remove_const<type_t>::type;
template <typename type_t> using remove_volatile_t = typename std::remove_volatile<type_t>::type;
// clang-format on
// is_integral
template <typename type_t>
using is_integral =
is_one_of<remove_cv_t<type_t>, bool, char, signed char, unsigned char,
wchar_t, char16_t, char32_t, short, unsigned short, int,
unsigned int, long, unsigned long, long long, unsigned long long>;
// is_signed
template <typename _Tp>
using is_signed_integer = is_one_of<remove_cv_t<_Tp>, signed char, signed short,
signed int, signed long, signed long long>;
// is_floating_point
template <typename type_t>
using is_floating_point =
is_one_of<remove_cv_t<type_t>, float, double, long double>;
// make_unsigned
// clang-format off
template <typename type_t> struct make_unsigned { using type = type_t; };
template <> struct make_unsigned<signed char> { using type = char; };
template <> struct make_unsigned<unsigned short> { using type = unsigned short; };
template <> struct make_unsigned<signed int> { using type = unsigned int; };
template <> struct make_unsigned<signed long> { using type = unsigned long; };
template <> struct make_unsigned<signed long long> { using type = unsigned long long; };
// clang-format on
// value definitions
template <typename type_t>
inline constexpr bool is_integral_v = is_integral<type_t>::value;
template <typename type_t>
inline constexpr bool is_signed_integer_v = is_signed_integer<type_t>::value;
template <typename type_t>
inline constexpr bool is_floating_point_v = is_floating_point<type_t>::value;
/*
*
* concepts
*
*/
template <typename type_t>
concept integral = is_integral_v<type_t>;
template <typename type_t>
concept signed_integral = is_signed_integer_v<type_t>;
template <typename type_t>
concept unsigned_integral = integral<type_t> && !signed_integral<type_t>;
template <typename type_t>
concept floating_point = is_floating_point_v<type_t>;
/*
*
* algorithm
*
*/
template <typename input_t, typename output_t>
constexpr inline void copy(const input_t* start, const input_t* end, output_t* dest_start) {
auto temp = start;
while (temp != end)
*(dest_start++) = *(temp++);
}
/*
*
* utility
*
*/
template <typename T>
std::remove_reference_t<T>&& move(T&& arg) noexcept {
return reinterpret_cast<std::remove_reference_t<T>&&>(arg);
}
template <typename T>
constexpr inline void swap(T& t1, T& t2) {
T temp = std::move(t1);
t1 = std::move(t2);
t2 = std::move(temp);
}
template <typename first_t, typename second_t>
struct pair {
first_t first;
second_t second;
};
/*
*
* array
*
*/
// TODO: Is std::size_t really the best bet here?
template <typename data_t, std::size_t t_size>
class array {
public:
template <typename... args_t>
constexpr array(args_t... args) noexcept : m_data{args...} {
static_assert(sizeof...(args) == t_size, "Invalid number of arguments");
}
constexpr array() noexcept = default;
constexpr array(const array&) = default;
constexpr array(array&&) = default;
constexpr array& operator=(const array& other) = default;
constexpr array& operator=(array&& other) = default;
constexpr void swap(array<data_t, t_size>& other) noexcept {
for (int i = 0; i < t_size; ++i) {
using std::swap;
swap(m_data[i], other.m_data[i]);
}
}
constexpr data_t& operator[](std::size_t index) noexcept {
return m_data[index];
}
constexpr const data_t& operator[](std::size_t index) const noexcept {
return m_data[index];
}
using iterator = data_t*;
using const_iterator = const data_t*;
constexpr iterator begin() noexcept {
return &(m_data[0]);
}
constexpr iterator end() noexcept {
return (&(m_data[t_size - 1]) + 1);
}
constexpr const_iterator begin() const noexcept {
return &(m_data[0]);
}
constexpr const_iterator end() const noexcept {
return (&(m_data[t_size - 1]) + 1);
}
constexpr const_iterator cbegin() const noexcept {
return &(m_data[0]);
}
constexpr const_iterator cend() const noexcept {
return (&(m_data[t_size - 1]) + 1);
}
data_t m_data[t_size];
};
}} // namespace const_fmt::std
#endif
#endif // CONST_FMT_STDLIB_H

View File

@@ -2,7 +2,8 @@
#define LOGGER_TYPES_H #define LOGGER_TYPES_H
#include <array>
#include "stdlib.h"
namespace const_fmt { namespace const_fmt_detail { namespace const_fmt { namespace const_fmt_detail {
@@ -19,8 +20,7 @@ template <std::size_t N>
class ConstString { class ConstString {
public: public:
constexpr ConstString(const char (&content)[N]) noexcept { constexpr ConstString(const char (&content)[N]) noexcept {
std::copy(std::begin(content), std::end(content), std::copy(&content[0], (&content[N - 1] + 1), m_content.begin());
std::begin(m_content));
} }
constexpr char operator[](std::size_t index) const noexcept { constexpr char operator[](std::size_t index) const noexcept {

View File

@@ -2,8 +2,9 @@
#define LOGGER_UTILITY_H #define LOGGER_UTILITY_H
#include <cstring> #include <string.h>
#include "stdlib.h"
#include "types.h" #include "types.h"

View File

@@ -1,17 +1,19 @@
#define CONST_FMT_NO_CPP_STDLIB
#include <const_fmt/format.h> #include <const_fmt/format.h>
#include <iostream> //#include <iostream>
#include <string_view> //#include <string_view>
using namespace const_fmt; using namespace const_fmt;
int main() { int main() {
constexpr auto s = "abcdef {:04}"_const_fmt(123); constexpr auto s = "This is an integer: {:08x}, and this is a float: {:09.4b}"_const_fmt(122u, -86.2);
// Convert s (with a type of 'std::array<char, N>') into something // Convert s (with a type of 'std::array<char, N>') into something
// writable to std::cout // writable to std::cout
std::string_view sv{&s[0], s.size()}; // std::string_view sv{&s[0], s.size()};
//
std::cout << sv << std::endl; // std::cout << sv << std::endl;
return 0; return 0;
} }

View File

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

View File

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

103
test/src/format_decimal.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include <const_fmt/format.h>
#include <gtest/gtest.h>
using namespace const_fmt;
using namespace const_fmt::const_fmt_detail;
TEST(FormatDecimal, positive_int) {
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
'0', '0', '0', '2'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(2);
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', '2',
'2', '2', '2', '2'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(22222);
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1',
'2', '3', '4', '5'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4}">(12345);
constexpr std::array<char, 4> control4 = {'6', '7', '8', '9'};
constexpr std::array<char, 4> formatted4 = const_format<"{:4}">(6789);
constexpr std::array<char, 4> control5 = {'f', 'f', 'f', 'f'};
constexpr std::array<char, 4> formatted5 = const_format<"{:4}">(67895);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}
TEST(FormatDecimal, negative_int) {
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
'0', '0', '0', '2'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(-2);
constexpr std::array<char, 8> control2 = {' ', ' ', '-', '2',
'2', '2', '2', '2'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(-22222);
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1',
'2', '3', '4', '5'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4}">(-12345);
constexpr std::array<char, 5> control4 = {'-', '6', '7', '8', '9'};
constexpr std::array<char, 5> formatted4 = const_format<"{:5}">(-6789);
constexpr std::array<char, 5> control5 = {'-', 'f', 'f', 'f', 'f'};
constexpr std::array<char, 5> formatted5 = const_format<"{:05}">(-66789);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}
TEST(FormatDecimal, positive_float) {
constexpr std::array<char, 8> control1 = {'0', '0', '2', '.',
'3', '4', '5', '6'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08.4}">(2.3456);
// Float error: 2323.2 -> 2323.1
constexpr std::array<char, 8> control2 = {' ', ' ', '2', '3',
'2', '3', '.', '1'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8.1}">(2323.2);
constexpr std::array<char, 8> control3 = {'1', '2', '3', '4',
'.', '5', '0', '0'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.3}">(1234.5);
// Float error: .9 -> .8
constexpr std::array<char, 4> control4 = {'f', 'f', '.', '8'};
constexpr std::array<char, 4> formatted4 = const_format<"{:4.1}">(678.9);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
}
TEST(FormatDecimal, negative_float) {
constexpr std::array<char, 8> control1 = {'-', '0', '2', '.',
'3', '4', '5', '6'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08.4}">(-2.3456);
// Float error: 2323.2 -> 2323.1
constexpr std::array<char, 8> control2 = {' ', '-', '2', '3',
'2', '3', '.', '1'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8.1}">(-2323.2);
constexpr std::array<char, 8> control3 = {'-', 'f', 'f', 'f',
'.', '5', '0', '0'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.3}">(-1234.5);
EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3);
}

View File

@@ -6,61 +6,54 @@ using namespace const_fmt;
using namespace const_fmt::const_fmt_detail; using namespace const_fmt::const_fmt_detail;
TEST(Format, positive_int) { TEST(FormatHex, positive_int) {
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0', constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
'0', '0', '0', '2'}; '0', '0', '1', '0'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(2); constexpr std::array<char, 8> formatted1 = const_format<"{:08x}">(0x10);
constexpr std::array<char, 8> control2 = {' ', ' ', ' ', '2', constexpr std::array<char, 8> control2 = {' ', ' ', ' ', 'F',
'2', '2', '2', '2'}; 'F', 'A', '7', '6'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(22222); constexpr std::array<char, 8> formatted2 = const_format<"{:8x}">(0xffa76);
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1', constexpr std::array<char, 8> control3 = {'0', '0', '0', '0',
'2', '3', '4', '5'}; 'B', 'C', 'E', 'F'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4}">(12345); constexpr std::array<char, 8> formatted3 = const_format<"{:08.4x}">(0xbcef);
constexpr std::array<char, 4> control4 = {'6', '7', '8', '9'}; constexpr std::array<char, 4> control4 = {'A', 'D', '0', '1'};
constexpr std::array<char, 4> formatted4 = const_format<"{:4}">(6789); constexpr std::array<char, 4> formatted4 = const_format<"{:4x}">(0xad01);
constexpr std::array<char, 4> control5 = {'f', 'f', 'f', 'f'};
constexpr std::array<char, 4> formatted5 = const_format<"{:4x}">(0x12345);
EXPECT_EQ(control1, formatted1); EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2); EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3); EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4); EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
} }
TEST(Format, negative_int) { TEST(FormatHex, negative_int) {
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0', constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
'0', '0', '0', '2'}; '0', '0', '1', '0'};
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(-2); constexpr std::array<char, 8> formatted1 = const_format<"{:08x}">(-0x10);
constexpr std::array<char, 8> control2 = {' ', ' ', '-', '2', constexpr std::array<char, 8> control2 = {' ', ' ', '-', 'F',
'2', '2', '2', '2'}; 'F', 'A', '7', '6'};
constexpr std::array<char, 8> formatted2 = const_format<"{:8}">(-22222); constexpr std::array<char, 8> formatted2 = const_format<"{:8x}">(-0xffa76);
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1', constexpr std::array<char, 8> control3 = {'-', '0', '0', '0',
'2', '3', '4', '5'}; 'B', 'C', 'E', 'F'};
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4}">(-12345); constexpr std::array<char, 8> formatted3 = const_format<"{:08.4x}">(-0xbcef);
constexpr std::array<char, 5> control4 = {'-', '6', '7', '8', '9'}; constexpr std::array<char, 4> control4 = {'-', 'A', 'D', '1'};
constexpr std::array<char, 5> formatted4 = const_format<"{:5}">(-6789); constexpr std::array<char, 4> formatted4 = const_format<"{:4x}">(-0xad1);
constexpr std::array<char, 4> control5 = {'-', 'f', 'f', 'f'};
constexpr std::array<char, 4> formatted5 = const_format<"{:04x}">(-0x1234);
EXPECT_EQ(control1, formatted1); EXPECT_EQ(control1, formatted1);
EXPECT_EQ(control2, formatted2); EXPECT_EQ(control2, formatted2);
EXPECT_EQ(control3, formatted3); EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4); EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
} }
//
//TEST(Format, positive_float) {
// // TODO
// EXPECT_EQ(true, false);
//}
//
//TEST(Format, negative_float) {
// // TODO
// EXPECT_EQ(true, false);
//}
//
//TEST(Format, string) {
// // TODO
// EXPECT_EQ(true, false);
//}

View File

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