22 Commits

Author SHA1 Message Date
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
f47a23ffef PreZero padding now being handled by preprocessing 2022-02-13 20:47:41 +01:00
4128ede5db English 2022-02-13 19:43:22 +01:00
bb5f4d5272 Renamed a bunch of identifiers to make sure there are no clashes with software this might be used in (fmt -> const_fmt) 2022-02-13 17:56:09 +01:00
3db91aebda Proper example 2022-02-13 17:53:17 +01:00
2ecc001a64 Put everything into a new 'const_fmt' namespace; Renamed namespace detail to const_fmt_detail 2022-02-13 17:48:24 +01:00
70fd273a70 Merge branch 'master' of http://git.mercurial-manifold.eu/an.tsouchlos/const_fmt 2022-02-13 17:41:17 +01:00
9626f5efc4 Added examples folder and CMakeLists.txt in repo root directory 2022-02-13 17:40:33 +01:00
9c5a73e2bd Removed Logger.h 2022-02-13 17:30:40 +01:00
50635a8e2f Removed src directory 2022-02-13 17:29:55 +01:00
f9cbfdd018 Fixed typos in readme 2022-02-10 21:17:41 +00:00
55ff861f67 Renamed inc directory to const_fmt 2022-02-09 17:51:37 +01:00
d26cf43b32 Added note about including library in project 2022-02-09 16:48:02 +00:00
c9e845b025 Added limitations to README 2022-02-09 16:45:04 +00:00
6597a3a360 Added instruction to build and run the tests 2022-02-09 16:36:11 +00:00
5bf165463c Created README 2022-02-09 16:31:45 +00:00
83f7f21635 Added build folder to gitignore 2022-02-09 17:21:46 +01:00
17 changed files with 352 additions and 218 deletions

1
.gitignore vendored
View File

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

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.21)
project(logger)
project(const_fmt)
set(CMAKE_CXX_STANDARD 20)
@@ -9,16 +9,10 @@ if(NOT CMAKE_BUILD_TYPE)
endif()
include_directories(inc)
add_executable(logger src/main.cpp)
include_directories(.)
if(MSVC)
target_compile_options(logger PRIVATE /W4 /WX)
else()
target_compile_options(logger PRIVATE -O3 -Wall -Wextra -pedantic -fno-exceptions)
endif()
add_subdirectory(examples)
option(PACKAGE_TESTS "Build the tests" ON)

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.

55
README.md Normal file
View 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.

View File

@@ -4,12 +4,12 @@
#include <cstring>
#include "format_impl.h"
#include "parse.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>
constexpr inline int get_output_len() {
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>();
}
@@ -42,16 +42,16 @@ constexpr inline void check_fmt_params() {
template <fmt_data_t fmt_data, std::integral arg_t>
constexpr inline void format_arg(char* dest, arg_t arg) {
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>
constexpr inline void format_arg(char* dest, arg_t arg) {
//detail::format_float(dest, arg, fmt_data);
constexpr inline void format_arg(char* dest, arg_t arg){
const_fmt_detail::format_float<arg_t, fmt_data>(dest, arg);
};
// TODO: Error handling
template<fmt_data_t fmt_data>
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;
@@ -74,30 +74,33 @@ 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) {
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');
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())
if (ast_node.is_char()) {
result[i++] = ast_node.get_char();
else
i += ast_node.get_node().length;
} 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 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>
constexpr inline auto format(args_t... args) {
constexpr auto ast = detail::parse_string<s>().value;
constexpr auto fmt_data = detail::get_fmt_data<ast>();
namespace const_fmt {
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;
}
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 <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 <detail::ConstString t_s>
constexpr auto operator""_fmt() {
return fmt_literal_obj_t<t_s>{};
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

View File

@@ -16,7 +16,7 @@
#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>
constexpr inline void format_decimal(char* out, uint_t value, int size) {
if (count_digits(value) > size) return;
constexpr inline void format_decimal(char* out, uint_t value, int n_digits,
int size) {
if (n_digits > size) {
for (int i = 0; i < size; ++i) {
*(out++) = 'f';
}
return;
}
out += size;
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)));
}
// 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>
constexpr inline void format_int(char* out, uint_t value, fmt_data_t fmt_node) {
format_decimal(out, value, fmt_node.length);
template <std::unsigned_integral uint_t, fmt_data_t t_fmt_node>
constexpr inline void format_int(char* out, uint_t value) {
format_decimal(out, value, count_digits(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;
template <std::signed_integral int_t, fmt_data_t t_fmt_node>
constexpr inline void format_int(char* out, int_t value) {
const auto [abs_value, negative] = get_abs_value(value);
if (negative) abs_value = 0 - abs_value;
format_decimal(out + 1 * (negative), abs_value,
fmt_node.length - 1 * (negative));
const std::size_t n_digits = count_digits(abs_value);
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>
constexpr inline void format_float(char* out, float_t value,
fmt_data_t fmt_data) {
template <std::floating_point float_t, fmt_data_t t_fmt_node>
constexpr inline void format_float(char* out, float_t value) {
// clang-format off
constexpr fmt_data_t fmt_node_integral = {
t_fmt_node.has_zero_padding, // has_zero_padding
t_fmt_node.length - t_fmt_node.precision - 1, // length
t_fmt_node.precision, // ignored
t_fmt_node.type, // ignored
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 + fmt_data.length - fmt_data.precision - 1) = '.';
*(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<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

View File

@@ -5,7 +5,7 @@
#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 {
/*
@@ -119,42 +119,42 @@ constexpr inline parse_result_t<unsigned> parse_number(unsigned i) {
template <ConstString s>
constexpr inline parse_result_t<FormatType> parse_type(unsigned i) {
switch (s[i]) {
case 's':
return {true, ++i, FormatType::s};
case 'c':
return {true, ++i, FormatType::c};
case 'b':
return {true, ++i, FormatType::b};
case 'B':
return {true, ++i, FormatType::B};
case 'd':
return {true, ++i, FormatType::d};
case 'o':
return {true, ++i, FormatType::o};
case 'x':
return {true, ++i, FormatType::x};
case 'X':
return {true, ++i, FormatType::X};
case 'a':
return {true, ++i, FormatType::a};
case 'A':
return {true, ++i, FormatType::A};
case 'e':
return {true, ++i, FormatType::e};
case 'E':
return {true, ++i, FormatType::E};
case 'f':
return {true, ++i, FormatType::f};
case 'F':
return {true, ++i, FormatType::F};
case 'g':
return {true, ++i, FormatType::g};
case 'G':
return {true, ++i, FormatType::G};
case 'p':
return {true, ++i, FormatType::p};
default:
return {false, i, FormatType::s};
case 's':
return {true, ++i, FormatType::s};
case 'c':
return {true, ++i, FormatType::c};
case 'b':
return {true, ++i, FormatType::b};
case 'B':
return {true, ++i, FormatType::B};
case 'd':
return {true, ++i, FormatType::d};
case 'o':
return {true, ++i, FormatType::o};
case 'x':
return {true, ++i, FormatType::x};
case 'X':
return {true, ++i, FormatType::X};
case 'a':
return {true, ++i, FormatType::a};
case 'A':
return {true, ++i, FormatType::A};
case 'e':
return {true, ++i, FormatType::e};
case 'E':
return {true, ++i, FormatType::E};
case 'f':
return {true, ++i, FormatType::f};
case 'F':
return {true, ++i, FormatType::F};
case 'g':
return {true, ++i, FormatType::g};
case 'G':
return {true, ++i, FormatType::G};
case 'p':
return {true, ++i, FormatType::p};
default:
return {false, i, FormatType::s};
}
}
@@ -214,7 +214,8 @@ constexpr inline parse_result_t<fmt_node_t> parse_braces(unsigned i) {
}
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;
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

View File

@@ -5,7 +5,7 @@
#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

View File

@@ -7,7 +7,7 @@
#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) {
@@ -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>
consteval inline std::size_t count_ast_format_nodes() {
std::size_t result = 0;
@@ -41,7 +30,8 @@ consteval inline std::size_t count_ast_format_nodes() {
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::size_t position = 0;
@@ -96,7 +86,7 @@ consteval inline int get_ast_output_len() {
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()) {
return *arg ? 1 + const_strlen(arg + 1) : 0;
} else {
@@ -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

7
examples/CMakeLists.txt Normal file
View 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
View 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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -14,8 +14,15 @@ macro(package_add_test TESTNAME)
PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_DIR}"
)
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()
package_add_test(utility_test src/utility.cpp)
package_add_test(parse_test src/parse.cpp)
package_add_test(format_test src/format.cpp)

View File

@@ -1,65 +1,108 @@
#include <format.h>
#include <const_fmt/format.h>
#include <gtest/gtest.h>
using namespace detail;
using namespace const_fmt;
using namespace const_fmt::const_fmt_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);
'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 = format<"{:8}">(22222);
'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 = format<"{:08.4}">(12345);
'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 = 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(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
EXPECT_EQ(control5, formatted5);
}
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);
'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 = format<"{:8}">(-22222);
'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 = format<"{:08.4}">(-12345);
'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 = 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(control2, formatted2);
EXPECT_EQ(control3, formatted3);
EXPECT_EQ(control4, formatted4);
}
TEST(Format, 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);
}
//
//TEST(Format, positive_float) {
// TEST(Format, string) {
// // TODO
// EXPECT_EQ(true, false);
//}
//
//TEST(Format, negative_float) {
// // TODO
// EXPECT_EQ(true, false);
//}
//
//TEST(Format, string) {
// // TODO
// EXPECT_EQ(true, false);
//}

View File

@@ -1,8 +1,8 @@
#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) {
@@ -93,4 +93,4 @@ TEST(Parse, ast_chars_and_nodes) {
' ', ' ', '-'};
EXPECT_EQ(parse_string<"{:06.3}ab d{:8s}{:.4} -">().value, control);
}
}

View File

@@ -1,9 +1,11 @@
#include <utility.h>
#include <const_fmt/utility.h>
#include <gtest/gtest.h>
using namespace const_fmt;
TEST(Utility, const_pow) {
EXPECT_EQ(detail::const_pow(10, 0), 1);
EXPECT_EQ(detail::const_pow(0, 1), 0);
EXPECT_EQ(detail::const_pow(2, 8), 0b1'0000'0000);
EXPECT_EQ(const_fmt_detail::const_pow(10, 0), 1);
EXPECT_EQ(const_fmt_detail::const_pow(0, 1), 0);
EXPECT_EQ(const_fmt_detail::const_pow(2, 8), 0b1'0000'0000);
}