This implements the initial version of the `std::formatter` class and its specializations. It also implements the following formatting functions: - `format` - `vformat` - `format_to` - `vformat_to` - `format_to_n` - `formatted_size` All functions have a `char` and `wchar_t` version. Parsing the format-spec and using the parsed format-spec hasn't been implemented. The code isn't optimized, neither for speed, nor for size. The goal is to have the rudimentary basics working, which can be used as a basis to improve upon. The formatters used in this commit are simple stubs that will be replaced by real formatters in later commits. The formatters that are slated to be replaced in this patch series don't have an availability macro to avoid merge conflicts. Note the formatter for `bool` uses `0` and `1` instead of "false" and "true". This will be fixed when the stub is replaced with a real formatter. Implements parts of: - P0645 Text Formatting Completes: - LWG3539 format_to must not copy models of output_iterator<const charT&> Reviewed By: ldionne, #libc, vitaut Differential Revision: https://reviews.llvm.org/D96664
939 lines
35 KiB
C++
939 lines
35 KiB
C++
// -*- C++ -*-
|
|
//===--------------------------- format -----------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef _LIBCPP_FORMAT
|
|
#define _LIBCPP_FORMAT
|
|
|
|
/*
|
|
|
|
namespace std {
|
|
// [format.context], class template basic_format_context
|
|
template<class Out, class charT>
|
|
class basic_format_context {
|
|
basic_format_args<basic_format_context> args_; // exposition only
|
|
Out out_; // exposition only
|
|
|
|
public:
|
|
using iterator = Out;
|
|
using char_type = charT;
|
|
template<class T> using formatter_type = formatter<T, charT>;
|
|
|
|
basic_format_arg<basic_format_context> arg(size_t id) const;
|
|
std::locale locale();
|
|
|
|
iterator out();
|
|
void advance_to(iterator it);
|
|
};
|
|
using format_context = basic_format_context<unspecified, char>;
|
|
using wformat_context = basic_format_context<unspecified, wchar_t>;
|
|
|
|
// [format.args], class template basic_format_args
|
|
template<class Context>
|
|
class basic_format_args {
|
|
size_t size_; // exposition only
|
|
const basic_format_arg<Context>* data_; // exposition only
|
|
|
|
public:
|
|
basic_format_args() noexcept;
|
|
|
|
template<class... Args>
|
|
basic_format_args(const format-arg-store<Context, Args...>& store) noexcept;
|
|
|
|
basic_format_arg<Context> get(size_t i) const noexcept;
|
|
};
|
|
using format_args = basic_format_args<format_context>;
|
|
using wformat_args = basic_format_args<wformat_context>;
|
|
|
|
|
|
template<class Out, class charT>
|
|
using format_args_t = basic_format_args<basic_format_context<Out, charT>>;
|
|
|
|
// [format.functions], formatting functions
|
|
template<class... Args>
|
|
string format(string_view fmt, const Args&... args);
|
|
template<class... Args>
|
|
wstring format(wstring_view fmt, const Args&... args);
|
|
template<class... Args>
|
|
string format(const locale& loc, string_view fmt, const Args&... args);
|
|
template<class... Args>
|
|
wstring format(const locale& loc, wstring_view fmt, const Args&... args);
|
|
|
|
string vformat(string_view fmt, format_args args);
|
|
wstring vformat(wstring_view fmt, wformat_args args);
|
|
string vformat(const locale& loc, string_view fmt, format_args args);
|
|
wstring vformat(const locale& loc, wstring_view fmt, wformat_args args);
|
|
|
|
template<class Out, class... Args>
|
|
Out format_to(Out out, string_view fmt, const Args&... args);
|
|
template<class Out, class... Args>
|
|
Out format_to(Out out, wstring_view fmt, const Args&... args);
|
|
template<class Out, class... Args>
|
|
Out format_to(Out out, const locale& loc, string_view fmt, const Args&... args);
|
|
template<class Out, class... Args>
|
|
Out format_to(Out out, const locale& loc, wstring_view fmt, const Args&... args);
|
|
|
|
template<class Out>
|
|
Out vformat_to(Out out, string_view fmt,
|
|
format_args_t<type_identity_t<Out>, char> args);
|
|
template<class Out>
|
|
Out vformat_to(Out out, wstring_view fmt,
|
|
format_args_t<type_identity_t<Out>, wchar_t> args);
|
|
template<class Out>
|
|
Out vformat_to(Out out, const locale& loc, string_view fmt,
|
|
format_args_t<type_identity_t<Out>, char> args);
|
|
template<class Out>
|
|
Out vformat_to(Out out, const locale& loc, wstring_view fmt,
|
|
format_args_t<type_identity_t<Out>, wchar_t> args);
|
|
|
|
template<class Out> struct format_to_n_result {
|
|
Out out;
|
|
iter_difference_t<Out> size;
|
|
};
|
|
|
|
template<class Out, class... Args>
|
|
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
|
string_view fmt, const Args&... args);
|
|
template<class Out, class... Args>
|
|
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
|
wstring_view fmt, const Args&... args);
|
|
template<class Out, class... Args>
|
|
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
|
const locale& loc, string_view fmt,
|
|
const Args&... args);
|
|
template<class Out, class... Args>
|
|
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
|
|
const locale& loc, wstring_view fmt,
|
|
const Args&... args);
|
|
|
|
template<class... Args>
|
|
size_t formatted_size(string_view fmt, const Args&... args);
|
|
template<class... Args>
|
|
size_t formatted_size(wstring_view fmt, const Args&... args);
|
|
template<class... Args>
|
|
size_t formatted_size(const locale& loc, string_view fmt, const Args&... args);
|
|
template<class... Args>
|
|
size_t formatted_size(const locale& loc, wstring_view fmt, const Args&... args);
|
|
|
|
// [format.formatter], formatter
|
|
template<> struct formatter<char, char>;
|
|
template<> struct formatter<char, wchar_t>;
|
|
template<> struct formatter<wchar_t, wchar_t>;
|
|
|
|
template<> struct formatter<charT*, charT>;
|
|
template<> struct formatter<const charT*, charT>;
|
|
template<size_t N> struct formatter<const charT[N], charT>;
|
|
template<class traits, class Allocator>
|
|
struct formatter<basic_string<charT, traits, Allocator>, charT>;
|
|
template<class traits>
|
|
struct formatter<basic_string_view<charT, traits>, charT>;
|
|
|
|
// [format.parse.ctx], class template basic_format_parse_context
|
|
template<class charT>
|
|
class basic_format_parse_context {
|
|
public:
|
|
using char_type = charT;
|
|
using const_iterator = typename basic_string_view<charT>::const_iterator;
|
|
using iterator = const_iterator;
|
|
|
|
private:
|
|
iterator begin_; // exposition only
|
|
iterator end_; // exposition only
|
|
enum indexing { unknown, manual, automatic }; // exposition only
|
|
indexing indexing_; // exposition only
|
|
size_t next_arg_id_; // exposition only
|
|
size_t num_args_; // exposition only
|
|
|
|
public:
|
|
constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt,
|
|
size_t num_args = 0) noexcept;
|
|
basic_format_parse_context(const basic_format_parse_context&) = delete;
|
|
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
|
|
|
|
constexpr const_iterator begin() const noexcept;
|
|
constexpr const_iterator end() const noexcept;
|
|
constexpr void advance_to(const_iterator it);
|
|
|
|
constexpr size_t next_arg_id();
|
|
constexpr void check_arg_id(size_t id);
|
|
};
|
|
using format_parse_context = basic_format_parse_context<char>;
|
|
using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
|
|
|
// [format.arguments], arguments
|
|
// [format.arg], class template basic_format_arg
|
|
template<class Context>
|
|
class basic_format_arg {
|
|
public:
|
|
class handle;
|
|
|
|
private:
|
|
using char_type = typename Context::char_type; // exposition only
|
|
|
|
variant<monostate, bool, char_type,
|
|
int, unsigned int, long long int, unsigned long long int,
|
|
float, double, long double,
|
|
const char_type*, basic_string_view<char_type>,
|
|
const void*, handle> value; // exposition only
|
|
|
|
template<class T> explicit basic_format_arg(const T& v) noexcept; // exposition only
|
|
explicit basic_format_arg(float n) noexcept; // exposition only
|
|
explicit basic_format_arg(double n) noexcept; // exposition only
|
|
explicit basic_format_arg(long double n) noexcept; // exposition only
|
|
explicit basic_format_arg(const char_type* s); // exposition only
|
|
|
|
template<class traits>
|
|
explicit basic_format_arg(
|
|
basic_string_view<char_type, traits> s) noexcept; // exposition only
|
|
|
|
template<class traits, class Allocator>
|
|
explicit basic_format_arg(
|
|
const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only
|
|
|
|
explicit basic_format_arg(nullptr_t) noexcept; // exposition only
|
|
|
|
template<class T>
|
|
explicit basic_format_arg(const T* p) noexcept; // exposition only
|
|
|
|
public:
|
|
basic_format_arg() noexcept;
|
|
|
|
explicit operator bool() const noexcept;
|
|
};
|
|
|
|
template<class Visitor, class Context>
|
|
see below visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
|
|
|
|
// [format.arg.store], class template format-arg-store
|
|
template<class Context, class... Args>
|
|
struct format-arg-store { // exposition only
|
|
array<basic_format_arg<Context>, sizeof...(Args)> args;
|
|
};
|
|
|
|
template<class Context = format_context, class... Args>
|
|
format-arg-store<Context, Args...>
|
|
make_format_args(const Args&... args);
|
|
template<class... Args>
|
|
format-arg-store<wformat_context, Args...>
|
|
make_wformat_args(const Args&... args);
|
|
|
|
// [format.error], class format_error
|
|
class format_error : public runtime_error {
|
|
public:
|
|
explicit format_error(const string& what_arg);
|
|
explicit format_error(const char* what_arg);
|
|
};
|
|
|
|
// [format.parse.ctx], class template basic_format_parse_context
|
|
template<class charT>
|
|
class basic_format_parse_context {
|
|
public:
|
|
using char_type = charT;
|
|
using const_iterator = typename basic_string_view<charT>::const_iterator;
|
|
using iterator = const_iterator;
|
|
|
|
private:
|
|
iterator begin_; // exposition only
|
|
iterator end_; // exposition only
|
|
enum indexing { unknown, manual, automatic }; // exposition only
|
|
indexing indexing_; // exposition only
|
|
size_t next_arg_id_; // exposition only
|
|
size_t num_args_; // exposition only
|
|
|
|
public:
|
|
constexpr explicit basic_format_parse_context(basic_string_view<charT> fmt,
|
|
size_t num_args = 0) noexcept;
|
|
basic_format_parse_context(const basic_format_parse_context&) = delete;
|
|
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
|
|
|
|
constexpr const_iterator begin() const noexcept;
|
|
constexpr const_iterator end() const noexcept;
|
|
constexpr void advance_to(const_iterator it);
|
|
|
|
constexpr size_t next_arg_id();
|
|
constexpr void check_arg_id(size_t id);
|
|
};
|
|
using format_parse_context = basic_format_parse_context<char>;
|
|
using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
|
}
|
|
|
|
*/
|
|
|
|
// Make sure all feature-test macros are available.
|
|
#include <version>
|
|
// Enable the contents of the header only when libc++ was built with LIBCXX_ENABLE_INCOMPLETE_FEATURES.
|
|
#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
|
|
|
|
#include <__config>
|
|
#include <__debug>
|
|
#include <__format/format_arg.h>
|
|
#include <__format/format_args.h>
|
|
#include <__format/format_context.h>
|
|
#include <__format/format_error.h>
|
|
#include <__format/format_parse_context.h>
|
|
#include <__format/format_string.h>
|
|
#include <__format/formatter.h>
|
|
#include <__variant/monostate.h>
|
|
#include <array>
|
|
#include <concepts>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
|
|
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
|
|
#include <locale>
|
|
#endif
|
|
|
|
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
|
#pragma GCC system_header
|
|
#endif
|
|
|
|
_LIBCPP_PUSH_MACROS
|
|
#include <__undef_macros>
|
|
|
|
_LIBCPP_BEGIN_NAMESPACE_STD
|
|
|
|
#if _LIBCPP_STD_VER > 17
|
|
|
|
// TODO FMT Remove this once we require compilers with proper C++20 support.
|
|
// If the compiler has no concepts support, the format header will be disabled.
|
|
// Without concepts support enable_if needs to be used and that too much effort
|
|
// to support compilers with partial C++20 support.
|
|
#if !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
|
|
|
// TODO FMT Evaluate which templates should be external templates. This
|
|
// improves the efficiency of the header. However since the header is still
|
|
// under heavy development and not all classes are stable it makes no sense
|
|
// to do this optimization now.
|
|
|
|
using format_args = basic_format_args<format_context>;
|
|
using wformat_args = basic_format_args<wformat_context>;
|
|
|
|
template <class _OutIt, class _CharT>
|
|
using format_args_t = basic_format_args<basic_format_context<_OutIt, _CharT>>;
|
|
|
|
template <class _Context, class... _Args>
|
|
struct _LIBCPP_TEMPLATE_VIS __format_arg_store {
|
|
// TODO FMT Use a built-in array.
|
|
array<basic_format_arg<_Context>, sizeof...(_Args)> __args;
|
|
};
|
|
|
|
template <class _Context = format_context, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...>
|
|
make_format_args(const _Args&... __args) {
|
|
return {basic_format_arg<_Context>(__args)...};
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI __format_arg_store<wformat_context, _Args...>
|
|
make_wformat_args(const _Args&... __args) {
|
|
return _VSTD::make_format_args<wformat_context>(__args...);
|
|
}
|
|
|
|
namespace __format {
|
|
template <class _Tp, class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS __formatter_char {
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) {
|
|
// TODO FMT Implement this function.
|
|
return __parse_ctx.begin();
|
|
}
|
|
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto format(_Tp __c, auto& __ctx) -> decltype(__ctx.out()) {
|
|
// TODO FMT Implement the parsed formatting arguments.
|
|
auto __out_it = __ctx.out();
|
|
*__out_it++ = _CharT(__c);
|
|
return __out_it;
|
|
}
|
|
};
|
|
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS __formatter_c_string {
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) {
|
|
// TODO FMT Implement this function.
|
|
return __parse_ctx.begin();
|
|
}
|
|
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto format(const _CharT* __str, auto& __ctx) -> decltype(__ctx.out()) {
|
|
// TODO FMT Implement the parsed formatting arguments.
|
|
auto __out_it = __ctx.out();
|
|
while (*__str)
|
|
*__out_it++ = *__str++;
|
|
return __out_it;
|
|
}
|
|
};
|
|
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS __formatter_string {
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) {
|
|
// TODO FMT Implement this function.
|
|
return __parse_ctx.begin();
|
|
}
|
|
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto format(basic_string_view<_CharT> __str, auto& __ctx)
|
|
-> decltype(__ctx.out()) {
|
|
// TODO FMT Implement the parsed formatting arguments.
|
|
auto __out_it = __ctx.out();
|
|
for (const auto __c : __str)
|
|
*__out_it++ = __c;
|
|
return __out_it;
|
|
}
|
|
};
|
|
|
|
template <class _Tp, class _CharT>
|
|
requires(is_arithmetic_v<_Tp> &&
|
|
!same_as<_Tp, bool>) struct _LIBCPP_HIDE_FROM_ABI
|
|
__formatter_arithmetic {
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) {
|
|
// TODO FMT Implement
|
|
return __parse_ctx.begin();
|
|
}
|
|
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto format(_Tp __value, auto& __ctx) -> decltype(__ctx.out()) {
|
|
return __handle_format(__value, __ctx);
|
|
}
|
|
|
|
private:
|
|
template <class _Uv>
|
|
_LIBCPP_HIDDEN static string
|
|
__convert(_Uv __value) requires(same_as<_CharT, char>) {
|
|
return _VSTD::to_string(__value);
|
|
}
|
|
template <class _Uv>
|
|
_LIBCPP_HIDDEN static wstring
|
|
__convert(_Uv __value) requires(same_as<_CharT, wchar_t>) {
|
|
return _VSTD::to_wstring(__value);
|
|
}
|
|
|
|
template <class _Uv>
|
|
_LIBCPP_HIDDEN auto __handle_format(_Uv __value, auto& __ctx)
|
|
-> decltype(__ctx.out())
|
|
#ifndef _LIBCPP_HAS_NO_INT128
|
|
requires(!same_as<_Uv, __int128_t> && !same_as<_Uv, __uint128_t>)
|
|
#endif
|
|
{
|
|
// TODO FMT Implement using formatting arguments
|
|
// TODO FMT Improve PoC since using std::to_string is inefficient.
|
|
// Note the code doesn't use std::string::iterator since the unit tests
|
|
// test with debug iterators and they fail with strings created from
|
|
// std::to_string.
|
|
auto __str = __convert(__value);
|
|
auto __out_it = __ctx.out();
|
|
for (size_t __i = 0, __e = __str.size(); __i != __e; ++__i)
|
|
*__out_it++ = __str[__i];
|
|
return __out_it;
|
|
}
|
|
#ifndef _LIBCPP_HAS_NO_INT128
|
|
template <class _Uv>
|
|
_LIBCPP_HIDDEN auto __handle_format(_Uv __value, auto& __ctx)
|
|
-> decltype(__ctx.out()) requires(same_as<_Uv, __int128_t> ||
|
|
same_as<_Uv, __uint128_t>) {
|
|
using _To = conditional_t<is_signed_v<_Uv>, long long, unsigned long long>;
|
|
// TODO FMT Implement full 128-bit support.
|
|
if (__value < numeric_limits<_To>::min() ||
|
|
__value > numeric_limits<_To>::max())
|
|
__throw_format_error("128-bit value is outside of implemented range");
|
|
|
|
return __handle_format(static_cast<_To>(__value), __ctx);
|
|
}
|
|
#endif
|
|
};
|
|
} // namespace __format
|
|
|
|
// These specializations are helper stubs and not proper formatters.
|
|
// TODO FMT Implement the proper formatter specializations.
|
|
|
|
// [format.formatter.spec]/2.1 The specializations
|
|
|
|
template <>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<char, char>
|
|
: public __format::__formatter_char<char, char> {};
|
|
|
|
template <>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<char, wchar_t>
|
|
: public __format::__formatter_char<char, wchar_t> {};
|
|
|
|
template <>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<wchar_t, wchar_t>
|
|
: public __format::__formatter_char<wchar_t, wchar_t> {};
|
|
|
|
// [format.formatter.spec]/2.2 For each charT, the string type specializations
|
|
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<_CharT*, _CharT>
|
|
: public __format::__formatter_c_string<_CharT> {
|
|
using _Base = __format::__formatter_c_string<_CharT>;
|
|
|
|
_LIBCPP_HIDE_FROM_ABI auto format(_CharT* __str, auto& __ctx)
|
|
-> decltype(__ctx.out()) {
|
|
_LIBCPP_ASSERT(__str, "The basic_format_arg constructor should have "
|
|
"prevented an invalid pointer");
|
|
return _Base::format(__str, __ctx);
|
|
}
|
|
};
|
|
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<const _CharT*, _CharT>
|
|
: public __format::__formatter_c_string<_CharT> {};
|
|
|
|
template <class _CharT, size_t _Size>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<const _CharT[_Size], _CharT>
|
|
: public __format::__formatter_string<_CharT> {
|
|
using _Base = __format::__formatter_string<_CharT>;
|
|
|
|
_LIBCPP_HIDE_FROM_ABI auto format(const _CharT __str[_Size], auto& __ctx)
|
|
-> decltype(__ctx.out()) {
|
|
return _Base::format(_VSTD::basic_string_view<_CharT>(__str, _Size), __ctx);
|
|
}
|
|
};
|
|
|
|
template <class _CharT, class _Traits, class _Allocator>
|
|
struct _LIBCPP_TEMPLATE_VIS
|
|
formatter<basic_string<_CharT, _Traits, _Allocator>, _CharT>
|
|
: public __format::__formatter_string<_CharT> {
|
|
using _Base = __format::__formatter_string<_CharT>;
|
|
|
|
_LIBCPP_HIDE_FROM_ABI auto
|
|
format(const basic_string<_CharT, _Traits, _Allocator>& __str, auto& __ctx)
|
|
-> decltype(__ctx.out()) {
|
|
return _Base::format(_VSTD::basic_string_view<_CharT>(__str), __ctx);
|
|
}
|
|
};
|
|
|
|
template <class _CharT, class _Traits>
|
|
struct _LIBCPP_TEMPLATE_VIS
|
|
formatter<basic_string_view<_CharT, _Traits>, _CharT>
|
|
: public __format::__formatter_string<_CharT> {};
|
|
|
|
// [format.formatter.spec]/2.3
|
|
// For each charT, for each cv-unqualified arithmetic type ArithmeticT other
|
|
// than char, wchar_t, char8_t, char16_t, or char32_t, a specialization
|
|
|
|
// Boolean.
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<bool, _CharT> {
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto parse(auto& __parse_ctx) -> decltype(__parse_ctx.begin()) {
|
|
// TODO FMT Implement
|
|
return __parse_ctx.begin();
|
|
}
|
|
|
|
_LIBCPP_HIDE_FROM_ABI
|
|
auto format(bool __b, auto& __ctx) -> decltype(__ctx.out()) {
|
|
// TODO FMT Implement using formatting arguments
|
|
auto __out_it = __ctx.out();
|
|
*__out_it++ = _CharT('0') + __b;
|
|
return __out_it;
|
|
}
|
|
};
|
|
|
|
// Signed integral types.
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<signed char, _CharT>
|
|
: public __format::__formatter_arithmetic<signed char, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<short, _CharT>
|
|
: public __format::__formatter_arithmetic<short, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<int, _CharT>
|
|
: public __format::__formatter_arithmetic<int, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<long, _CharT>
|
|
: public __format::__formatter_arithmetic<long, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<long long, _CharT>
|
|
: public __format::__formatter_arithmetic<long long, _CharT> {};
|
|
#ifndef _LIBCPP_HAS_NO_INT128
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<__int128_t, _CharT>
|
|
: public __format::__formatter_arithmetic<__int128_t, _CharT> {};
|
|
#endif
|
|
|
|
// Unsigned integral types.
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned char, _CharT>
|
|
: public __format::__formatter_arithmetic<unsigned char, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned short, _CharT>
|
|
: public __format::__formatter_arithmetic<unsigned short, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned, _CharT>
|
|
: public __format::__formatter_arithmetic<unsigned, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned long, _CharT>
|
|
: public __format::__formatter_arithmetic<unsigned long, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<unsigned long long, _CharT>
|
|
: public __format::__formatter_arithmetic<unsigned long long, _CharT> {};
|
|
#ifndef _LIBCPP_HAS_NO_INT128
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS formatter<__uint128_t, _CharT>
|
|
: public __format::__formatter_arithmetic<__uint128_t, _CharT> {};
|
|
#endif
|
|
|
|
// Floating point types.
|
|
// TODO FMT There are no replacements for the floating point stubs due to not
|
|
// having floating point support in std::to_chars yet. These stubs aren't
|
|
// removed since they are useful for developing the real versions.
|
|
// Ultimately the stubs should be implemented properly and this code can be
|
|
// removed.
|
|
#if 0
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<float, _CharT>
|
|
: public __format::__formatter_arithmetic<float, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
|
formatter<double, _CharT>
|
|
: public __format::__formatter_arithmetic<double, _CharT> {};
|
|
template <class _CharT>
|
|
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT
|
|
formatter<long double, _CharT>
|
|
: public __format::__formatter_arithmetic<long double, _CharT> {};
|
|
#endif
|
|
|
|
namespace __format {
|
|
|
|
template <class _CharT, class _ParseCtx, class _Ctx>
|
|
_LIBCPP_HIDE_FROM_ABI const _CharT*
|
|
__handle_replacement_field(const _CharT* __begin, const _CharT* __end,
|
|
_ParseCtx& __parse_ctx, _Ctx& __ctx) {
|
|
__format::__parse_number_result __r =
|
|
__format::__parse_arg_id(__begin, __end, __parse_ctx);
|
|
|
|
switch (*__r.__ptr) {
|
|
case _CharT(':'):
|
|
// The arg-id has a format-specifier, advance the input to the format-spec.
|
|
__parse_ctx.advance_to(__r.__ptr + 1);
|
|
break;
|
|
case _CharT('}'):
|
|
// The arg-id has no format-specifier.
|
|
__parse_ctx.advance_to(__r.__ptr);
|
|
break;
|
|
default:
|
|
__throw_format_error(
|
|
"The replacement field arg-id should terminate at a ':' or '}'");
|
|
}
|
|
|
|
_VSTD::visit_format_arg(
|
|
[&](auto __arg) {
|
|
if constexpr (same_as<decltype(__arg), monostate>)
|
|
__throw_format_error("Argument index out of bounds");
|
|
else {
|
|
formatter<decltype(__arg), _CharT> __formatter;
|
|
__parse_ctx.advance_to(__formatter.parse(__parse_ctx));
|
|
__ctx.advance_to(__formatter.format(__arg, __ctx));
|
|
}
|
|
},
|
|
__ctx.arg(__r.__value));
|
|
|
|
__begin = __parse_ctx.begin();
|
|
if (__begin == __end || *__begin != _CharT('}'))
|
|
__throw_format_error("The replacement field misses a terminating '}'");
|
|
|
|
return ++__begin;
|
|
}
|
|
|
|
template <class _ParseCtx, class _Ctx>
|
|
_LIBCPP_HIDE_FROM_ABI typename _Ctx::iterator
|
|
__vformat_to(_ParseCtx&& __parse_ctx, _Ctx&& __ctx) {
|
|
using _CharT = typename _ParseCtx::char_type;
|
|
static_assert(same_as<typename _Ctx::char_type, _CharT>);
|
|
|
|
const _CharT* __begin = __parse_ctx.begin();
|
|
const _CharT* __end = __parse_ctx.end();
|
|
typename _Ctx::iterator __out_it = __ctx.out();
|
|
while (__begin != __end) {
|
|
switch (*__begin) {
|
|
case _CharT('{'):
|
|
++__begin;
|
|
if (__begin == __end)
|
|
__throw_format_error("The format string terminates at a '{'");
|
|
|
|
if (*__begin != _CharT('{')) [[likely]] {
|
|
__ctx.advance_to(_VSTD::move(__out_it));
|
|
__begin =
|
|
__handle_replacement_field(__begin, __end, __parse_ctx, __ctx);
|
|
__out_it = __ctx.out();
|
|
|
|
// The output is written and __begin points to the next character. So
|
|
// start the next iteration.
|
|
continue;
|
|
}
|
|
// The string is an escape character.
|
|
break;
|
|
|
|
case _CharT('}'):
|
|
++__begin;
|
|
if (__begin == __end || *__begin != _CharT('}'))
|
|
__throw_format_error(
|
|
"The format string contains an invalid escape sequence");
|
|
|
|
break;
|
|
}
|
|
|
|
// Copy the character to the output verbatim.
|
|
*__out_it++ = *__begin++;
|
|
}
|
|
return __out_it;
|
|
}
|
|
|
|
} // namespace __format
|
|
|
|
template <class _OutIt, class _CharT>
|
|
requires(output_iterator<_OutIt, const _CharT&>) _LIBCPP_HIDE_FROM_ABI _OutIt
|
|
__vformat_to(_OutIt __out_it, basic_string_view<_CharT> __fmt,
|
|
format_args_t<type_identity_t<_OutIt>, _CharT> __args) {
|
|
return __format::__vformat_to(
|
|
basic_format_parse_context{__fmt, __args.__size()},
|
|
_VSTD::__format_context_create(_VSTD::move(__out_it), __args));
|
|
}
|
|
|
|
template <output_iterator<const char&> _OutIt>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
|
|
vformat_to(_OutIt __out_it, string_view __fmt,
|
|
format_args_t<type_identity_t<_OutIt>, char> __args) {
|
|
return _VSTD::__vformat_to(_VSTD::move(__out_it), __fmt, __args);
|
|
}
|
|
|
|
template <output_iterator<const wchar_t&> _OutIt>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
|
|
vformat_to(_OutIt __out_it, wstring_view __fmt,
|
|
format_args_t<type_identity_t<_OutIt>, wchar_t> __args) {
|
|
return _VSTD::__vformat_to(_VSTD::move(__out_it), __fmt, __args);
|
|
}
|
|
|
|
template <output_iterator<const char&> _OutIt, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
|
|
format_to(_OutIt __out_it, string_view __fmt, const _Args&... __args) {
|
|
return _VSTD::vformat_to(
|
|
_VSTD::move(__out_it), __fmt,
|
|
_VSTD::make_format_args<basic_format_context<_OutIt, char>>(__args...));
|
|
}
|
|
|
|
template <output_iterator<const wchar_t&> _OutIt, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
|
|
format_to(_OutIt __out_it, wstring_view __fmt, const _Args&... __args) {
|
|
return _VSTD::vformat_to(
|
|
_VSTD::move(__out_it), __fmt,
|
|
_VSTD::make_format_args<basic_format_context<_OutIt, wchar_t>>(
|
|
__args...));
|
|
}
|
|
|
|
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string
|
|
vformat(string_view __fmt, format_args __args) {
|
|
string __res;
|
|
_VSTD::vformat_to(_VSTD::back_inserter(__res), __fmt, __args);
|
|
return __res;
|
|
}
|
|
|
|
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring
|
|
vformat(wstring_view __fmt, wformat_args __args) {
|
|
wstring __res;
|
|
_VSTD::vformat_to(_VSTD::back_inserter(__res), __fmt, __args);
|
|
return __res;
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string
|
|
format(string_view __fmt, const _Args&... __args) {
|
|
return _VSTD::vformat(__fmt, _VSTD::make_format_args(__args...));
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring
|
|
format(wstring_view __fmt, const _Args&... __args) {
|
|
return _VSTD::vformat(__fmt, _VSTD::make_wformat_args(__args...));
|
|
}
|
|
|
|
template <class _OutIt>
|
|
struct _LIBCPP_TEMPLATE_VIS format_to_n_result {
|
|
_OutIt out;
|
|
iter_difference_t<_OutIt> size;
|
|
};
|
|
|
|
template <output_iterator<const char&> _OutIt, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt>
|
|
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, string_view __fmt,
|
|
const _Args&... __args) {
|
|
// TODO FMT Improve PoC: using std::string is inefficient.
|
|
string __str = _VSTD::vformat(__fmt, _VSTD::make_format_args(__args...));
|
|
iter_difference_t<_OutIt> __s = __str.size();
|
|
iter_difference_t<_OutIt> __m =
|
|
_VSTD::clamp(__n, iter_difference_t<_OutIt>(0), __s);
|
|
__out_it = _VSTD::copy_n(__str.begin(), __m, _VSTD::move(__out_it));
|
|
return {_VSTD::move(__out_it), __s};
|
|
}
|
|
|
|
template <output_iterator<const wchar_t&> _OutIt, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt>
|
|
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, wstring_view __fmt,
|
|
const _Args&... __args) {
|
|
// TODO FMT Improve PoC: using std::string is inefficient.
|
|
wstring __str = _VSTD::vformat(__fmt, _VSTD::make_wformat_args(__args...));
|
|
iter_difference_t<_OutIt> __s = __str.size();
|
|
iter_difference_t<_OutIt> __m =
|
|
_VSTD::clamp(__n, iter_difference_t<_OutIt>(0), __s);
|
|
__out_it = _VSTD::copy_n(__str.begin(), __m, _VSTD::move(__out_it));
|
|
return {_VSTD::move(__out_it), __s};
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
|
|
formatted_size(string_view __fmt, const _Args&... __args) {
|
|
// TODO FMT Improve PoC: using std::string is inefficient.
|
|
return _VSTD::vformat(__fmt, _VSTD::make_format_args(__args...)).size();
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
|
|
formatted_size(wstring_view __fmt, const _Args&... __args) {
|
|
// TODO FMT Improve PoC: using std::string is inefficient.
|
|
return _VSTD::vformat(__fmt, _VSTD::make_wformat_args(__args...)).size();
|
|
}
|
|
|
|
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
|
|
|
|
template <class _OutIt, class _CharT>
|
|
requires(output_iterator<_OutIt, const _CharT&>) _LIBCPP_HIDE_FROM_ABI _OutIt
|
|
__vformat_to(_OutIt __out_it, locale __loc, basic_string_view<_CharT> __fmt,
|
|
format_args_t<type_identity_t<_OutIt>, _CharT> __args) {
|
|
return __format::__vformat_to(
|
|
basic_format_parse_context{__fmt, __args.__size()},
|
|
_VSTD::__format_context_create(_VSTD::move(__out_it), __args,
|
|
_VSTD::move(__loc)));
|
|
}
|
|
|
|
template <output_iterator<const char&> _OutIt>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
|
|
vformat_to(_OutIt __out_it, locale __loc, string_view __fmt,
|
|
format_args_t<type_identity_t<_OutIt>, char> __args) {
|
|
return _VSTD::__vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt,
|
|
__args);
|
|
}
|
|
|
|
template <output_iterator<const wchar_t&> _OutIt>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt
|
|
vformat_to(_OutIt __out_it, locale __loc, wstring_view __fmt,
|
|
format_args_t<type_identity_t<_OutIt>, wchar_t> __args) {
|
|
return _VSTD::__vformat_to(_VSTD::move(__out_it), _VSTD::move(__loc), __fmt,
|
|
__args);
|
|
}
|
|
|
|
template <output_iterator<const char&> _OutIt, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt format_to(
|
|
_OutIt __out_it, locale __loc, string_view __fmt, const _Args&... __args) {
|
|
return _VSTD::vformat_to(
|
|
_VSTD::move(__out_it), _VSTD::move(__loc), __fmt,
|
|
_VSTD::make_format_args<basic_format_context<_OutIt, char>>(__args...));
|
|
}
|
|
|
|
template <output_iterator<const wchar_t&> _OutIt, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT _OutIt format_to(
|
|
_OutIt __out_it, locale __loc, wstring_view __fmt, const _Args&... __args) {
|
|
return _VSTD::vformat_to(
|
|
_VSTD::move(__out_it), _VSTD::move(__loc), __fmt,
|
|
_VSTD::make_format_args<basic_format_context<_OutIt, wchar_t>>(
|
|
__args...));
|
|
}
|
|
|
|
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string
|
|
vformat(locale __loc, string_view __fmt, format_args __args) {
|
|
string __res;
|
|
_VSTD::vformat_to(_VSTD::back_inserter(__res), _VSTD::move(__loc), __fmt,
|
|
__args);
|
|
return __res;
|
|
}
|
|
|
|
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring
|
|
vformat(locale __loc, wstring_view __fmt, wformat_args __args) {
|
|
wstring __res;
|
|
_VSTD::vformat_to(_VSTD::back_inserter(__res), _VSTD::move(__loc), __fmt,
|
|
__args);
|
|
return __res;
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT string
|
|
format(locale __loc, string_view __fmt, const _Args&... __args) {
|
|
return _VSTD::vformat(_VSTD::move(__loc), __fmt,
|
|
_VSTD::make_format_args(__args...));
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT wstring
|
|
format(locale __loc, wstring_view __fmt, const _Args&... __args) {
|
|
return _VSTD::vformat(_VSTD::move(__loc), __fmt,
|
|
_VSTD::make_wformat_args(__args...));
|
|
}
|
|
|
|
template <output_iterator<const char&> _OutIt, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt>
|
|
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc,
|
|
string_view __fmt, const _Args&... __args) {
|
|
// TODO FMT Improve PoC: using std::string is inefficient.
|
|
string __str = _VSTD::vformat(_VSTD::move(__loc), __fmt,
|
|
_VSTD::make_format_args(__args...));
|
|
iter_difference_t<_OutIt> __s = __str.size();
|
|
iter_difference_t<_OutIt> __m =
|
|
_VSTD::clamp(__n, iter_difference_t<_OutIt>(0), __s);
|
|
__out_it = _VSTD::copy_n(__str.begin(), __m, _VSTD::move(__out_it));
|
|
return {_VSTD::move(__out_it), __s};
|
|
}
|
|
|
|
template <output_iterator<const wchar_t&> _OutIt, class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT format_to_n_result<_OutIt>
|
|
format_to_n(_OutIt __out_it, iter_difference_t<_OutIt> __n, locale __loc,
|
|
wstring_view __fmt, const _Args&... __args) {
|
|
// TODO FMT Improve PoC: using std::string is inefficient.
|
|
wstring __str = _VSTD::vformat(_VSTD::move(__loc), __fmt,
|
|
_VSTD::make_wformat_args(__args...));
|
|
iter_difference_t<_OutIt> __s = __str.size();
|
|
iter_difference_t<_OutIt> __m =
|
|
_VSTD::clamp(__n, iter_difference_t<_OutIt>(0), __s);
|
|
__out_it = _VSTD::copy_n(__str.begin(), __m, _VSTD::move(__out_it));
|
|
return {_VSTD::move(__out_it), __s};
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
|
|
formatted_size(locale __loc, string_view __fmt, const _Args&... __args) {
|
|
// TODO FMT Improve PoC: using std::string is inefficient.
|
|
return _VSTD::vformat(_VSTD::move(__loc), __fmt,
|
|
_VSTD::make_format_args(__args...))
|
|
.size();
|
|
}
|
|
|
|
template <class... _Args>
|
|
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT size_t
|
|
formatted_size(locale __loc, wstring_view __fmt, const _Args&... __args) {
|
|
// TODO FMT Improve PoC: using std::string is inefficient.
|
|
return _VSTD::vformat(_VSTD::move(__loc), __fmt,
|
|
_VSTD::make_wformat_args(__args...))
|
|
.size();
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif // !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
|
#endif //_LIBCPP_STD_VER > 17
|
|
|
|
_LIBCPP_END_NAMESPACE_STD
|
|
|
|
_LIBCPP_POP_MACROS
|
|
|
|
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
|
|
|
|
#endif // _LIBCPP_FORMAT
|