From 20c79ca511eb1b0e17a6fef1f1438b18700f3a63 Mon Sep 17 00:00:00 2001 From: Sean Middleditch Date: Sun, 24 Mar 2019 14:54:13 -0700 Subject: [PATCH] Format spec cleanup (#12) * format_value takes a format_spec directly now * doctest build fixes * Bitfields in format_spec * Cleanup format traits digit handling Almost every use was just accessing 0 anyway * Split format and printf spec parsing functions * Begin making format_spec a reasonable public API type * constexpr + noexcept on basic_format_spec constructor * Pass format_spec by const& because it's not tiny * Rename format_spec.remaining to user * Rename format_spec to format_options * Renames * Finally get rid of options.has_precision The default precision is now ~0u, which I dislike (I like default == 0) but this gets it done. * Update README --- CMakeLists.txt | 1 + README.md | 56 +++++++------ external/CMakeLists.txt | 3 +- external/litexx | 2 +- include/formatxx/_detail/format_arg.h | 14 ++-- include/formatxx/_detail/format_arg_impl.h | 42 +++++----- include/formatxx/_detail/format_impl.h | 18 ++-- include/formatxx/_detail/format_traits.h | 30 +++---- include/formatxx/_detail/parse_format.h | 37 ++++---- include/formatxx/_detail/parse_printf.h | 98 ++++++++++++++++++++++ include/formatxx/_detail/printf_impl.h | 24 +++--- include/formatxx/_detail/write_float.h | 25 +++--- include/formatxx/_detail/write_integer.h | 98 +++++++++++----------- include/formatxx/_detail/write_string.h | 18 ++-- include/formatxx/_detail/write_wide.h | 30 +++---- include/formatxx/format.h | 92 +++++++++++++------- include/formatxx/std_string.h | 8 +- source/format.cc | 28 ++++--- tests/test_format.cc | 14 ++-- 19 files changed, 382 insertions(+), 256 deletions(-) create mode 100644 include/formatxx/_detail/parse_printf.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4beccf7..6308446 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set(FORMATXX_PRIVATE_HEADERS include/formatxx/_detail/format_traits.h include/formatxx/_detail/format_util.h include/formatxx/_detail/parse_format.h + include/formatxx/_detail/parse_printf.h include/formatxx/_detail/parse_unsigned.h include/formatxx/_detail/printf_impl.h include/formatxx/_detail/write_float.h diff --git a/README.md b/README.md index 24f89e5..be0da72 100644 --- a/README.md +++ b/README.md @@ -26,53 +26,55 @@ necessary. Users can easily write their own buffer systems as well. The underlying method of operation of formatxx is to collect a list of arguments via variadic templates, lookup a `format_value` function for each of those arguments, and then pass the format string, an array of format functions, and an array of `void` pointers to arguments into the -the actual formatting function. The header mechanisms that generate these lists of functions -and pointers are intended to be as absolutel light-weight on the compiler as possible. The -actual formatting work is all implemented in a source file and not the header, to keep the +the actual formatting function. Builtin C++ types go through a slightly different mechanism for +the sake of avoiding excessing ADL noise. The header mechanisms that generate these lists of +functions and pointers are intended to be light-weight on the compiler to the extent reasonable. +The actual formatting work is all implemented in a source file and not the header, to keep the header small and cheap to include. ## Usage formatxx can write into user-defined buffers, or a user may use one of the provided buffer types. Formatting is support for any type that has an appropriate `format_value` free function with -the signature `void format_value(formatxx::IWriter&, TheType, formatxx::format_spec)`. For instance: +the signature `void format_value(formatxx::IWriter&, TheType, formatxx::format_options const&)`. +For instance: + +```c++ +#include +#include -```C++ struct Foo { int value }; -void format_value(formatxx::writer& out, Foo const& foo, formatxx::string_view spec) -{ - format(out, "Foo({})", foo.value); +void format_value(formatxx::writer& out, Foo const& foo, formatxx::format_options const& opts) { + format_to(out, "Foo({})", foo.value); } -int main() -{ - std::cout << formatxx::FormatString<>("testing {0}", Foo{123}); +int main() { + std::cout << formatxx::format_as("testing {0}", Foo{123}); } ``` The above will print `testing Foo(123)` to standard output. -The `spec` argument are additional options passed to the formatter. These can be -interpreted by the `format_value` function anyway it sees fit. The -`formatxx::parse_format_spec` function will return a `formatxx::format_spec` structure -with various printf-style flags and options parsed, which are used by default for built-in -format types like integers, floats, and strings. +The `options` argument are additional options passed to the formatter. These are parsed from +format string options. In the `format_to` case user-provided arguments may be provided, +otherwise they are interpreted by Python or printf rules. + +The functions `formatxx::parse_format_spec` and `formatxx::parse_printf_spec` can be used +to interpret format specs from string inputs. -The `formatxx::format(string_view, ...)` template can be used -for formatting a series of arguments into a `std::string` or any compatible string type. +The `formatxx::format_as(string_view, ...)` template can be used +for formatting a series of arguments into any result type that implements a `string`-like +`append` method. Including `formatxx/std_string.h` also provides a `format_string` function +that defaults to returning `std::string` results. -The `formatxx::format(formatxx::writer&, string_view, ...)` template can be used to -write into a write buffer. +The `formatxx::format_to(formatxx::writer&, string_view, ...)` template can be used to +write into a write buffer. This is the recommended way of formatting. The provided write buffers are: -- `fmt::fixed_writer` - a write buffer that will never allocate but only support - `N`-1 characters. -- `fmt::string_writer` - a write buffer that writes into a `std::string`- - compatible type. -- `fmt::buffered_writer>` - a write buffer that - will not allocate for strings up to `N`-1 characters long but will allocate when - necessary if that length is exceeded. +- `formatxx::append_writer` - writes to a `string`-like object using `append`. +- `fmt::container_writer` - writes to a container using `insert` at the end. +- `fmt::span_writer` - writes to a pre-allocated buffer. All three of the provided write buffers guarantee NUL-terminated strings, but support use with string types that are not NUL-terminated (another important use case for diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 00dbc3a..fe86904 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(litexx EXCLUDE_FROM_ALL) -if(FORMATXX_BUILD_TESTS) +if(FORMATXX_BUILD_TESTS AND NOT TARGET doctest) set(DOCTEST_WITH_TESTS OFF CACHE BOOL "enable doctest tests") + set(DOCTEST_WITH_MAIN_IN_STATIC_LIB OFF CACHE BOOL "enable doctest static library") add_subdirectory(doctest EXCLUDE_FROM_ALL) endif() diff --git a/external/litexx b/external/litexx index 9b9d444..30d1d4f 160000 --- a/external/litexx +++ b/external/litexx @@ -1 +1 @@ -Subproject commit 9b9d44400265ce857b7e41e91f34ede66c8d4d7e +Subproject commit 30d1d4fa83e2fa28751700ce35b54620a2ed81ef diff --git a/include/formatxx/_detail/format_arg.h b/include/formatxx/_detail/format_arg.h index eadea0b..428b399 100644 --- a/include/formatxx/_detail/format_arg.h +++ b/include/formatxx/_detail/format_arg.h @@ -66,13 +66,13 @@ enum class formatxx::_detail::format_arg_type { template class formatxx::_detail::basic_format_arg { public: - using thunk_type = result_code(FORMATXX_API*)(basic_format_writer&, void const*, basic_string_view); + using thunk_type = result_code(FORMATXX_API*)(basic_format_writer&, void const*, basic_format_options); constexpr basic_format_arg() noexcept = default; constexpr basic_format_arg(_detail::format_arg_type type, void const* value) noexcept : _type(type), _value(value) {} constexpr basic_format_arg(thunk_type thunk, void const* value) noexcept : _type(_detail::format_arg_type::custom), _thunk(thunk), _value(value) {} - FORMATXX_PUBLIC result_code FORMATXX_API format_into(basic_format_writer& output, basic_string_view spec) const; + FORMATXX_PUBLIC result_code FORMATXX_API format_into(basic_format_writer& output, basic_format_options const& options) const; private: _detail::format_arg_type _type = _detail::format_arg_type::unknown; @@ -91,8 +91,8 @@ class formatxx::_detail::basic_format_arg_list { constexpr basic_format_arg_list() noexcept = default; constexpr basic_format_arg_list(std::initializer_list args) noexcept : _args(args.begin()), _count(args.size()) {} - constexpr result_code format_arg(basic_format_writer& output, size_type index, basic_string_view spec) const { - return index < _count ? _args[index].format_into(output, spec) : result_code::out_of_range; + constexpr result_code format_arg(basic_format_writer& output, size_type index, basic_format_options const& options) const { + return index < _count ? _args[index].format_into(output, options) : result_code::out_of_range; } private: @@ -110,7 +110,7 @@ namespace formatxx::_detail { template struct has_format_value { static constexpr bool value = false; }; template - struct has_format_value&>(), std::declval(), std::declval>()))>> { + struct has_format_value&>(), std::declval(), std::declval>()))>> { static constexpr bool value = true; }; @@ -141,8 +141,8 @@ namespace formatxx::_detail { #undef FORMTAXX_TYPE template - result_code FORMATXX_API format_value_thunk(basic_format_writer& out, void const* ptr, basic_string_view spec) { - format_value(out, *static_cast(ptr), spec); + result_code FORMATXX_API format_value_thunk(basic_format_writer& out, void const* ptr, basic_format_options options) { + format_value(out, *static_cast(ptr), options); return result_code::success; } diff --git a/include/formatxx/_detail/format_arg_impl.h b/include/formatxx/_detail/format_arg_impl.h index 05e4427..709da0f 100644 --- a/include/formatxx/_detail/format_arg_impl.h +++ b/include/formatxx/_detail/format_arg_impl.h @@ -36,67 +36,67 @@ #include template -formatxx::result_code FORMATXX_API formatxx::_detail::basic_format_arg::format_into(basic_format_writer& output, basic_string_view spec) const { +formatxx::result_code FORMATXX_API formatxx::_detail::basic_format_arg::format_into(basic_format_writer& output, basic_format_options const& options) const { switch (_type) { case _detail::format_arg_type::char_t: - _detail::write_char(output, *static_cast(_value), spec); + _detail::write_char(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::wchar: - _detail::write_char(output, *static_cast(_value), spec); + _detail::write_char(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::signed_char: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::unsigned_char: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::signed_int: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::unsigned_int: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::signed_short_int: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::unsigned_short_int: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::signed_long_int: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::unsigned_long_int: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::signed_long_long_int: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::unsigned_long_long_int: - _detail::write_integer(output, *static_cast(_value), spec); + _detail::write_integer(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::single_float: - _detail::write_float(output, *static_cast(_value), spec); + _detail::write_float(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::double_float: - _detail::write_float(output, *static_cast(_value), spec); + _detail::write_float(output, *static_cast(_value), options); return result_code::success; case _detail::format_arg_type::boolean: - _detail::write_string(output, *static_cast(_value) ? _detail::FormatTraits::sTrue : _detail::FormatTraits::sFalse, spec); + _detail::write_string(output, *static_cast(_value) ? _detail::FormatTraits::sTrue : _detail::FormatTraits::sFalse, options); return result_code::success; case _detail::format_arg_type::char_string: - _detail::write_string(output, string_view(*static_cast(_value)), spec); + _detail::write_string(output, string_view(*static_cast(_value)), options); return result_code::success; case _detail::format_arg_type::wchar_string: - _detail::write_string(output, wstring_view(*static_cast(_value)), spec); + _detail::write_string(output, wstring_view(*static_cast(_value)), options); return result_code::success; case _detail::format_arg_type::null_pointer: - _detail::write_string(output, _detail::FormatTraits::sNullptr, spec); + _detail::write_string(output, _detail::FormatTraits::sNullptr, options); return result_code::success; case _detail::format_arg_type::void_pointer: - _detail::write_integer(output, reinterpret_cast(*static_cast(_value)), spec); + _detail::write_integer(output, reinterpret_cast(*static_cast(_value)), options); return result_code::success; case _detail::format_arg_type::custom: - return _thunk(output, _value, spec); + return _thunk(output, _value, options); default: return result_code::success; } diff --git a/include/formatxx/_detail/format_impl.h b/include/formatxx/_detail/format_impl.h index d2e0338..00ba13d 100644 --- a/include/formatxx/_detail/format_impl.h +++ b/include/formatxx/_detail/format_impl.h @@ -33,6 +33,7 @@ #pragma once #include "parse_unsigned.h" +#include "parse_format.h" namespace formatxx::_detail { @@ -86,7 +87,7 @@ namespace formatxx::_detail { index = next_index; } - basic_string_view spec; + basic_format_options options; // if a : follows the number, we have some formatting controls if (*iter == FormatTraits::cFormatSep) { @@ -98,15 +99,22 @@ namespace formatxx::_detail { } if (iter == end) { - // invalid spec + // invalid options result = result_code::malformed_input; break; } - spec = basic_string_view(spec_begin, iter); + basic_parse_spec_result const spec_result = parse_format_spec({ spec_begin, iter }); + if (spec_result.code != result_code::success) { + result = spec_result.code; + break; + } + + options = spec_result.options; + options.user = spec_result.unparsed; } - // after the index/spec, we expect an end to the format marker + // after the index/options, we expect an end to the format marker if (*iter != FormatTraits::cFormatEnd) { // we have something besides a number, no bueno result = result_code::malformed_input; @@ -114,7 +122,7 @@ namespace formatxx::_detail { continue; } - result_code const arg_result = args.format_arg(out, index, spec); + result_code const arg_result = args.format_arg(out, index, options); if (arg_result != result_code::success) { result = arg_result; } diff --git a/include/formatxx/_detail/format_traits.h b/include/formatxx/_detail/format_traits.h index bdba932..3f24f29 100644 --- a/include/formatxx/_detail/format_traits.h +++ b/include/formatxx/_detail/format_traits.h @@ -46,18 +46,19 @@ namespace formatxx::_detail { static constexpr char cSpace = ' '; static constexpr char cHash = '#'; static constexpr char cDot = '.'; - - static constexpr char to_digit(char c) { return c + '0'; } + static constexpr char cZero = '0'; static constexpr char cPrintfSpec = '%'; static constexpr char cPrintfIndex = '$'; - static constexpr string_view sTrue{ "true", 4 }; - static constexpr string_view sFalse{ "false", 5 }; - static constexpr string_view sNullptr{ "nullptr", 7 }; + static constexpr string_view sTrue{ "true" }; + static constexpr string_view sFalse{ "false" }; + static constexpr string_view sNullptr{ "nullptr" }; + + static constexpr string_view sFormatSpecifiers{ "bcsdioxXfFeEaAgG" }; - static constexpr string_view sPrintfSpecifiers{ "bcCsSdioxXufFeEaAgGp", 20 }; - static constexpr string_view sPrintfModifiers{ "hljztL", 6 }; + static constexpr string_view sPrintfSpecifiers{ "bcCsSdioxXufFeEaAgGp" }; + static constexpr string_view sPrintfModifiers{ "hljztL" }; static constexpr char const sDecimalPairs[] = "00010203040506070809" @@ -84,18 +85,19 @@ namespace formatxx::_detail { static constexpr wchar_t cSpace = L' '; static constexpr wchar_t cHash = L'#'; static constexpr wchar_t cDot = L'.'; - - static constexpr wchar_t to_digit(wchar_t c) { return c + L'0'; } + static constexpr wchar_t cZero = L'0'; static constexpr wchar_t cPrintfSpec = L'%'; static constexpr wchar_t cPrintfIndex = L'$'; - static constexpr wstring_view sTrue{ L"true", 4 }; - static constexpr wstring_view sFalse{ L"false", 5 }; - static constexpr wstring_view sNullptr{ L"nullptr", 7 }; + static constexpr wstring_view sTrue{ L"true" }; + static constexpr wstring_view sFalse{ L"false" }; + static constexpr wstring_view sNullptr{ L"nullptr" }; + + static constexpr wstring_view sFormatSpecifiers{ L"bcsdioxXfFeEaAgG" }; - static constexpr wstring_view sPrintfSpecifiers{ L"bcCsSdioxXufFeEaAgGp", 20 }; - static constexpr wstring_view sPrintfModifiers{ L"hljztL", 6 }; + static constexpr wstring_view sPrintfSpecifiers{ L"bcCsSdioxXufFeEaAgGp" }; + static constexpr wstring_view sPrintfModifiers{ L"hljztL" }; static constexpr wchar_t const sDecimalPairs[] = L"00010203040506070809" diff --git a/include/formatxx/_detail/parse_format.h b/include/formatxx/_detail/parse_format.h index 067c2f1..949bab5 100644 --- a/include/formatxx/_detail/parse_format.h +++ b/include/formatxx/_detail/parse_format.h @@ -38,31 +38,30 @@ namespace formatxx { template - FORMATXX_PUBLIC basic_format_spec FORMATXX_API parse_format_spec(basic_string_view spec) noexcept { + FORMATXX_PUBLIC basic_parse_spec_result FORMATXX_API parse_format_spec(basic_string_view spec_string) noexcept { using Traits = _detail::FormatTraits; - basic_format_spec result; + basic_parse_spec_result result; - result.remaining = spec.data(); - CharT const*& start = result.remaining; - CharT const* const end = spec.data() + spec.size(); + CharT const* start = spec_string.data(); + CharT const* const end = start + spec_string.size(); // flags while (start != end) { if (*start == Traits::cPlus) { - result.prepend_sign = true; + result.options.sign = format_sign::always; } else if (*start == Traits::cMinus) { - result.left_justify = true; + result.options.justify = format_justify::left; } - else if (*start == Traits::to_digit(0)) { - result.leading_zeroes = true; + else if (*start == Traits::cZero) { + result.options.leading_zeroes = true; } else if (*start == Traits::cSpace) { - result.prepend_space = true; + result.options.sign = format_sign::space; } else if (*start == Traits::cHash) { - result.alternate_form = true; + result.options.alternate_form = true; } else { break; @@ -71,23 +70,19 @@ namespace formatxx { } // read in width - start = _detail::parse_unsigned(start, end, result.width); + start = _detail::parse_unsigned(start, end, result.options.width); // read in precision, if present if (start != end && *start == Traits::cDot) { - result.has_precision = true; - start = _detail::parse_unsigned(start + 1, end, result.precision); + start = _detail::parse_unsigned(start + 1, end, result.options.precision); } - // read in any of the modifiers like h or l that modify a type code (no effect in our system) - while (start != end && _detail::string_contains(Traits::sPrintfModifiers, *start)) { - ++start; + // generic code specified option allowed (mostly to set format_options on numeric formatting) + if (start != end && _detail::string_contains(Traits::sFormatSpecifiers, *start)) { + result.options.specifier = *start++; } - // generic code specified option allowed (required for printf) - if (start != end && _detail::string_contains(Traits::sPrintfSpecifiers, *start)) { - result.code = *start++; - } + result.unparsed = { start, end }; return result; } diff --git a/include/formatxx/_detail/parse_printf.h b/include/formatxx/_detail/parse_printf.h new file mode 100644 index 0000000..134c122 --- /dev/null +++ b/include/formatxx/_detail/parse_printf.h @@ -0,0 +1,98 @@ +// formatxx - C++ string formatting library. +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non - commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// 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 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. +// +// For more information, please refer to +// +// Authors: +// Sean Middleditch + +#if !defined(_guard_FORMATXX_DETAIL_PARSE_PRINTF_H) +#define _guard_FORMATXX_DETAIL_PARSE_PRINTF_H +#pragma once + +#include "parse_unsigned.h" +#include "format_util.h" + +namespace formatxx { + + template + FORMATXX_PUBLIC basic_parse_spec_result FORMATXX_API parse_printf_spec(basic_string_view spec_string) noexcept { + using Traits = _detail::FormatTraits; + + basic_parse_spec_result result; + + CharT const* start = spec_string.data(); + CharT const* const end = start + spec_string.size(); + + // flags + while (start != end) { + if (*start == Traits::cPlus) { + result.options.sign = format_sign::always; + } + else if (*start == Traits::cMinus) { + result.options.justify = format_justify::left; + } + else if (*start == Traits::cZero) { + result.options.leading_zeroes = true; + } + else if (*start == Traits::cSpace) { + result.options.sign = format_sign::space; + } + else if (*start == Traits::cHash) { + result.options.alternate_form = true; + } + else { + break; + } + ++start; + } + + // read in width + start = _detail::parse_unsigned(start, end, result.options.width); + + // read in precision, if present + if (start != end && *start == Traits::cDot) { + start = _detail::parse_unsigned(start + 1, end, result.options.precision); + } + + // read in any of the modifiers like h or l that modify a type code (no effect in our system) + while (start != end && _detail::string_contains(Traits::sPrintfModifiers, *start)) { + ++start; + } + + // mandatory generic code + if (start == end || !_detail::string_contains(Traits::sPrintfSpecifiers, *start)) { + result.code = result_code::malformed_input; + return result; + } + result.options.specifier = *start++; + + result.unparsed = { start, end }; + return result; + } + +} // namespace formatxx + +#endif // _guard_FORMATXX_DETAIL_PARSE_PRINTF_H diff --git a/include/formatxx/_detail/printf_impl.h b/include/formatxx/_detail/printf_impl.h index e99309d..4b271bf 100644 --- a/include/formatxx/_detail/printf_impl.h +++ b/include/formatxx/_detail/printf_impl.h @@ -32,6 +32,8 @@ #define _guard_FORMATXX_DETAIL_PRINTF_IMPL_H #pragma once +#include "parse_printf.h" + namespace formatxx::_detail { template @@ -69,7 +71,7 @@ namespace formatxx::_detail { continue; } - basic_string_view spec_string; + basic_format_options options; // determine which argument we're going to format (optional in printf syntax) unsigned index = 0; @@ -107,26 +109,26 @@ namespace formatxx::_detail { index = next_index; // the decimal input had nothing to do with position; reset so the call to - // parse_format_spec ensures we have a valid spec, not something like 1#2.3 + // parse_format_spec ensures we have a valid options, not something like 1#2.3 iter = start; } // parse forward through the specification and verify that it's correct and will // properly decode in parse_format_spec later. CharT const* const spec_begin = iter; - basic_format_spec const spec = parse_format_spec(basic_string_view(iter, end)); - spec_string = { spec_begin, spec.remaining }; - if (spec.code == CharT(0)) { - // invalid spec - result = result_code::malformed_input; - break; - } + basic_parse_spec_result const spec_result = parse_printf_spec(basic_string_view(iter, end)); + if (spec_result.code != result_code::success) { + result = spec_result.code; + break; + } + + options = spec_result.options; // prepare for next round - begin = iter = spec.remaining; + begin = iter = spec_result.unparsed.begin(); } - result_code const arg_result = args.format_arg(out, index, spec_string); + result_code const arg_result = args.format_arg(out, index, options); if (arg_result != result_code::success) { result = arg_result; } diff --git a/include/formatxx/_detail/write_float.h b/include/formatxx/_detail/write_float.h index 7dc3111..d9c82bc 100644 --- a/include/formatxx/_detail/write_float.h +++ b/include/formatxx/_detail/write_float.h @@ -46,9 +46,7 @@ namespace formatxx::_detail { } template - void write_float(basic_format_writer& out, double value, basic_string_view spec_string) { - auto const spec = parse_format_spec(spec_string); - + void write_float(basic_format_writer& out, double value, basic_format_options options) { constexpr std::size_t fmt_buf_size = 10; CharT fmt_buf[fmt_buf_size]; CharT* fmt_ptr = fmt_buf + fmt_buf_size; @@ -57,7 +55,7 @@ namespace formatxx::_detail { *--fmt_ptr = 0; // every sprint call must have a valid code (1) - switch (spec.code) { + switch (options.specifier) { case 'a': case 'A': case 'e': @@ -66,7 +64,7 @@ namespace formatxx::_detail { case 'F': case 'g': case 'G': - *--fmt_ptr = spec.code; + *--fmt_ptr = options.specifier; break; default: *--fmt_ptr = 'f'; @@ -80,21 +78,20 @@ namespace formatxx::_detail { *--fmt_ptr = '*'; // these flags are mutually exclusive within themselves (1) - if (spec.prepend_sign) { - *--fmt_ptr = FormatTraits::cPlus; - } - else if (spec.prepend_space) { - *--fmt_ptr = FormatTraits::cSpace; + switch (options.sign) { + case format_sign::negative: break; + case format_sign::always: *--fmt_ptr = FormatTraits::cPlus; break; + case format_sign::space: *--fmt_ptr = FormatTraits::cSpace; break; } // these flags may all be set together (3) - if (spec.left_justify) { + if (options.justify == format_justify::left) { *--fmt_ptr = '-'; } - if (spec.leading_zeroes) { + if (options.leading_zeroes) { *--fmt_ptr = '0'; } - if (spec.alternate_form) { + if (options.alternate_form) { *--fmt_ptr = FormatTraits::cHash; } @@ -104,7 +101,7 @@ namespace formatxx::_detail { constexpr std::size_t buf_size = 1078; CharT buf[buf_size]; - int const result = float_helper(buf, buf_size, fmt_ptr, spec.width, spec.has_precision ? spec.precision : -1, value); + int const result = float_helper(buf, buf_size, fmt_ptr, options.width, options.precision, value); if (result > 0) { out.write({ buf, result < buf_size ? std::size_t(result) : buf_size }); } diff --git a/include/formatxx/_detail/write_integer.h b/include/formatxx/_detail/write_integer.h index 897223a..bcb755c 100644 --- a/include/formatxx/_detail/write_integer.h +++ b/include/formatxx/_detail/write_integer.h @@ -39,33 +39,35 @@ namespace formatxx::_detail { - template void write_integer(basic_format_writer& out, T value, basic_string_view spec); + template void write_integer(basic_format_writer& out, T value, basic_format_options const& options); struct prefix_helper { // type prefix (2), sign (1) static constexpr std::size_t buffer_size() { return 3; } template - static basic_string_view write(CharT* buffer, basic_format_spec const& spec, bool negative) { + static basic_string_view write(CharT* buffer, basic_format_options const& options, bool negative, bool add_sign) { CharT* const end = buffer + buffer_size(); CharT* ptr = end; // add numeric type prefix (2) - if (spec.alternate_form) { - *--ptr = spec.code; - *--ptr = FormatTraits::to_digit(0); + if (options.alternate_form) { + *--ptr = options.specifier; + *--ptr = FormatTraits::cZero; } // add sign (1) - if (negative) { - *--ptr = FormatTraits::cMinus; - } - else if (spec.prepend_sign) { - *--ptr = FormatTraits::cPlus; - } - else if (spec.prepend_space) { - *--ptr = FormatTraits::cSpace; - } + if (add_sign) { + if (negative) { + *--ptr = FormatTraits::cMinus; + } + else if (options.sign == format_sign::always) { + *--ptr = FormatTraits::cPlus; + } + else if (options.sign == format_sign::space) { + *--ptr = FormatTraits::cSpace; + } + } return { ptr, end }; } @@ -77,7 +79,9 @@ namespace formatxx::_detail { // 99 but not 999, so its digits10 is 2, even though the value 255 could be stored // and has 3 digits. template - static constexpr std::size_t buffer_size() { return std::numeric_limits::digits10 + 1; } + static constexpr std::size_t buffer_size = std::numeric_limits::digits10 + 1; + + static constexpr bool use_signs = true; template static basic_string_view write(CharT* buffer, UnsignedT value) { @@ -85,7 +89,7 @@ namespace formatxx::_detail { // which took the notes from Alexandrescu from "Three Optimization Tips for C++" CharT const* const table = FormatTraits::sDecimalPairs; - CharT* const end = buffer + buffer_size(); + CharT* const end = buffer + buffer_size; CharT* ptr = end; // work on every two decimal digits (groups of 100). notes taken from cppformat, @@ -109,7 +113,7 @@ namespace formatxx::_detail { } else { // we have but a single digit left, so this is easy - *--ptr = FormatTraits::to_digit(static_cast(value)); + *--ptr = static_cast(FormatTraits::cZero + value); } return { ptr, end }; @@ -120,11 +124,13 @@ namespace formatxx::_detail { struct hexadecimal_helper { // 2 hex digits per octet template - static constexpr std::size_t buffer_size() { return 2 * sizeof(UnsignedT); } + static constexpr std::size_t buffer_size = 2 * sizeof(UnsignedT); + + static constexpr bool use_signs = false; template static basic_string_view write(CharT* buffer, UnsignedT value) { - CharT* const end = buffer + buffer_size(); + CharT* const end = buffer + buffer_size; CharT* ptr = end; CharT const* const alphabet = LowerCase ? @@ -142,11 +148,13 @@ namespace formatxx::_detail { struct octal_helper { // up to three 3 octal digits per octet - FIXME is that right? I don't think that's right template - static constexpr std::size_t buffer_size() { return 3 * sizeof(UnsignedT); } + static constexpr std::size_t buffer_size = 3 * sizeof(UnsignedT); + + static constexpr bool use_signs = true; template static basic_string_view write(CharT* buffer, UnsignedT value) { - CharT* const end = buffer + buffer_size(); + CharT* const end = buffer + buffer_size; CharT* ptr = end; // the octal alphabet is a subset of hexadecimal, @@ -164,15 +172,17 @@ namespace formatxx::_detail { struct binary_helper { // one digit per bit of the input template - static constexpr std::size_t buffer_size() { return std::numeric_limits::digits; } + static constexpr std::size_t buffer_size = std::numeric_limits::digits; + + static constexpr bool use_signs = true; template static basic_string_view write(CharT* buffer, UnsignedT value) { - CharT* const end = buffer + buffer_size(); + CharT* const end = buffer + buffer_size; CharT* ptr = end; do { - *--ptr = FormatTraits::to_digit(value & 1); + *--ptr = static_cast(FormatTraits::cZero + (value & 1)); } while ((value >>= 1) != 0); return { ptr, end }; @@ -180,7 +190,7 @@ namespace formatxx::_detail { }; template - void write_integer_helper(basic_format_writer & out, ValueT raw_value, basic_format_spec const& spec) { + void write_integer_helper(basic_format_writer & out, ValueT raw_value, basic_format_options const& options) { using unsigned_type = std::make_unsigned_t; // convert to an unsigned value to make the formatting easier; note that must @@ -190,28 +200,28 @@ namespace formatxx::_detail { // calculate prefixes like signs CharT prefix_buffer[prefix_helper::buffer_size()]; - auto const prefix = prefix_helper::write(prefix_buffer, spec, raw_value < 0); + auto const prefix = prefix_helper::write(prefix_buffer, options, raw_value < 0, HelperT::use_signs); // generate the actual number - CharT value_buffer[HelperT::template buffer_size()]; + CharT value_buffer[HelperT::template buffer_size]; auto const result = HelperT::write(value_buffer, unsigned_value); - if (spec.has_precision) { + if (options.precision != ~0u) { out.write(prefix); - write_padded_align_right(out, result, FormatTraits::to_digit(0), spec.precision); + write_padded_align_right(out, result, FormatTraits::cZero, options.precision); } else { std::size_t const output_length = prefix.size() + result.size(); - std::size_t const padding = spec.width > output_length ? spec.width - output_length : 0; + std::size_t const padding = options.width > output_length ? options.width - output_length : 0; - if (spec.left_justify) { + if (options.justify == format_justify::left) { out.write(prefix); out.write(result); write_padding(out, FormatTraits::cSpace, padding); } - else if (spec.leading_zeroes) { + else if (options.leading_zeroes) { out.write(prefix); - write_padding(out, FormatTraits::to_digit(0), padding); + write_padding(out, FormatTraits::cZero, padding); out.write(result); } else { @@ -223,31 +233,23 @@ namespace formatxx::_detail { } template - void write_integer(basic_format_writer & out, T raw, basic_string_view spec_string) { - basic_format_spec spec = parse_format_spec(spec_string); - - switch (spec.code) { + void write_integer(basic_format_writer & out, T raw, basic_format_options const& options) { + switch (options.specifier) { default: case 0: case 'i': - spec.code = 'd'; // code is used literally in alt-form, and 'd' is decimal code - return write_integer_helper(out, raw, spec); case 'd': case 'D': - return write_integer_helper(out, raw, spec); + return write_integer_helper(out, raw, options); case 'x': - spec.prepend_sign = spec.prepend_space = false; // ignored on hex numbers - return write_integer_helper>(out, typename std::make_unsigned::type(raw), spec); + return write_integer_helper>(out, std::make_unsigned_t(raw), options); case 'X': - spec.prepend_sign = spec.prepend_space = false; // ignored on hex numbers - return write_integer_helper>(out, typename std::make_unsigned::type(raw), spec); + return write_integer_helper>(out, std::make_unsigned_t(raw), options); case 'o': - case 'O': - return write_integer_helper(out, raw, spec); + return write_integer_helper(out, raw, options); break; case 'b': - case 'B': - return write_integer_helper(out, raw, spec); + return write_integer_helper(out, raw, options); break; } } diff --git a/include/formatxx/_detail/write_string.h b/include/formatxx/_detail/write_string.h index 43f4813..b8b42fe 100644 --- a/include/formatxx/_detail/write_string.h +++ b/include/formatxx/_detail/write_string.h @@ -37,24 +37,22 @@ namespace formatxx::_detail { template - void write_string(basic_format_writer& out, basic_string_view str, basic_string_view spec_string) { - auto const spec = parse_format_spec(spec_string); - - if (spec.has_precision) { - str = trim_string(str, spec.precision); + void write_string(basic_format_writer& out, basic_string_view str, basic_format_options const& options) { + if (options.precision != ~0u) { + str = trim_string(str, options.precision); } - if (!spec.left_justify) { - write_padded_align_right(out, str, FormatTraits::cSpace, spec.width); + if (options.justify == format_justify::right) { + write_padded_align_right(out, str, FormatTraits::cSpace, options.width); } else { - write_padded_align_left(out, str, FormatTraits::cSpace, spec.width); + write_padded_align_left(out, str, FormatTraits::cSpace, options.width); } } template - void write_char(basic_format_writer& out, CharT ch, basic_string_view spec) { - write_string(out, { &ch, 1 }, spec); + void write_char(basic_format_writer& out, CharT ch, basic_format_options const& options) { + write_string(out, { &ch, 1 }, options); } } // namespace formatxx::_detail diff --git a/include/formatxx/_detail/write_wide.h b/include/formatxx/_detail/write_wide.h index d0b432f..cd7a06f 100644 --- a/include/formatxx/_detail/write_wide.h +++ b/include/formatxx/_detail/write_wide.h @@ -41,51 +41,41 @@ namespace formatxx::_detail { #pragma warning(push) #pragma warning(disable: 4996) - inline void write_char(wformat_writer& out, char ch, wstring_view) noexcept - { + inline void write_char(wformat_writer& out, char ch, wformat_options const&) noexcept { std::mbstate_t state{}; wchar_t wc; std::size_t const rs = std::mbrtowc(&wc, &ch, 1, &state); - if (rs > 0 && rs < static_cast(-2)) - { + if (rs > 0 && rs < static_cast(-2)) { out.write({ &wc, 1 }); } } - inline void write_string(wformat_writer& out, string_view str, wstring_view) noexcept - { + inline void write_string(wformat_writer& out, string_view str, wformat_options const&) noexcept { std::mbstate_t state{}; - for (auto const ch : str) - { + for (auto const ch : str) { wchar_t wc; std::size_t const rs = std::mbrtowc(&wc, &ch, 1, &state); - if (rs < static_cast(-2)) - { + if (rs < static_cast(-2)) { out.write({ &wc, 1 }); } } } - inline void write_char(format_writer& out, wchar_t ch, string_view) noexcept - { + inline void write_char(format_writer& out, wchar_t ch, format_options const&) noexcept { std::mbstate_t state{}; char mb[MB_LEN_MAX]; std::size_t const rs = std::wcrtomb(mb, ch, &state); - if (rs != static_cast(-1)) - { + if (rs != static_cast(-1)) { out.write({ mb, rs }); } } - inline void write_string(format_writer& out, wstring_view str, string_view) noexcept - { + inline void write_string(format_writer& out, wstring_view str, format_options const&) noexcept { std::mbstate_t state{}; char mb[MB_LEN_MAX]; - for (auto const ch : str) - { + for (auto const ch : str) { std::size_t const rs = std::wcrtomb(mb, ch, &state); - if (rs != static_cast(-1)) - { + if (rs != static_cast(-1)) { out.write({ mb, rs }); } } diff --git a/include/formatxx/format.h b/include/formatxx/format.h index f689b1d..38529f6 100644 --- a/include/formatxx/format.h +++ b/include/formatxx/format.h @@ -60,17 +60,20 @@ namespace formatxx { template using basic_string_view = litexx::basic_string_view; template class basic_format_writer; - template class basic_format_spec; + template class basic_format_options; + template class basic_parse_spec_result; - enum class result_code; + enum class result_code : unsigned int; + enum class format_justify : unsigned char; + enum class format_sign : unsigned char; using string_view = basic_string_view; using format_writer = basic_format_writer; - using format_spec = basic_format_spec; + using format_options = basic_format_options; using wstring_view = basic_string_view; using wformat_writer = basic_format_writer; - using wformat_spec = basic_format_spec; + using wformat_options = basic_format_options; template result_code format_to(basic_format_writer& writer, FormatT const& format, Args const& ... args); template result_code printf_to(basic_format_writer& writer, FormatT const& format, Args const& ... args); @@ -79,18 +82,31 @@ namespace formatxx { template ResultT printf_as(FormatT const& format, Args const& ... args); template - result_code format_value_to(basic_format_writer& writer, T const& value, basic_string_view spec = {}); + result_code format_value_to(basic_format_writer& writer, T const& value, basic_format_options const& options = {}); - template FORMATXX_PUBLIC basic_format_spec FORMATXX_API parse_format_spec(basic_string_view spec) noexcept; + template FORMATXX_PUBLIC basic_parse_spec_result FORMATXX_API parse_format_spec(basic_string_view spec_string) noexcept; + template FORMATXX_PUBLIC basic_parse_spec_result FORMATXX_API parse_printf_spec(basic_string_view spec_string) noexcept; } -enum class formatxx::result_code { +enum class formatxx::result_code : unsigned int { success, out_of_range, malformed_input, out_of_space, }; +enum class formatxx::format_justify : unsigned char { + right, + left, + center +}; + +enum class formatxx::format_sign : unsigned char { + negative, + always, + space +}; + #include "formatxx/_detail/append_writer.h" #include "formatxx/_detail/format_arg.h" @@ -105,28 +121,37 @@ class formatxx::basic_format_writer { virtual void write(basic_string_view str) = 0; }; +/// Result from parse_format_spec. +template +class formatxx::basic_parse_spec_result { +public: + result_code code = result_code::success; + basic_format_options options; + basic_string_view unparsed; +}; + /// Extra formatting specifications. template -class formatxx::basic_format_spec { +class formatxx::basic_format_options { public: - CharT const* remaining = nullptr; + constexpr basic_format_options() noexcept : alternate_form(false), leading_zeroes(false) {} + + basic_string_view user; unsigned width = 0; - unsigned precision = 0; - CharT code = 0; - bool has_precision = false; - bool left_justify = false; - bool prepend_sign = false; - bool prepend_space = false; - bool alternate_form = false; - bool leading_zeroes = false; + unsigned precision = ~0u; + CharT specifier = 0; + format_justify justify = format_justify::right; + format_sign sign = format_sign::negative; + bool alternate_form : 1; + bool leading_zeroes : 1; }; namespace formatxx { /// Default format helpers. - FORMATXX_PUBLIC void FORMATXX_API format_value(format_writer& out, string_view str, string_view spec = {}) noexcept; - FORMATXX_PUBLIC void FORMATXX_API format_value(format_writer& out, wstring_view str, string_view spec = {}) noexcept; - FORMATXX_PUBLIC void FORMATXX_API format_value(wformat_writer& out, string_view str, wstring_view spec = {}) noexcept; - FORMATXX_PUBLIC void FORMATXX_API format_value(wformat_writer& out, wstring_view str, wstring_view spec = {}) noexcept; + FORMATXX_PUBLIC void FORMATXX_API format_value(format_writer& out, string_view str, format_options const& options = {}) noexcept; + FORMATXX_PUBLIC void FORMATXX_API format_value(format_writer& out, wstring_view str, format_options const& options = {}) noexcept; + FORMATXX_PUBLIC void FORMATXX_API format_value(wformat_writer& out, string_view str, wformat_options const& options = {}) noexcept; + FORMATXX_PUBLIC void FORMATXX_API format_value(wformat_writer& out, wstring_view str, wformat_options const& options = {}) noexcept; } /// @internal @@ -139,13 +164,15 @@ namespace formatxx::_detail { extern template FORMATXX_PUBLIC formatxx::result_code FORMATXX_API formatxx::_detail::format_impl(basic_format_writer& out, basic_string_view format, basic_format_arg_list args); extern template FORMATXX_PUBLIC formatxx::result_code FORMATXX_API formatxx::_detail::printf_impl(basic_format_writer& out, basic_string_view format, basic_format_arg_list args); -extern template FORMATXX_PUBLIC formatxx::result_code FORMATXX_API formatxx::_detail::basic_format_arg::format_into(basic_format_writer& output, basic_string_view spec) const; -extern template FORMATXX_PUBLIC formatxx::basic_format_spec FORMATXX_API formatxx::parse_format_spec(basic_string_view spec) noexcept; +extern template FORMATXX_PUBLIC formatxx::result_code FORMATXX_API formatxx::_detail::basic_format_arg::format_into(basic_format_writer& output, basic_format_options const& options) const; +extern template FORMATXX_PUBLIC formatxx::basic_parse_spec_result FORMATXX_API formatxx::parse_format_spec(basic_string_view spec_string) noexcept; +extern template FORMATXX_PUBLIC formatxx::basic_parse_spec_result FORMATXX_API formatxx::parse_printf_spec(basic_string_view spec_string) noexcept; extern template FORMATXX_PUBLIC formatxx::result_code FORMATXX_API formatxx::_detail::format_impl(basic_format_writer& out, basic_string_view format, basic_format_arg_list args); extern template FORMATXX_PUBLIC formatxx::result_code FORMATXX_API formatxx::_detail::printf_impl(basic_format_writer& out, basic_string_view format, basic_format_arg_list args); -extern template FORMATXX_PUBLIC formatxx::result_code FORMATXX_API formatxx::_detail::basic_format_arg::format_into(basic_format_writer& output, basic_string_view spec) const; -extern template FORMATXX_PUBLIC formatxx::basic_format_spec FORMATXX_API formatxx::parse_format_spec(basic_string_view spec) noexcept; +extern template FORMATXX_PUBLIC formatxx::result_code FORMATXX_API formatxx::_detail::basic_format_arg::format_into(basic_format_writer& output, basic_format_options const& options) const; +extern template FORMATXX_PUBLIC formatxx::basic_parse_spec_result FORMATXX_API formatxx::parse_format_spec(basic_string_view spec_string) noexcept; +extern template FORMATXX_PUBLIC formatxx::basic_parse_spec_result FORMATXX_API formatxx::parse_printf_spec(basic_string_view spec_string) noexcept; /// Write the string format using the given parameters into a buffer. /// @param writer The write buffer that will receive the formatted text. @@ -173,9 +200,10 @@ formatxx::result_code formatxx::printf_to(basic_format_writer& writer, Fo /// @returns a formatted string. template ResultT formatxx::format_as(FormatT const& format, Args const& ... args) { + using char_type = typename ResultT::value_type; ResultT result; append_writer writer(result); - formatxx::format_to(writer, basic_string_view(format), args...); + _detail::format_impl(writer, basic_string_view(format), { _detail::make_format_arg>(args)... }); return result; } @@ -185,21 +213,21 @@ ResultT formatxx::format_as(FormatT const& format, Args const& ... args) { /// @returns a formatted string. template ResultT formatxx::printf_as(FormatT const& format, Args const& ... args) { + using char_type = typename ResultT::value_type; ResultT result; append_writer writer(result); - formatxx::printf_to(writer, basic_string_view(format), args...); + _detail::printf_impl(writer, basic_string_view(format), { _detail::make_format_arg>(args)... }); return result; } -/// Format a value into a buffer using the given spec. +/// Format a value into a buffer using the given options. /// @param writer The write buffer that will receive the formatted text. /// @param value The value to format. -/// @param spec The format control spec. +/// @param options The format control options. /// @returns a result code indicating any errors. template -formatxx::result_code formatxx::format_value_to(basic_format_writer& writer, T const& value, basic_string_view spec) { - auto arg = _detail::make_format_arg(value); - return arg.format_into(writer, spec); +formatxx::result_code formatxx::format_value_to(basic_format_writer& writer, T const& value, basic_format_options const& options) { + return _detail::make_format_arg(value).format_into(writer, options); } #endif // !defined(_guard_FORMATXX_H) diff --git a/include/formatxx/std_string.h b/include/formatxx/std_string.h index cf266bc..8410240 100644 --- a/include/formatxx/std_string.h +++ b/include/formatxx/std_string.h @@ -38,13 +38,13 @@ namespace formatxx { template - void format_value(basic_format_writer& out, std::basic_string const& string, basic_string_view spec) { - format_value(out, basic_string_view(string.data(), string.size()), spec); + void format_value(basic_format_writer& out, std::basic_string const& string, basic_format_options const& options) { + format_value(out, basic_string_view(string.data(), string.size()), options); } template - void format_value(basic_format_writer& out, std::basic_string_view const& string, basic_string_view spec) { - format_value(out, basic_string_view(string.data(), string.size()), spec); + void format_value(basic_format_writer& out, std::basic_string_view const& string, basic_format_options const& options) { + format_value(out, basic_string_view(string.data(), string.size()), options); } template StringT format_string(FormatT const& format, Args const& ... args) { diff --git a/source/format.cc b/source/format.cc index b932887..ed433ac 100644 --- a/source/format.cc +++ b/source/format.cc @@ -33,34 +33,36 @@ #include #include #include +#include #include #include namespace formatxx { - FORMATXX_PUBLIC void FORMATXX_API format_value(format_writer& output, string_view value, string_view spec) noexcept { - _detail::write_string(output, value, spec); + FORMATXX_PUBLIC void FORMATXX_API format_value(format_writer& output, string_view value, format_options const& options) noexcept { + _detail::write_string(output, value, options); } - FORMATXX_PUBLIC void FORMATXX_API format_value(format_writer& output, wstring_view value, string_view spec) noexcept { - _detail::write_string(output, value, spec); + FORMATXX_PUBLIC void FORMATXX_API format_value(format_writer& output, wstring_view value, format_options const& options) noexcept { + _detail::write_string(output, value, options); } - FORMATXX_PUBLIC void FORMATXX_API format_value(wformat_writer& output, string_view value, wstring_view spec) noexcept { - _detail::write_string(output, value, spec); + FORMATXX_PUBLIC void FORMATXX_API format_value(wformat_writer& output, string_view value, wformat_options const& options) noexcept { + _detail::write_string(output, value, options); } - FORMATXX_PUBLIC void FORMATXX_API format_value(wformat_writer& output, wstring_view value, wstring_view spec) noexcept { - _detail::write_string(output, value, spec); + FORMATXX_PUBLIC void FORMATXX_API format_value(wformat_writer& output, wstring_view value, wformat_options const& options) noexcept { + _detail::write_string(output, value, options); } template FORMATXX_PUBLIC result_code FORMATXX_API _detail::format_impl(basic_format_writer& out, basic_string_view format, basic_format_arg_list args); template FORMATXX_PUBLIC result_code FORMATXX_API _detail::printf_impl(basic_format_writer& out, basic_string_view format, basic_format_arg_list args); - template FORMATXX_PUBLIC result_code FORMATXX_API _detail::basic_format_arg::format_into(basic_format_writer& output, basic_string_view spec) const; - template FORMATXX_PUBLIC basic_format_spec FORMATXX_API parse_format_spec(basic_string_view) noexcept; - + template FORMATXX_PUBLIC result_code FORMATXX_API _detail::basic_format_arg::format_into(basic_format_writer& output, basic_format_options const& options) const; + template FORMATXX_PUBLIC basic_parse_spec_result FORMATXX_API parse_format_spec(basic_string_view spec_string) noexcept; + template FORMATXX_PUBLIC basic_parse_spec_result FORMATXX_API parse_printf_spec(basic_string_view spec_string) noexcept; template FORMATXX_PUBLIC result_code FORMATXX_API _detail::format_impl(basic_format_writer& out, basic_string_view format, basic_format_arg_list args); template FORMATXX_PUBLIC result_code FORMATXX_API _detail::printf_impl(basic_format_writer& out, basic_string_view format, basic_format_arg_list args); - template FORMATXX_PUBLIC result_code FORMATXX_API _detail::basic_format_arg::format_into(basic_format_writer& output, basic_string_view spec) const; - template FORMATXX_PUBLIC basic_format_spec FORMATXX_API parse_format_spec(basic_string_view) noexcept; + template FORMATXX_PUBLIC result_code FORMATXX_API _detail::basic_format_arg::format_into(basic_format_writer& output, basic_format_options const& options) const; + template FORMATXX_PUBLIC basic_parse_spec_result FORMATXX_API parse_format_spec(basic_string_view spec_string) noexcept; + template FORMATXX_PUBLIC basic_parse_spec_result FORMATXX_API parse_printf_spec(basic_string_view spec_string) noexcept; } // namespace formatxx diff --git a/tests/test_format.cc b/tests/test_format.cc index d15b891..f19ede7 100644 --- a/tests/test_format.cc +++ b/tests/test_format.cc @@ -10,18 +10,18 @@ enum class custom_enum { foo, bar }; class custom_type {}; -void format_value(formatxx::format_writer& writer, custom_enum value, formatxx::string_view spec) noexcept { +void format_value(formatxx::format_writer& writer, custom_enum value, formatxx::format_options options) noexcept { switch (value) { - case custom_enum::foo: writer.write("foo"); return; - case custom_enum::bar: writer.write("bar"); return; + case custom_enum::foo: format_value_to(writer, "foo", options); return; + case custom_enum::bar: format_value_to(writer, "bar", options); return; } } -void format_value(formatxx::format_writer& writer, custom_type, formatxx::string_view spec) noexcept { - writer.write("custom"); +void format_value(formatxx::format_writer& writer, custom_type, formatxx::format_options options) noexcept { + format_value_to(writer, "custom", options); } -void format_value(formatxx::format_writer& writer, custom_type const*, formatxx::string_view spec) noexcept { - writer.write("custom pointer"); +void format_value(formatxx::format_writer& writer, custom_type const*, formatxx::format_options options) noexcept { + format_value_to(writer, "custom pointer", options); } template