Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
fmt
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
fmt
Commits
ded0a3bb
Commit
ded0a3bb
authored
Jul 03, 2019
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Internalize undocumented basic_writer
parent
83174f2a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
1187 additions
and
1193 deletions
+1187
-1193
include/fmt/chrono.h
include/fmt/chrono.h
+1
-1
include/fmt/format-inl.h
include/fmt/format-inl.h
+4
-5
include/fmt/format.h
include/fmt/format.h
+1177
-1178
test/format-test.cc
test/format-test.cc
+5
-9
No files found.
include/fmt/chrono.h
View file @
ded0a3bb
...
...
@@ -803,7 +803,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
basic_memory_buffer
<
Char
>
buf
;
auto
out
=
std
::
back_inserter
(
buf
);
using
range
=
internal
::
output_range
<
decltype
(
ctx
.
out
()),
Char
>
;
basic_writer
<
range
>
w
(
range
(
ctx
.
out
()));
internal
::
basic_writer
<
range
>
w
(
range
(
ctx
.
out
()));
internal
::
handle_dynamic_spec
<
internal
::
width_checker
>
(
spec
.
width_
,
width_ref
,
ctx
,
format_str
.
begin
());
internal
::
handle_dynamic_spec
<
internal
::
precision_checker
>
(
...
...
include/fmt/format-inl.h
View file @
ded0a3bb
...
...
@@ -164,7 +164,7 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
++
error_code_size
;
}
error_code_size
+=
internal
::
to_unsigned
(
internal
::
count_digits
(
abs_value
));
writer
w
(
out
);
internal
::
writer
w
(
out
);
if
(
message
.
size
()
<=
inline_buffer_size
-
error_code_size
)
{
w
.
write
(
message
);
w
.
write
(
SEP
);
...
...
@@ -245,10 +245,9 @@ template <typename T>
int
format_float
(
char
*
buf
,
std
::
size_t
size
,
const
char
*
format
,
int
precision
,
T
value
)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if
(
precision
>
100000
)
{
if
(
precision
>
100000
)
throw
std
::
runtime_error
(
"fuzz mode - avoid large allocation inside snprintf"
);
}
#endif
// Suppress the warning about nonliteral format string.
auto
snprintf_ptr
=
FMT_SNPRINTF
;
...
...
@@ -898,7 +897,7 @@ FMT_FUNC void internal::format_windows_error(internal::buffer<char>& out,
if
(
result
!=
0
)
{
utf16_to_utf8
utf8_message
;
if
(
utf8_message
.
convert
(
system_message
)
==
ERROR_SUCCESS
)
{
writer
w
(
out
);
internal
::
writer
w
(
out
);
w
.
write
(
message
);
w
.
write
(
": "
);
w
.
write
(
utf8_message
);
...
...
@@ -927,7 +926,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
int
result
=
internal
::
safe_strerror
(
error_code
,
system_message
,
buf
.
size
());
if
(
result
==
0
)
{
writer
w
(
out
);
internal
::
writer
w
(
out
);
w
.
write
(
message
);
w
.
write
(
": "
);
w
.
write
(
system_message
);
...
...
include/fmt/format.h
View file @
ded0a3bb
...
...
@@ -300,7 +300,7 @@ enum { inline_buffer_size = 500 };
A dynamically growing memory buffer for trivially copyable/constructible types
with the first ``SIZE`` elements stored in the object itself.
You can use one of the following type
def
s for common character types:
You can use one of the following type
aliase
s for common character types:
+----------------+------------------------------+
| Type | Definition |
...
...
@@ -340,8 +340,8 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
void
grow
(
std
::
size_t
size
)
FMT_OVERRIDE
;
public:
typedef
T
value_type
;
typedef
const
T
&
const_reference
;
using
value_type
=
T
;
using
const_reference
=
const
T
&
;
explicit
basic_memory_buffer
(
const
Allocator
&
alloc
=
Allocator
())
:
Allocator
(
alloc
)
{
...
...
@@ -397,9 +397,7 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
template
<
typename
T
,
std
::
size_t
SIZE
,
typename
Allocator
>
void
basic_memory_buffer
<
T
,
SIZE
,
Allocator
>::
grow
(
std
::
size_t
size
)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if
(
size
>
1000
)
{
throw
std
::
runtime_error
(
"fuzz mode - won't grow that much"
);
}
if
(
size
>
1000
)
throw
std
::
runtime_error
(
"fuzz mode - won't grow that much"
);
#endif
std
::
size_t
old_capacity
=
this
->
capacity
();
std
::
size_t
new_capacity
=
old_capacity
+
old_capacity
/
2
;
...
...
@@ -416,8 +414,8 @@ void basic_memory_buffer<T, SIZE, Allocator>::grow(std::size_t size) {
if
(
old_data
!=
store_
)
Allocator
::
deallocate
(
old_data
,
old_capacity
);
}
typedef
basic_memory_buffer
<
char
>
memory_buffer
;
typedef
basic_memory_buffer
<
wchar_t
>
wmemory_buffer
;
using
memory_buffer
=
basic_memory_buffer
<
char
>
;
using
wmemory_buffer
=
basic_memory_buffer
<
wchar_t
>
;
/** A formatting error such as invalid format string. */
class
FMT_API
format_error
:
public
std
::
runtime_error
{
...
...
@@ -428,10 +426,6 @@ class FMT_API format_error : public std::runtime_error {
~
format_error
()
FMT_NOEXCEPT
;
};
template
<
typename
Range
>
class
basic_writer
;
using
writer
=
basic_writer
<
internal
::
buffer_range
<
char
>>
;
using
wwriter
=
basic_writer
<
internal
::
buffer_range
<
wchar_t
>>
;
namespace
internal
{
// A workaround for std::string not having mutable data() until C++17.
...
...
@@ -1069,9 +1063,8 @@ It grisu_prettify(const char* digits, int size, int exp, It it,
if
(
params
.
trailing_zeros
)
{
*
it
++
=
static_cast
<
Char
>
(
'.'
);
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if
(
num_zeros
>
1000
)
{
if
(
num_zeros
>
1000
)
throw
std
::
runtime_error
(
"fuzz mode - avoiding excessive cpu use"
);
}
#endif
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
}
...
...
@@ -1271,836 +1264,914 @@ void arg_map<Context>::init(const basic_format_args<Context>& args) {
}
}
template
<
typename
Range
,
typename
ErrorHandler
=
internal
::
error_handler
>
class
arg_formatter_base
{
/**
This template provides operations for formatting and writing data into a
character range.
*/
template
<
typename
Range
>
class
basic_writer
{
public:
typedef
typename
Range
::
value_type
char_type
;
typedef
decltype
(
std
::
declval
<
Range
>
().
begin
())
iterator
;
typedef
basic_format_specs
<
char_type
>
format_specs
;
private:
typedef
basic_writer
<
Range
>
writer_type
;
writer_type
writer_
;
format_specs
*
specs_
;
iterator
out_
;
// Output iterator.
internal
::
locale_ref
locale_
;
struct
char_writer
{
char_type
value
;
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
auto
reserve
(
std
::
size_t
n
)
->
decltype
(
internal
::
reserve
(
out_
,
n
))
{
return
internal
::
reserve
(
out_
,
n
);
}
size_t
size
()
const
{
return
1
;
}
size_t
width
()
const
{
return
1
;
}
template
<
typename
F
>
struct
padded_int_writer
{
size_t
size_
;
string_view
prefix
;
char_type
fill
;
std
::
size_t
padding
;
F
f
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
*
it
++
=
value
;
}
size_t
size
()
const
{
return
size_
;
}
size_t
width
()
const
{
return
size_
;
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
if
(
prefix
.
size
()
!=
0
)
it
=
internal
::
copy_str
<
char_type
>
(
prefix
.
begin
(),
prefix
.
end
(),
it
);
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
f
(
it
);
}
};
void
write_char
(
char_type
value
)
{
if
(
specs_
)
writer_
.
write_padded
(
*
specs_
,
char_writer
{
value
});
else
writer_
.
write
(
value
);
// Writes an integer in the format
// <left-padding><prefix><numeric-padding><digits><right-padding>
// where <digits> are written by f(it).
template
<
typename
Spec
,
typename
F
>
void
write_int
(
int
num_digits
,
string_view
prefix
,
const
Spec
&
spec
,
F
f
)
{
std
::
size_t
size
=
prefix
.
size
()
+
internal
::
to_unsigned
(
num_digits
);
char_type
fill
=
static_cast
<
char_type
>
(
spec
.
fill
());
std
::
size_t
padding
=
0
;
if
(
spec
.
align
()
==
ALIGN_NUMERIC
)
{
if
(
spec
.
width
()
>
size
)
{
padding
=
spec
.
width
()
-
size
;
size
=
spec
.
width
();
}
}
else
if
(
spec
.
precision
>
num_digits
)
{
size
=
prefix
.
size
()
+
internal
::
to_unsigned
(
spec
.
precision
);
padding
=
internal
::
to_unsigned
(
spec
.
precision
-
num_digits
);
fill
=
static_cast
<
char_type
>
(
'0'
);
}
align_spec
as
=
spec
;
if
(
spec
.
align
()
==
ALIGN_DEFAULT
)
as
.
align_
=
ALIGN_RIGHT
;
write_padded
(
as
,
padded_int_writer
<
F
>
{
size
,
prefix
,
fill
,
padding
,
f
});
}
void
write_pointer
(
const
void
*
p
)
{
writer_
.
write_pointer
(
internal
::
bit_cast
<
internal
::
uintptr_t
>
(
p
),
specs_
);
// Writes a decimal integer.
template
<
typename
Int
>
void
write_decimal
(
Int
value
)
{
typedef
typename
internal
::
int_traits
<
Int
>::
main_type
main_type
;
main_type
abs_value
=
static_cast
<
main_type
>
(
value
);
bool
is_negative
=
internal
::
is_negative
(
value
);
if
(
is_negative
)
abs_value
=
0
-
abs_value
;
int
num_digits
=
internal
::
count_digits
(
abs_value
);
auto
&&
it
=
reserve
((
is_negative
?
1
:
0
)
+
static_cast
<
size_t
>
(
num_digits
));
if
(
is_negative
)
*
it
++
=
static_cast
<
char_type
>
(
'-'
);
it
=
internal
::
format_decimal
<
char_type
>
(
it
,
abs_value
,
num_digits
);
}
protected:
writer_type
&
writer
()
{
return
writer_
;
}
format_specs
*
spec
()
{
return
specs_
;
}
iterator
out
()
{
return
writer_
.
out
();
}
// The handle_int_type_spec handler that writes an integer.
template
<
typename
Int
,
typename
Spec
>
struct
int_writer
{
typedef
typename
internal
::
int_traits
<
Int
>::
main_type
unsigned_type
;
void
write
(
bool
value
)
{
string_view
sv
(
value
?
"true"
:
"false"
);
specs_
?
writer_
.
write
(
sv
,
*
specs_
)
:
writer_
.
write
(
sv
);
}
basic_writer
<
Range
>&
writer
;
const
Spec
&
spec
;
unsigned_type
abs_value
;
char
prefix
[
4
];
unsigned
prefix_size
;
void
write
(
const
char_type
*
value
)
{
if
(
!
value
)
FMT_THROW
(
format_error
(
"string pointer is null"
));
auto
length
=
std
::
char_traits
<
char_type
>::
length
(
value
);
basic_string_view
<
char_type
>
sv
(
value
,
length
);
specs_
?
writer_
.
write
(
sv
,
*
specs_
)
:
writer_
.
write
(
sv
);
}
string_view
get_prefix
()
const
{
return
string_view
(
prefix
,
prefix_size
);
}
public:
arg_formatter_base
(
Range
r
,
format_specs
*
s
,
locale_ref
loc
)
:
writer_
(
r
,
loc
),
specs_
(
s
)
{}
int_writer
(
basic_writer
<
Range
>&
w
,
Int
value
,
const
Spec
&
s
)
:
writer
(
w
),
spec
(
s
),
abs_value
(
static_cast
<
unsigned_type
>
(
value
)),
prefix_size
(
0
)
{
if
(
internal
::
is_negative
(
value
))
{
prefix
[
0
]
=
'-'
;
++
prefix_size
;
abs_value
=
0
-
abs_value
;
}
else
if
(
spec
.
has
(
SIGN_FLAG
))
{
prefix
[
0
]
=
spec
.
has
(
PLUS_FLAG
)
?
'+'
:
' '
;
++
prefix_size
;
}
}
iterator
operator
()(
monostate
)
{
FMT_ASSERT
(
false
,
"invalid argument type"
);
return
out
();
}
struct
dec_writer
{
unsigned_type
abs_value
;
int
num_digits
;
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_integral
<
T
>
::
value
)
>
iterator
operator
()(
T
value
)
{
if
(
specs_
)
writer_
.
write_int
(
value
,
*
specs_
);
else
writer_
.
write
(
value
);
return
out
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
format_decimal
<
char_type
>
(
it
,
abs_value
,
num_digits
);
}
};
iterator
operator
()(
char_type
value
)
{
internal
::
handle_char_specs
(
specs_
,
char_spec_handler
(
*
this
,
static_cast
<
char_type
>
(
value
)));
return
out
(
);
}
void
on_dec
(
)
{
int
num_digits
=
internal
::
count_digits
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
spec
,
dec_writer
{
abs_value
,
num_digits
}
);
}
iterator
operator
()(
bool
value
)
{
if
(
specs_
&&
specs_
->
type
)
return
(
*
this
)(
value
?
1
:
0
);
write
(
value
!=
0
);
return
out
();
}
struct
hex_writer
{
int_writer
&
self
;
int
num_digits
;
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_floating_point
<
T
>
::
value
)
>
iterator
operator
()(
T
value
)
{
writer_
.
write_double
(
value
,
specs_
?
*
specs_
:
format_specs
()
);
return
out
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
format_uint
<
4
,
char_type
>
(
it
,
self
.
abs_value
,
num_digits
,
self
.
spec
.
type
!=
'x'
);
}
};
struct
char_spec_handler
:
ErrorHandler
{
arg_formatter_base
&
formatter
;
char_type
value
;
void
on_hex
()
{
if
(
spec
.
has
(
HASH_FLAG
))
{
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
static_cast
<
char
>
(
spec
.
type
);
}
int
num_digits
=
internal
::
count_digits
<
4
>
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
spec
,
hex_writer
{
*
this
,
num_digits
});
}
char_spec_handler
(
arg_formatter_base
&
f
,
char_type
val
)
:
formatter
(
f
),
value
(
val
)
{}
template
<
int
BITS
>
struct
bin_writer
{
unsigned_type
abs_value
;
int
num_digits
;
void
on_int
()
{
if
(
formatter
.
specs_
)
formatter
.
writer_
.
write_int
(
value
,
*
formatter
.
specs_
);
else
formatter
.
writer_
.
write
(
value
);
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
format_uint
<
BITS
,
char_type
>
(
it
,
abs_value
,
num_digits
);
}
};
void
on_bin
()
{
if
(
spec
.
has
(
HASH_FLAG
))
{
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
static_cast
<
char
>
(
spec
.
type
);
}
int
num_digits
=
internal
::
count_digits
<
1
>
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
spec
,
bin_writer
<
1
>
{
abs_value
,
num_digits
});
}
void
on_char
()
{
formatter
.
write_char
(
value
);
}
};
struct
cstring_spec_handler
:
internal
::
error_handler
{
arg_formatter_base
&
formatter
;
const
char_type
*
value
;
void
on_oct
()
{
int
num_digits
=
internal
::
count_digits
<
3
>
(
abs_value
);
if
(
spec
.
has
(
HASH_FLAG
)
&&
spec
.
precision
<=
num_digits
)
{
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
prefix
[
prefix_size
++
]
=
'0'
;
}
writer
.
write_int
(
num_digits
,
get_prefix
(),
spec
,
bin_writer
<
3
>
{
abs_value
,
num_digits
});
}
cstring_spec_handler
(
arg_formatter_base
&
f
,
const
char_type
*
val
)
:
formatter
(
f
),
value
(
val
)
{}
enum
{
SEP_SIZE
=
1
};
void
on_string
()
{
formatter
.
write
(
value
);
}
void
on_pointer
()
{
formatter
.
write_pointer
(
value
);
}
};
struct
num_writer
{
unsigned_type
abs_value
;
int
size
;
char_type
sep
;
iterator
operator
()(
const
char_type
*
value
)
{
if
(
!
specs_
)
return
write
(
value
),
out
(
);
internal
::
handle_cstring_type_spec
(
specs_
->
type
,
cstring_spec_handler
(
*
this
,
value
));
return
out
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
basic_string_view
<
char_type
>
s
(
&
sep
,
SEP_SIZE
);
it
=
internal
::
format_decimal
<
char_type
>
(
it
,
abs_value
,
size
,
internal
::
add_thousands_sep
<
char_type
>
(
s
));
}
};
iterator
operator
()(
basic_string_view
<
char_type
>
value
)
{
if
(
specs_
)
{
internal
::
check_string_type_spec
(
specs_
->
type
,
internal
::
error_handler
()
);
writer_
.
write
(
value
,
*
specs_
);
}
else
{
writer_
.
write
(
value
);
void
on_num
(
)
{
int
num_digits
=
internal
::
count_digits
(
abs_value
);
char_type
sep
=
internal
::
thousands_sep
<
char_type
>
(
writer
.
locale_
);
int
size
=
num_digits
+
SEP_SIZE
*
((
num_digits
-
1
)
/
3
);
writer
.
write_int
(
size
,
get_prefix
(),
spec
,
num_writer
{
abs_value
,
size
,
sep
}
);
}
return
out
();
}
iterator
operator
()(
const
void
*
value
)
{
if
(
specs_
)
check_pointer_type_spec
(
specs_
->
type
,
internal
::
error_handler
());
write_pointer
(
value
);
return
out
();
}
};
FMT_NORETURN
void
on_error
()
{
FMT_THROW
(
format_error
(
"invalid type specifier"
));
}
};
template
<
typename
Char
>
FMT_CONSTEXPR
bool
is_name_start
(
Char
c
)
{
return
(
'a'
<=
c
&&
c
<=
'z'
)
||
(
'A'
<=
c
&&
c
<=
'Z'
)
||
'_'
==
c
;
}
enum
{
INF_SIZE
=
3
};
// This is an enum to workaround a bug in MSVC.
// Parses the range [begin, end) as an unsigned integer. This function assumes
// that the range is non-empty and the first character is a digit.
template
<
typename
Char
,
typename
ErrorHandler
>
FMT_CONSTEXPR
unsigned
parse_nonnegative_int
(
const
Char
*&
begin
,
const
Char
*
end
,
ErrorHandler
&&
eh
)
{
assert
(
begin
!=
end
&&
'0'
<=
*
begin
&&
*
begin
<=
'9'
);
if
(
*
begin
==
'0'
)
{
++
begin
;
return
0
;
}
unsigned
value
=
0
;
// Convert to unsigned to prevent a warning.
unsigned
max_int
=
(
std
::
numeric_limits
<
int
>::
max
)();
unsigned
big
=
max_int
/
10
;
do
{
// Check for overflow.
if
(
value
>
big
)
{
value
=
max_int
+
1
;
break
;
struct
inf_or_nan_writer
{
char
sign
;
bool
as_percentage
;
const
char
*
str
;
size_t
size
()
const
{
return
static_cast
<
std
::
size_t
>
(
INF_SIZE
+
(
sign
?
1
:
0
)
+
(
as_percentage
?
1
:
0
));
}
value
=
value
*
10
+
unsigned
(
*
begin
-
'0'
);
++
begin
;
}
while
(
begin
!=
end
&&
'0'
<=
*
begin
&&
*
begin
<=
'9'
);
if
(
value
>
max_int
)
eh
.
on_error
(
"number is too big"
);
return
value
;
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
Context
>
class
custom_formatter
{
private:
typedef
typename
Context
::
char_type
char_type
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
if
(
sign
)
*
it
++
=
static_cast
<
char_type
>
(
sign
);
it
=
internal
::
copy_str
<
char_type
>
(
str
,
str
+
static_cast
<
std
::
size_t
>
(
INF_SIZE
),
it
);
if
(
as_percentage
)
*
it
++
=
static_cast
<
char_type
>
(
'%'
);
}
};
basic_parse_context
<
char_type
>&
parse_ctx_
;
Context
&
ctx_
;
struct
double_writer
{
char
sign
;
internal
::
buffer
<
char
>&
buffer
;
public:
explicit
custom_formatter
(
basic_parse_context
<
char_type
>&
parse_ctx
,
Context
&
ctx
)
:
parse_ctx_
(
parse_ctx
),
ctx_
(
ctx
)
{}
size_t
size
()
const
{
return
buffer
.
size
()
+
(
sign
?
1
:
0
);
}
size_t
width
()
const
{
return
size
();
}
bool
operator
()(
typename
basic_format_arg
<
Context
>::
handle
h
)
const
{
h
.
format
(
parse_ctx_
,
ctx_
);
return
true
;
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
sign
)
*
it
++
=
static_cast
<
char_type
>
(
sign
);
it
=
internal
::
copy_str
<
char_type
>
(
buffer
.
begin
(),
buffer
.
end
(),
it
);
}
};
template
<
typename
T
>
bool
operator
()(
T
)
const
{
return
false
;
}
};
class
grisu_writer
{
private:
internal
::
buffer
<
char
>&
digits_
;
size_t
size_
;
char
sign_
;
int
exp_
;
internal
::
gen_digits_params
params_
;
template
<
typename
T
>
struct
is_integer
{
enum
{
value
=
std
::
is_integral
<
T
>::
value
&&
!
std
::
is_same
<
T
,
bool
>::
value
&&
!
std
::
is_same
<
T
,
char
>::
value
&&
!
std
::
is_same
<
T
,
wchar_t
>::
value
public:
grisu_writer
(
char
sign
,
internal
::
buffer
<
char
>&
digits
,
int
exp
,
const
internal
::
gen_digits_params
&
params
)
:
digits_
(
digits
),
sign_
(
sign
),
exp_
(
exp
),
params_
(
params
)
{
int
num_digits
=
static_cast
<
int
>
(
digits
.
size
());
int
full_exp
=
num_digits
+
exp
-
1
;
int
precision
=
params
.
num_digits
>
0
?
params
.
num_digits
:
11
;
params_
.
fixed
|=
full_exp
>=
-
4
&&
full_exp
<
precision
;
auto
it
=
internal
::
grisu_prettify
<
char
>
(
digits
.
data
(),
num_digits
,
exp
,
internal
::
counting_iterator
<
char
>
(),
params_
);
size_
=
it
.
count
();
}
size_t
size
()
const
{
return
size_
+
(
sign_
?
1
:
0
);
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
sign_
)
*
it
++
=
static_cast
<
char_type
>
(
sign_
);
int
num_digits
=
static_cast
<
int
>
(
digits_
.
size
());
it
=
internal
::
grisu_prettify
<
char_type
>
(
digits_
.
data
(),
num_digits
,
exp_
,
it
,
params_
);
}
};
};
template
<
typename
ErrorHandler
>
class
width_check
er
{
public:
explicit
FMT_CONSTEXPR
width_checker
(
ErrorHandler
&
eh
)
:
handler_
(
eh
)
{}
template
<
typename
Char
>
struct
str_writ
er
{
const
Char
*
s
;
size_t
size_
;
template
<
typename
T
,
FMT_ENABLE_IF
(
is_integer
<
T
>
::
value
)
>
FMT_CONSTEXPR
unsigned
long
long
operator
()(
T
value
)
{
if
(
is_negative
(
value
))
handler_
.
on_error
(
"negative width"
);
return
static_cast
<
unsigned
long
long
>
(
value
);
}
size_t
size
()
const
{
return
size_
;
}
size_t
width
()
const
{
return
internal
::
count_code_points
(
basic_string_view
<
Char
>
(
s
,
size_
));
}
template
<
typename
T
,
FMT_ENABLE_IF
(
!
is_integer
<
T
>
::
value
)
>
FMT_CONSTEXPR
unsigned
long
long
operator
()(
T
)
{
handler_
.
on_error
(
"width is not integer"
);
return
0
;
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
copy_str
<
char_type
>
(
s
,
s
+
size_
,
it
);
}
};
private:
ErrorHandler
&
handler_
;
};
template
<
typename
UIntPtr
>
struct
pointer_writer
{
UIntPtr
value
;
int
num_digits
;
size_t
size
()
const
{
return
num_digits
+
2
;
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
*
it
++
=
static_cast
<
char_type
>
(
'0'
);
*
it
++
=
static_cast
<
char_type
>
(
'x'
);
it
=
internal
::
format_uint
<
4
,
char_type
>
(
it
,
value
,
num_digits
);
}
};
template
<
typename
ErrorHandler
>
class
precision_checker
{
public:
explicit
FMT_CONSTEXPR
precision_checker
(
ErrorHandler
&
eh
)
:
handler_
(
eh
)
{}
/** Constructs a ``basic_writer`` object. */
explicit
basic_writer
(
Range
out
,
internal
::
locale_ref
loc
=
internal
::
locale_ref
())
:
out_
(
out
.
begin
()),
locale_
(
loc
)
{}
template
<
typename
T
,
FMT_ENABLE_IF
(
is_integer
<
T
>
::
value
)
>
FMT_CONSTEXPR
unsigned
long
long
operator
()(
T
value
)
{
if
(
is_negative
(
value
))
handler_
.
on_error
(
"negative precision"
);
return
static_cast
<
unsigned
long
long
>
(
value
);
}
iterator
out
()
const
{
return
out_
;
}
template
<
typename
T
,
FMT_ENABLE_IF
(
!
is_integer
<
T
>
::
value
)
>
FMT_CONSTEXPR
unsigned
long
long
operator
()(
T
)
{
handler_
.
on_error
(
"precision is not integer"
);
return
0
;
// Writes a value in the format
// <left-padding><value><right-padding>
// where <value> is written by f(it).
template
<
typename
F
>
void
write_padded
(
const
align_spec
&
spec
,
F
&&
f
)
{
unsigned
width
=
spec
.
width
();
// User-perceived width (in code points).
size_t
size
=
f
.
size
();
// The number of code units.
size_t
num_code_points
=
width
!=
0
?
f
.
width
()
:
size
;
if
(
width
<=
num_code_points
)
return
f
(
reserve
(
size
));
auto
&&
it
=
reserve
(
width
+
(
size
-
num_code_points
));
char_type
fill
=
static_cast
<
char_type
>
(
spec
.
fill
());
std
::
size_t
padding
=
width
-
num_code_points
;
if
(
spec
.
align
()
==
ALIGN_RIGHT
)
{
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
f
(
it
);
}
else
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
std
::
size_t
left_padding
=
padding
/
2
;
it
=
std
::
fill_n
(
it
,
left_padding
,
fill
);
f
(
it
);
it
=
std
::
fill_n
(
it
,
padding
-
left_padding
,
fill
);
}
else
{
f
(
it
);
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
}
}
private:
ErrorHandler
&
handler_
;
};
// A format specifier handler that sets fields in basic_format_specs.
template
<
typename
Char
>
class
specs_setter
{
public:
explicit
FMT_CONSTEXPR
specs_setter
(
basic_format_specs
<
Char
>&
specs
)
:
specs_
(
specs
)
{}
void
write
(
int
value
)
{
write_decimal
(
value
);
}
void
write
(
long
value
)
{
write_decimal
(
value
);
}
void
write
(
long
long
value
)
{
write_decimal
(
value
);
}
FMT_CONSTEXPR
specs_setter
(
const
specs_setter
&
other
)
:
specs_
(
other
.
specs_
)
{}
void
write
(
unsigned
value
)
{
write_decimal
(
value
);
}
void
write
(
unsigned
long
value
)
{
write_decimal
(
value
);
}
void
write
(
unsigned
long
long
value
)
{
write_decimal
(
value
);
}
FMT_CONSTEXPR
void
on_align
(
alignment
align
)
{
specs_
.
align_
=
align
;
}
FMT_CONSTEXPR
void
on_fill
(
Char
fill
)
{
specs_
.
fill_
=
fill
;
}
FMT_CONSTEXPR
void
on_plus
()
{
specs_
.
flags
|=
SIGN_FLAG
|
PLUS_FLAG
;
}
FMT_CONSTEXPR
void
on_minus
()
{
specs_
.
flags
|=
MINUS_FLAG
;
}
FMT_CONSTEXPR
void
on_space
()
{
specs_
.
flags
|=
SIGN_FLAG
;
}
FMT_CONSTEXPR
void
on_hash
()
{
specs_
.
flags
|=
HASH_FLAG
;
}
// Writes a formatted integer.
template
<
typename
T
,
typename
Spec
>
void
write_int
(
T
value
,
const
Spec
&
spec
)
{
internal
::
handle_int_type_spec
(
spec
.
type
,
int_writer
<
T
,
Spec
>
(
*
this
,
value
,
spec
));
}
FMT_CONSTEXPR
void
on_zero
()
{
specs_
.
align_
=
ALIGN_NUMERIC
;
specs_
.
fill_
=
'0'
;
/**
\rst
Formats *value* and writes it to the buffer.
\endrst
*/
template
<
typename
T
,
typename
FormatSpec
,
typename
...
FormatSpecs
,
FMT_ENABLE_IF
(
std
::
is_integral
<
T
>
::
value
)
>
void
write
(
T
value
,
FormatSpec
spec
,
FormatSpecs
...
specs
)
{
format_specs
s
(
spec
,
specs
...);
s
.
align_
=
ALIGN_RIGHT
;
write_int
(
value
,
s
);
}
FMT_CONSTEXPR
void
on_width
(
unsigned
width
)
{
specs_
.
width_
=
width
;
}
FMT_CONSTEXPR
void
on_precision
(
unsigned
precision
)
{
specs_
.
precision
=
static_cast
<
int
>
(
precision
);
void
write
(
double
value
,
const
format_specs
&
spec
=
format_specs
())
{
write_double
(
value
,
spec
);
}
FMT_CONSTEXPR
void
end_precision
()
{}
FMT_CONSTEXPR
void
on_type
(
Char
type
)
{
specs_
.
type
=
static_cast
<
char
>
(
type
);
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void
write
(
long
double
value
,
const
format_specs
&
spec
=
format_specs
())
{
write_double
(
value
,
spec
);
}
protected:
basic_format_specs
<
Char
>&
specs_
;
};
template
<
typename
ErrorHandler
>
class
numeric_specs_checker
{
public:
FMT_CONSTEXPR
numeric_specs_checker
(
ErrorHandler
&
eh
,
internal
::
type
arg_type
)
:
error_handler_
(
eh
),
arg_type_
(
arg_type
)
{}
FMT_CONSTEXPR
void
require_numeric_argument
()
{
if
(
!
is_arithmetic
(
arg_type_
))
error_handler_
.
on_error
(
"format specifier requires numeric argument"
);
}
// Formats a floating-point number (double or long double).
template
<
typename
T
>
void
write_double
(
T
value
,
const
format_specs
&
spec
);
FMT_CONSTEXPR
void
check_sign
()
{
require_numeric_argument
();
if
(
is_integral
(
arg_type_
)
&&
arg_type_
!=
int_type
&&
arg_type_
!=
long_long_type
&&
arg_type_
!=
internal
::
char_type
)
{
error_handler_
.
on_error
(
"format specifier requires signed argument"
);
}
/** Writes a character to the buffer. */
void
write
(
char
value
)
{
auto
&&
it
=
reserve
(
1
);
*
it
++
=
value
;
}
FMT_CONSTEXPR
void
check_precision
()
{
if
(
is_integral
(
arg_type_
)
||
arg_type_
==
internal
::
pointer_type
)
error_handler_
.
on_error
(
"precision not allowed for this argument type"
);
template
<
typename
Char
,
FMT_ENABLE_IF
(
std
::
is_same
<
Char
,
char_type
>
::
value
)
>
void
write
(
Char
value
)
{
auto
&&
it
=
reserve
(
1
);
*
it
++
=
value
;
}
private:
ErrorHandler
&
error_handler_
;
internal
::
type
arg_type_
;
};
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template
<
typename
Handler
>
class
specs_checker
:
public
Handler
{
public:
FMT_CONSTEXPR
specs_checker
(
const
Handler
&
handler
,
internal
::
type
arg_type
)
:
Handler
(
handler
),
checker_
(
*
this
,
arg_type
)
{}
FMT_CONSTEXPR
specs_checker
(
const
specs_checker
&
other
)
:
Handler
(
other
),
checker_
(
*
this
,
other
.
arg_type_
)
{}
FMT_CONSTEXPR
void
on_align
(
alignment
align
)
{
if
(
align
==
ALIGN_NUMERIC
)
checker_
.
require_numeric_argument
();
Handler
::
on_align
(
align
);
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void
write
(
string_view
value
)
{
auto
&&
it
=
reserve
(
value
.
size
());
it
=
internal
::
copy_str
<
char_type
>
(
value
.
begin
(),
value
.
end
(),
it
);
}
FMT_CONSTEXPR
void
on_plus
()
{
checker_
.
check_sign
(
);
Handler
::
on_plus
(
);
void
write
(
wstring_view
value
)
{
static_assert
(
std
::
is_same
<
char_type
,
wchar_t
>::
value
,
""
);
auto
&&
it
=
reserve
(
value
.
size
()
);
it
=
std
::
copy
(
value
.
begin
(),
value
.
end
(),
it
);
}
FMT_CONSTEXPR
void
on_minus
()
{
checker_
.
check_sign
();
Handler
::
on_minus
();
// Writes a formatted string.
template
<
typename
Char
>
void
write
(
const
Char
*
s
,
std
::
size_t
size
,
const
align_spec
&
spec
)
{
write_padded
(
spec
,
str_writer
<
Char
>
{
s
,
size
});
}
FMT_CONSTEXPR
void
on_space
()
{
checker_
.
check_sign
();
Handler
::
on_space
();
template
<
typename
Char
>
void
write
(
basic_string_view
<
Char
>
s
,
const
format_specs
&
spec
=
format_specs
())
{
const
Char
*
data
=
s
.
data
();
std
::
size_t
size
=
s
.
size
();
if
(
spec
.
precision
>=
0
&&
internal
::
to_unsigned
(
spec
.
precision
)
<
size
)
size
=
internal
::
to_unsigned
(
spec
.
precision
);
write
(
data
,
size
,
spec
);
}
FMT_CONSTEXPR
void
on_hash
()
{
checker_
.
require_numeric_argument
();
Handler
::
on_hash
();
template
<
typename
UIntPtr
>
void
write_pointer
(
UIntPtr
value
,
const
align_spec
*
spec
)
{
int
num_digits
=
internal
::
count_digits
<
4
>
(
value
);
auto
pw
=
pointer_writer
<
UIntPtr
>
{
value
,
num_digits
};
if
(
!
spec
)
return
pw
(
reserve
(
num_digits
+
2
));
align_spec
as
=
*
spec
;
if
(
as
.
align
()
==
ALIGN_DEFAULT
)
as
.
align_
=
ALIGN_RIGHT
;
write_padded
(
as
,
pw
);
}
};
FMT_CONSTEXPR
void
on_zero
()
{
checker_
.
require_numeric_argument
();
Handler
::
on_zero
();
}
using
writer
=
basic_writer
<
buffer_range
<
char
>>
;
FMT_CONSTEXPR
void
end_precision
()
{
checker_
.
check_precision
();
}
template
<
typename
Range
,
typename
ErrorHandler
=
internal
::
error_handler
>
class
arg_formatter_base
{
public:
typedef
typename
Range
::
value_type
char_type
;
typedef
decltype
(
std
::
declval
<
Range
>
().
begin
())
iterator
;
typedef
basic_format_specs
<
char_type
>
format_specs
;
private:
numeric_specs_checker
<
Handler
>
checker_
;
};
template
<
template
<
typename
>
class
Handler
,
typename
T
,
typename
FormatArg
,
typename
ErrorHandler
>
FMT_CONSTEXPR
void
set_dynamic_spec
(
T
&
value
,
FormatArg
arg
,
ErrorHandler
eh
)
{
unsigned
long
long
big_value
=
visit_format_arg
(
Handler
<
ErrorHandler
>
(
eh
),
arg
);
if
(
big_value
>
to_unsigned
((
std
::
numeric_limits
<
int
>::
max
)()))
eh
.
on_error
(
"number is too big"
);
value
=
static_cast
<
T
>
(
big_value
);
}
typedef
basic_writer
<
Range
>
writer_type
;
writer_type
writer_
;
format_specs
*
specs_
;
struct
auto_id
{};
struct
char_writer
{
char_type
value
;
template
<
typename
Context
>
FMT_CONSTEXPR
typename
Context
::
format_arg
get_arg
(
Context
&
ctx
,
unsigned
id
)
{
auto
arg
=
ctx
.
arg
(
id
);
if
(
!
arg
)
ctx
.
on_error
(
"argument index out of range"
);
return
arg
;
}
size_t
size
()
const
{
return
1
;
}
size_t
width
()
const
{
return
1
;
}
// The standard format specifier handler with checking.
template
<
typename
ParseContext
,
typename
Context
>
class
specs_handler
:
public
specs_setter
<
typename
Context
::
char_type
>
{
public:
typedef
typename
Context
::
char_type
char_type
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
*
it
++
=
value
;
}
};
FMT_CONSTEXPR
specs_handler
(
basic_format_specs
<
char_type
>&
specs
,
ParseContext
&
parse_ctx
,
Context
&
ctx
)
:
specs_setter
<
char_type
>
(
specs
),
parse_context_
(
parse_ctx
),
context_
(
ctx
)
{}
void
write_char
(
char_type
value
)
{
if
(
specs_
)
writer_
.
write_padded
(
*
specs_
,
char_writer
{
value
});
else
writer_
.
write
(
value
);
}
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_width
(
Id
arg_id
)
{
set_dynamic_spec
<
width_checker
>
(
this
->
specs_
.
width_
,
get_arg
(
arg_id
),
context_
.
error_handler
());
void
write_pointer
(
const
void
*
p
)
{
writer_
.
write_pointer
(
internal
::
bit_cast
<
internal
::
uintptr_t
>
(
p
),
specs_
);
}
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_precision
(
Id
arg_id
)
{
set_dynamic_spec
<
precision_checker
>
(
this
->
specs_
.
precision
,
get_arg
(
arg_id
),
context_
.
error_handler
());
protected:
writer_type
&
writer
()
{
return
writer_
;
}
format_specs
*
spec
()
{
return
specs_
;
}
iterator
out
()
{
return
writer_
.
out
();
}
void
write
(
bool
value
)
{
string_view
sv
(
value
?
"true"
:
"false"
);
specs_
?
writer_
.
write
(
sv
,
*
specs_
)
:
writer_
.
write
(
sv
);
}
void
on_error
(
const
char
*
message
)
{
context_
.
on_error
(
message
);
}
void
write
(
const
char_type
*
value
)
{
if
(
!
value
)
FMT_THROW
(
format_error
(
"string pointer is null"
));
auto
length
=
std
::
char_traits
<
char_type
>::
length
(
value
);
basic_string_view
<
char_type
>
sv
(
value
,
length
);
specs_
?
writer_
.
write
(
sv
,
*
specs_
)
:
writer_
.
write
(
sv
);
}
p
rivate
:
// This is only needed for compatibility with gcc 4.4.
typedef
typename
Context
::
format_arg
format_arg
;
p
ublic
:
arg_formatter_base
(
Range
r
,
format_specs
*
s
,
locale_ref
loc
)
:
writer_
(
r
,
loc
),
specs_
(
s
)
{}
FMT_CONSTEXPR
format_arg
get_arg
(
auto_id
)
{
return
internal
::
get_arg
(
context_
,
parse_context_
.
next_arg_id
());
iterator
operator
()(
monostate
)
{
FMT_ASSERT
(
false
,
"invalid argument type"
);
return
out
();
}
FMT_CONSTEXPR
format_arg
get_arg
(
unsigned
arg_id
)
{
parse_context_
.
check_arg_id
(
arg_id
);
return
internal
::
get_arg
(
context_
,
arg_id
);
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_integral
<
T
>
::
value
)
>
iterator
operator
()(
T
value
)
{
if
(
specs_
)
writer_
.
write_int
(
value
,
*
specs_
);
else
writer_
.
write
(
value
);
return
out
();
}
FMT_CONSTEXPR
format_arg
get_arg
(
basic_string_view
<
char_type
>
arg_id
)
{
parse_context_
.
check_arg_id
(
arg_id
);
return
context_
.
arg
(
arg_id
);
iterator
operator
()(
char_type
value
)
{
internal
::
handle_char_specs
(
specs_
,
char_spec_handler
(
*
this
,
static_cast
<
char_type
>
(
value
)));
return
out
();
}
ParseContext
&
parse_context_
;
Context
&
context_
;
};
iterator
operator
()(
bool
value
)
{
if
(
specs_
&&
specs_
->
type
)
return
(
*
this
)(
value
?
1
:
0
);
write
(
value
!=
0
);
return
out
();
}
struct
string_view_metadata
{
FMT_CONSTEXPR
string_view_metadata
()
:
offset_
(
0u
),
size_
(
0u
)
{}
template
<
typename
Char
>
FMT_CONSTEXPR
string_view_metadata
(
basic_string_view
<
Char
>
primary_string
,
basic_string_view
<
Char
>
view
)
:
offset_
(
view
.
data
()
-
primary_string
.
data
()),
size_
(
view
.
size
())
{}
FMT_CONSTEXPR
string_view_metadata
(
std
::
size_t
offset
,
std
::
size_t
size
)
:
offset_
(
offset
),
size_
(
size
)
{}
template
<
typename
Char
>
FMT_CONSTEXPR
basic_string_view
<
Char
>
to_view
(
const
Char
*
str
)
const
{
return
{
str
+
offset_
,
size_
};
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_floating_point
<
T
>
::
value
)
>
iterator
operator
()(
T
value
)
{
writer_
.
write_double
(
value
,
specs_
?
*
specs_
:
format_specs
());
return
out
();
}
st
d
::
size_t
offset_
;
std
::
size_t
size_
;
}
;
st
ruct
char_spec_handler
:
ErrorHandler
{
arg_formatter_base
&
formatter
;
char_type
value
;
// An argument reference.
template
<
typename
Char
>
struct
arg_ref
{
enum
Kind
{
NONE
,
INDEX
,
NAME
};
typedef
Char
char_type
;
char_spec_handler
(
arg_formatter_base
&
f
,
char_type
val
)
:
formatter
(
f
),
value
(
val
)
{}
FMT_CONSTEXPR
arg_ref
()
:
kind
(
NONE
),
val
()
{}
FMT_CONSTEXPR
explicit
arg_ref
(
unsigned
index
)
:
kind
(
INDEX
),
val
(
index
)
{}
FMT_CONSTEXPR
explicit
arg_ref
(
string_view_metadata
name
)
:
kind
(
NAME
),
val
(
name
)
{}
void
on_int
()
{
if
(
formatter
.
specs_
)
formatter
.
writer_
.
write_int
(
value
,
*
formatter
.
specs_
);
else
formatter
.
writer_
.
write
(
value
);
}
void
on_char
()
{
formatter
.
write_char
(
value
);
}
};
FMT_CONSTEXPR
arg_ref
&
operator
=
(
unsigned
idx
)
{
kind
=
INDEX
;
val
.
index
=
idx
;
return
*
this
;
}
struct
cstring_spec_handler
:
internal
::
error_handler
{
arg_formatter_base
&
formatter
;
const
char_type
*
value
;
Kind
kind
;
union
value
{
FMT_CONSTEXPR
value
()
:
index
(
0u
)
{}
FMT_CONSTEXPR
value
(
unsigned
id
)
:
index
(
id
)
{}
FMT_CONSTEXPR
value
(
string_view_metadata
n
)
:
name
(
n
)
{}
cstring_spec_handler
(
arg_formatter_base
&
f
,
const
char_type
*
val
)
:
formatter
(
f
),
value
(
val
)
{}
unsigned
index
;
string_view_metadata
name
;
}
val
;
};
void
on_string
()
{
formatter
.
write
(
value
);
}
void
on_pointer
()
{
formatter
.
write_pointer
(
value
);
}
};
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// different sets of arguments (precompilation of format strings).
template
<
typename
Char
>
struct
dynamic_format_specs
:
basic_format_specs
<
Char
>
{
arg_ref
<
Char
>
width_ref
;
arg_ref
<
Char
>
precision_ref
;
iterator
operator
()(
const
char_type
*
value
)
{
if
(
!
specs_
)
return
write
(
value
),
out
();
internal
::
handle_cstring_type_spec
(
specs_
->
type
,
cstring_spec_handler
(
*
this
,
value
));
return
out
();
}
iterator
operator
()(
basic_string_view
<
char_type
>
value
)
{
if
(
specs_
)
{
internal
::
check_string_type_spec
(
specs_
->
type
,
internal
::
error_handler
());
writer_
.
write
(
value
,
*
specs_
);
}
else
{
writer_
.
write
(
value
);
}
return
out
();
}
iterator
operator
()(
const
void
*
value
)
{
if
(
specs_
)
check_pointer_type_spec
(
specs_
->
type
,
internal
::
error_handler
());
write_pointer
(
value
);
return
out
();
}
};
// Format spec handler that saves references to arguments representing dynamic
// width and precision to be resolved at formatting time.
template
<
typename
ParseContext
>
class
dynamic_specs_handler
:
public
specs_setter
<
typename
ParseContext
::
char_type
>
{
public:
typedef
typename
ParseContext
::
char_type
char_type
;
template
<
typename
Char
>
FMT_CONSTEXPR
bool
is_name_start
(
Char
c
)
{
return
(
'a'
<=
c
&&
c
<=
'z'
)
||
(
'A'
<=
c
&&
c
<=
'Z'
)
||
'_'
==
c
;
}
FMT_CONSTEXPR
dynamic_specs_handler
(
dynamic_format_specs
<
char_type
>&
specs
,
ParseContext
&
ctx
)
:
specs_setter
<
char_type
>
(
specs
),
specs_
(
specs
),
context_
(
ctx
)
{}
// Parses the range [begin, end) as an unsigned integer. This function assumes
// that the range is non-empty and the first character is a digit.
template
<
typename
Char
,
typename
ErrorHandler
>
FMT_CONSTEXPR
unsigned
parse_nonnegative_int
(
const
Char
*&
begin
,
const
Char
*
end
,
ErrorHandler
&&
eh
)
{
assert
(
begin
!=
end
&&
'0'
<=
*
begin
&&
*
begin
<=
'9'
);
if
(
*
begin
==
'0'
)
{
++
begin
;
return
0
;
}
unsigned
value
=
0
;
// Convert to unsigned to prevent a warning.
unsigned
max_int
=
(
std
::
numeric_limits
<
int
>::
max
)();
unsigned
big
=
max_int
/
10
;
do
{
// Check for overflow.
if
(
value
>
big
)
{
value
=
max_int
+
1
;
break
;
}
value
=
value
*
10
+
unsigned
(
*
begin
-
'0'
);
++
begin
;
}
while
(
begin
!=
end
&&
'0'
<=
*
begin
&&
*
begin
<=
'9'
);
if
(
value
>
max_int
)
eh
.
on_error
(
"number is too big"
);
return
value
;
}
FMT_CONSTEXPR
dynamic_specs_handler
(
const
dynamic_specs_handler
&
other
)
:
specs_setter
<
char_type
>
(
other
),
specs_
(
other
.
specs_
),
context_
(
other
.
context_
)
{}
template
<
typename
Context
>
class
custom_formatter
{
private:
typedef
typename
Context
::
char_type
char_type
;
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_width
(
Id
arg_id
)
{
specs_
.
width_ref
=
make_arg_ref
(
arg_id
);
basic_parse_context
<
char_type
>&
parse_ctx_
;
Context
&
ctx_
;
public:
explicit
custom_formatter
(
basic_parse_context
<
char_type
>&
parse_ctx
,
Context
&
ctx
)
:
parse_ctx_
(
parse_ctx
),
ctx_
(
ctx
)
{}
bool
operator
()(
typename
basic_format_arg
<
Context
>::
handle
h
)
const
{
h
.
format
(
parse_ctx_
,
ctx_
);
return
true
;
}
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_precision
(
Id
arg_id
)
{
specs_
.
precision_ref
=
make_arg_ref
(
arg_id
);
template
<
typename
T
>
bool
operator
()(
T
)
const
{
return
false
;
}
};
template
<
typename
T
>
struct
is_integer
{
enum
{
value
=
std
::
is_integral
<
T
>::
value
&&
!
std
::
is_same
<
T
,
bool
>::
value
&&
!
std
::
is_same
<
T
,
char
>::
value
&&
!
std
::
is_same
<
T
,
wchar_t
>::
value
};
};
template
<
typename
ErrorHandler
>
class
width_checker
{
public:
explicit
FMT_CONSTEXPR
width_checker
(
ErrorHandler
&
eh
)
:
handler_
(
eh
)
{}
template
<
typename
T
,
FMT_ENABLE_IF
(
is_integer
<
T
>
::
value
)
>
FMT_CONSTEXPR
unsigned
long
long
operator
()(
T
value
)
{
if
(
is_negative
(
value
))
handler_
.
on_error
(
"negative width"
);
return
static_cast
<
unsigned
long
long
>
(
value
);
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
context_
.
on_error
(
message
);
template
<
typename
T
,
FMT_ENABLE_IF
(
!
is_integer
<
T
>
::
value
)
>
FMT_CONSTEXPR
unsigned
long
long
operator
()(
T
)
{
handler_
.
on_error
(
"width is not integer"
);
return
0
;
}
private:
typedef
arg_ref
<
char_type
>
arg_ref_type
;
ErrorHandler
&
handler_
;
};
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
unsigned
arg_id
)
{
context_
.
check_arg_id
(
arg_id
);
return
arg_ref_type
(
arg_id
);
}
template
<
typename
ErrorHandler
>
class
precision_checker
{
public:
explicit
FMT_CONSTEXPR
precision_checker
(
ErrorHandler
&
eh
)
:
handler_
(
eh
)
{}
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
auto_id
)
{
return
arg_ref_type
(
context_
.
next_arg_id
());
template
<
typename
T
,
FMT_ENABLE_IF
(
is_integer
<
T
>
::
value
)
>
FMT_CONSTEXPR
unsigned
long
long
operator
()(
T
value
)
{
if
(
is_negative
(
value
))
handler_
.
on_error
(
"negative precision"
);
return
static_cast
<
unsigned
long
long
>
(
value
);
}
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
basic_string_view
<
char_type
>
arg_id
)
{
context_
.
check_arg_id
(
arg_id
);
basic_string_view
<
char_type
>
format_str
(
context_
.
begin
(),
context_
.
end
()
-
context_
.
begin
());
const
auto
id_metadata
=
string_view_metadata
(
format_str
,
arg_id
);
return
arg_ref_type
(
id_metadata
);
template
<
typename
T
,
FMT_ENABLE_IF
(
!
is_integer
<
T
>
::
value
)
>
FMT_CONSTEXPR
unsigned
long
long
operator
()(
T
)
{
handler_
.
on_error
(
"precision is not integer"
);
return
0
;
}
dynamic_format_specs
<
char_type
>&
specs_
;
ParseContext
&
context
_
;
private:
ErrorHandler
&
handler
_
;
};
template
<
typename
Char
,
typename
IDHandler
>
FMT_CONSTEXPR
const
Char
*
parse_arg_id
(
const
Char
*
begin
,
const
Char
*
end
,
IDHandler
&&
handler
)
{
assert
(
begin
!=
end
);
Char
c
=
*
begin
;
if
(
c
==
'}'
||
c
==
':'
)
return
handler
(),
begin
;
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
unsigned
index
=
parse_nonnegative_int
(
begin
,
end
,
handler
);
if
(
begin
==
end
||
(
*
begin
!=
'}'
&&
*
begin
!=
':'
))
return
handler
.
on_error
(
"invalid format string"
),
begin
;
handler
(
index
);
return
begin
;
}
if
(
!
is_name_start
(
c
))
return
handler
.
on_error
(
"invalid format string"
),
begin
;
auto
it
=
begin
;
do
{
++
it
;
}
while
(
it
!=
end
&&
(
is_name_start
(
c
=
*
it
)
||
(
'0'
<=
c
&&
c
<=
'9'
)));
handler
(
basic_string_view
<
Char
>
(
begin
,
to_unsigned
(
it
-
begin
)));
return
it
;
}
// A format specifier handler that sets fields in basic_format_specs.
template
<
typename
Char
>
class
specs_setter
{
public:
explicit
FMT_CONSTEXPR
specs_setter
(
basic_format_specs
<
Char
>&
specs
)
:
specs_
(
specs
)
{}
// Adapts SpecHandler to IDHandler API for dynamic width.
template
<
typename
SpecHandler
,
typename
Char
>
struct
width_adapter
{
explicit
FMT_CONSTEXPR
width_adapter
(
SpecHandler
&
h
)
:
handler
(
h
)
{}
FMT_CONSTEXPR
specs_setter
(
const
specs_setter
&
other
)
:
specs_
(
other
.
specs_
)
{}
FMT_CONSTEXPR
void
operator
()()
{
handler
.
on_dynamic_width
(
auto_id
());
}
FMT_CONSTEXPR
void
operator
()(
unsigned
id
)
{
handler
.
on_dynamic_width
(
id
);
}
FMT_CONSTEXPR
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_dynamic_width
(
id
);
FMT_CONSTEXPR
void
on_align
(
alignment
align
)
{
specs_
.
align_
=
align
;
}
FMT_CONSTEXPR
void
on_fill
(
Char
fill
)
{
specs_
.
fill_
=
fill
;
}
FMT_CONSTEXPR
void
on_plus
()
{
specs_
.
flags
|=
SIGN_FLAG
|
PLUS_FLAG
;
}
FMT_CONSTEXPR
void
on_minus
()
{
specs_
.
flags
|=
MINUS_FLAG
;
}
FMT_CONSTEXPR
void
on_space
()
{
specs_
.
flags
|=
SIGN_FLAG
;
}
FMT_CONSTEXPR
void
on_hash
()
{
specs_
.
flags
|=
HASH_FLAG
;
}
FMT_CONSTEXPR
void
on_zero
()
{
specs_
.
align_
=
ALIGN_NUMERIC
;
specs_
.
fill_
=
'0'
;
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
FMT_CONSTEXPR
void
on_width
(
unsigned
width
)
{
specs_
.
width_
=
width
;
}
FMT_CONSTEXPR
void
on_precision
(
unsigned
precision
)
{
specs_
.
precision
=
static_cast
<
int
>
(
precision
);
}
FMT_CONSTEXPR
void
end_precision
()
{}
SpecHandler
&
handler
;
FMT_CONSTEXPR
void
on_type
(
Char
type
)
{
specs_
.
type
=
static_cast
<
char
>
(
type
);
}
protected:
basic_format_specs
<
Char
>&
specs_
;
};
// Adapts SpecHandler to IDHandler API for dynamic precision.
template
<
typename
SpecHandler
,
typename
Char
>
struct
precision_adapter
{
explicit
FMT_CONSTEXPR
precision_adapter
(
SpecHandler
&
h
)
:
handler
(
h
)
{}
template
<
typename
ErrorHandler
>
class
numeric_specs_checker
{
public:
FMT_CONSTEXPR
numeric_specs_checker
(
ErrorHandler
&
eh
,
internal
::
type
arg_type
)
:
error_handler_
(
eh
),
arg_type_
(
arg_type
)
{}
FMT_CONSTEXPR
void
operator
()()
{
handler
.
on_dynamic_precision
(
auto_id
());
}
FMT_CONSTEXPR
void
operator
()(
unsigned
id
)
{
handler
.
on_dynamic_precision
(
id
);
FMT_CONSTEXPR
void
require_numeric_argument
()
{
if
(
!
is_arithmetic
(
arg_type_
))
error_handler_
.
on_error
(
"format specifier requires numeric argument"
);
}
FMT_CONSTEXPR
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_dynamic_precision
(
id
);
FMT_CONSTEXPR
void
check_sign
()
{
require_numeric_argument
();
if
(
is_integral
(
arg_type_
)
&&
arg_type_
!=
int_type
&&
arg_type_
!=
long_long_type
&&
arg_type_
!=
internal
::
char_type
)
{
error_handler_
.
on_error
(
"format specifier requires signed argument"
);
}
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
FMT_CONSTEXPR
void
check_precision
()
{
if
(
is_integral
(
arg_type_
)
||
arg_type_
==
internal
::
pointer_type
)
error_handler_
.
on_error
(
"precision not allowed for this argument type"
);
}
SpecHandler
&
handler
;
private:
ErrorHandler
&
error_handler_
;
internal
::
type
arg_type_
;
};
// Parses fill and alignment.
template
<
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
const
Char
*
parse_align
(
const
Char
*
begin
,
const
Char
*
end
,
Handler
&&
handler
)
{
FMT_ASSERT
(
begin
!=
end
,
""
);
alignment
align
=
ALIGN_DEFAULT
;
int
i
=
0
;
if
(
begin
+
1
!=
end
)
++
i
;
do
{
switch
(
static_cast
<
char
>
(
begin
[
i
]))
{
case
'<'
:
align
=
ALIGN_LEFT
;
break
;
case
'>'
:
align
=
ALIGN_RIGHT
;
break
;
case
'='
:
align
=
ALIGN_NUMERIC
;
break
;
case
'^'
:
align
=
ALIGN_CENTER
;
break
;
}
if
(
align
!=
ALIGN_DEFAULT
)
{
if
(
i
>
0
)
{
auto
c
=
*
begin
;
if
(
c
==
'{'
)
return
handler
.
on_error
(
"invalid fill character '{'"
),
begin
;
begin
+=
2
;
handler
.
on_fill
(
c
);
}
else
++
begin
;
handler
.
on_align
(
align
);
break
;
}
}
while
(
i
--
>
0
);
return
begin
;
}
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template
<
typename
Handler
>
class
specs_checker
:
public
Handler
{
public:
FMT_CONSTEXPR
specs_checker
(
const
Handler
&
handler
,
internal
::
type
arg_type
)
:
Handler
(
handler
),
checker_
(
*
this
,
arg_type
)
{}
template
<
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
const
Char
*
parse_width
(
const
Char
*
begin
,
const
Char
*
end
,
Handler
&&
handler
)
{
FMT_ASSERT
(
begin
!=
end
,
""
);
if
(
'0'
<=
*
begin
&&
*
begin
<=
'9'
)
{
handler
.
on_width
(
parse_nonnegative_int
(
begin
,
end
,
handler
));
}
else
if
(
*
begin
==
'{'
)
{
++
begin
;
if
(
begin
!=
end
)
begin
=
parse_arg_id
(
begin
,
end
,
width_adapter
<
Handler
,
Char
>
(
handler
));
if
(
begin
==
end
||
*
begin
!=
'}'
)
return
handler
.
on_error
(
"invalid format string"
),
begin
;
++
begin
;
}
return
begin
;
}
FMT_CONSTEXPR
specs_checker
(
const
specs_checker
&
other
)
:
Handler
(
other
),
checker_
(
*
this
,
other
.
arg_type_
)
{}
template
<
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
const
Char
*
parse_precision
(
const
Char
*
begin
,
const
Char
*
end
,
Handler
&&
handler
)
{
++
begin
;
auto
c
=
begin
!=
end
?
*
begin
:
Char
();
if
(
'0'
<=
c
&&
c
<=
'9'
)
{
handler
.
on_precision
(
parse_nonnegative_int
(
begin
,
end
,
handler
));
}
else
if
(
c
==
'{'
)
{
++
begin
;
if
(
begin
!=
end
)
{
begin
=
parse_arg_id
(
begin
,
end
,
precision_adapter
<
Handler
,
Char
>
(
handler
));
}
if
(
begin
==
end
||
*
begin
++
!=
'}'
)
return
handler
.
on_error
(
"invalid format string"
),
begin
;
}
else
{
return
handler
.
on_error
(
"missing precision specifier"
),
begin
;
FMT_CONSTEXPR
void
on_align
(
alignment
align
)
{
if
(
align
==
ALIGN_NUMERIC
)
checker_
.
require_numeric_argument
();
Handler
::
on_align
(
align
);
}
handler
.
end_precision
();
return
begin
;
}
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
template
<
typename
Char
,
typename
SpecHandler
>
FMT_CONSTEXPR
const
Char
*
parse_format_specs
(
const
Char
*
begin
,
const
Char
*
end
,
SpecHandler
&&
handler
)
{
if
(
begin
==
end
||
*
begin
==
'}'
)
return
begin
;
FMT_CONSTEXPR
void
on_plus
()
{
checker_
.
check_sign
();
Handler
::
on_plus
();
}
begin
=
parse_align
(
begin
,
end
,
handler
);
if
(
begin
==
end
)
return
begin
;
FMT_CONSTEXPR
void
on_minus
()
{
checker_
.
check_sign
();
Handler
::
on_minus
();
}
// Parse sign.
switch
(
static_cast
<
char
>
(
*
begin
))
{
case
'+'
:
handler
.
on_plus
();
++
begin
;
break
;
case
'-'
:
handler
.
on_minus
();
++
begin
;
break
;
case
' '
:
handler
.
on_space
();
++
begin
;
break
;
FMT_CONSTEXPR
void
on_space
()
{
checker_
.
check_sign
();
Handler
::
on_space
();
}
if
(
begin
==
end
)
return
begin
;
if
(
*
begin
==
'#'
)
{
handler
.
on_hash
();
if
(
++
begin
==
end
)
return
begin
;
FMT_CONSTEXPR
void
on_hash
(
)
{
checker_
.
require_numeric_argument
();
Handler
::
on_hash
()
;
}
// Parse zero flag.
if
(
*
begin
==
'0'
)
{
handler
.
on_zero
();
if
(
++
begin
==
end
)
return
begin
;
FMT_CONSTEXPR
void
on_zero
()
{
checker_
.
require_numeric_argument
();
Handler
::
on_zero
();
}
begin
=
parse_width
(
begin
,
end
,
handler
);
if
(
begin
==
end
)
return
begin
;
FMT_CONSTEXPR
void
end_precision
()
{
checker_
.
check_precision
();
}
// Parse precision.
if
(
*
begin
==
'.'
)
{
begin
=
parse_precision
(
begin
,
end
,
handler
);
}
private:
numeric_specs_checker
<
Handler
>
checker_
;
};
// Parse type.
if
(
begin
!=
end
&&
*
begin
!=
'}'
)
handler
.
on_type
(
*
begin
++
);
return
begin
;
template
<
template
<
typename
>
class
Handler
,
typename
T
,
typename
FormatArg
,
typename
ErrorHandler
>
FMT_CONSTEXPR
void
set_dynamic_spec
(
T
&
value
,
FormatArg
arg
,
ErrorHandler
eh
)
{
unsigned
long
long
big_value
=
visit_format_arg
(
Handler
<
ErrorHandler
>
(
eh
),
arg
);
if
(
big_value
>
to_unsigned
((
std
::
numeric_limits
<
int
>::
max
)()))
eh
.
on_error
(
"number is too big"
);
value
=
static_cast
<
T
>
(
big_value
);
}
// Return the result via the out param to workaround gcc bug 77539.
template
<
bool
IS_CONSTEXPR
,
typename
T
,
typename
Ptr
=
const
T
*
>
FMT_CONSTEXPR
bool
find
(
Ptr
first
,
Ptr
last
,
T
value
,
Ptr
&
out
)
{
for
(
out
=
first
;
out
!=
last
;
++
out
)
{
if
(
*
out
==
value
)
return
true
;
}
return
false
;
}
struct
auto_id
{};
template
<
>
inline
bool
find
<
false
,
char
>
(
const
char
*
first
,
const
char
*
last
,
char
value
,
const
char
*&
out
)
{
out
=
static_cast
<
const
char
*>
(
std
::
memchr
(
first
,
value
,
internal
::
to_unsigned
(
last
-
first
)));
return
out
!=
nullptr
;
template
<
typename
Context
>
FMT_CONSTEXPR
typename
Context
::
format_arg
get_arg
(
Context
&
ctx
,
unsigned
id
)
{
auto
arg
=
ctx
.
arg
(
id
);
if
(
!
arg
)
ctx
.
on_error
(
"argument index out of range"
);
return
arg
;
}
template
<
typename
Handler
,
typename
Char
>
struct
id_adapter
{
FMT_CONSTEXPR
void
operator
()()
{
handler
.
on_arg_id
();
}
FMT_CONSTEXPR
void
operator
()(
unsigned
id
)
{
handler
.
on_arg_id
(
id
);
}
FMT_CONSTEXPR
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_arg_id
(
id
);
// The standard format specifier handler with checking.
template
<
typename
ParseContext
,
typename
Context
>
class
specs_handler
:
public
specs_setter
<
typename
Context
::
char_type
>
{
public:
typedef
typename
Context
::
char_type
char_type
;
FMT_CONSTEXPR
specs_handler
(
basic_format_specs
<
char_type
>&
specs
,
ParseContext
&
parse_ctx
,
Context
&
ctx
)
:
specs_setter
<
char_type
>
(
specs
),
parse_context_
(
parse_ctx
),
context_
(
ctx
)
{}
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_width
(
Id
arg_id
)
{
set_dynamic_spec
<
width_checker
>
(
this
->
specs_
.
width_
,
get_arg
(
arg_id
),
context_
.
error_handler
());
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_precision
(
Id
arg_id
)
{
set_dynamic_spec
<
precision_checker
>
(
this
->
specs_
.
precision
,
get_arg
(
arg_id
),
context_
.
error_handler
());
}
Handler
&
handler
;
};
template
<
bool
IS_CONSTEXPR
,
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
void
parse_format_string
(
basic_string_view
<
Char
>
format_str
,
Handler
&&
handler
)
{
struct
writer
{
FMT_CONSTEXPR
void
operator
()(
const
Char
*
begin
,
const
Char
*
end
)
{
if
(
begin
==
end
)
return
;
for
(;;)
{
const
Char
*
p
=
nullptr
;
if
(
!
find
<
IS_CONSTEXPR
>
(
begin
,
end
,
'}'
,
p
))
return
handler_
.
on_text
(
begin
,
end
);
++
p
;
if
(
p
==
end
||
*
p
!=
'}'
)
return
handler_
.
on_error
(
"unmatched '}' in format string"
);
handler_
.
on_text
(
begin
,
p
);
begin
=
p
+
1
;
}
}
Handler
&
handler_
;
}
write
{
handler
};
auto
begin
=
format_str
.
data
();
auto
end
=
begin
+
format_str
.
size
();
while
(
begin
!=
end
)
{
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const
Char
*
p
=
begin
;
if
(
*
begin
!=
'{'
&&
!
find
<
IS_CONSTEXPR
>
(
begin
,
end
,
'{'
,
p
))
return
write
(
begin
,
end
);
write
(
begin
,
p
);
++
p
;
if
(
p
==
end
)
return
handler
.
on_error
(
"invalid format string"
);
if
(
static_cast
<
char
>
(
*
p
)
==
'}'
)
{
handler
.
on_arg_id
();
handler
.
on_replacement_field
(
p
);
}
else
if
(
*
p
==
'{'
)
{
handler
.
on_text
(
p
,
p
+
1
);
}
else
{
p
=
parse_arg_id
(
p
,
end
,
id_adapter
<
Handler
,
Char
>
{
handler
});
Char
c
=
p
!=
end
?
*
p
:
Char
();
if
(
c
==
'}'
)
{
handler
.
on_replacement_field
(
p
);
}
else
if
(
c
==
':'
)
{
p
=
handler
.
on_format_specs
(
p
+
1
,
end
);
if
(
p
==
end
||
*
p
!=
'}'
)
return
handler
.
on_error
(
"unknown format specifier"
);
}
else
{
return
handler
.
on_error
(
"missing '}' in format string"
);
}
}
begin
=
p
+
1
;
void
on_error
(
const
char
*
message
)
{
context_
.
on_error
(
message
);
}
private:
// This is only needed for compatibility with gcc 4.4.
typedef
typename
Context
::
format_arg
format_arg
;
FMT_CONSTEXPR
format_arg
get_arg
(
auto_id
)
{
return
internal
::
get_arg
(
context_
,
parse_context_
.
next_arg_id
());
}
}
template
<
typename
T
,
typename
ParseContext
>
FMT_CONSTEXPR
const
typename
ParseContext
::
char_type
*
parse_format_specs
(
ParseContext
&
ctx
)
{
using
char_type
=
typename
ParseContext
::
char_type
;
using
context
=
buffer_context
<
char_type
>
;
using
mapped_type
=
conditional_t
<
internal
::
mapped_type_constant
<
T
,
context
>::
value
!=
internal
::
custom_type
,
decltype
(
arg_mapper
<
context
>
().
map
(
std
::
declval
<
T
>
())),
T
>
;
conditional_t
<
has_formatter
<
mapped_type
,
context
>::
value
,
formatter
<
mapped_type
,
char_type
>
,
internal
::
fallback_formatter
<
T
,
char_type
>>
f
;
return
f
.
parse
(
ctx
);
}
FMT_CONSTEXPR
format_arg
get_arg
(
unsigned
arg_id
)
{
parse_context_
.
check_arg_id
(
arg_id
);
return
internal
::
get_arg
(
context_
,
arg_id
);
}
template
<
typename
Char
,
typename
ErrorHandler
,
typename
...
Args
>
class
format_string_checker
{
public:
explicit
FMT_CONSTEXPR
format_string_checker
(
basic_string_view
<
Char
>
format_str
,
ErrorHandler
eh
)
:
arg_id_
((
std
::
numeric_limits
<
unsigned
>::
max
)()),
context_
(
format_str
,
eh
),
parse_funcs_
{
&
parse_format_specs
<
Args
,
parse_context_type
>
...}
{}
FMT_CONSTEXPR
format_arg
get_arg
(
basic_string_view
<
char_type
>
arg_id
)
{
parse_context_
.
check_arg_id
(
arg_id
);
return
context_
.
arg
(
arg_id
);
}
FMT_CONSTEXPR
void
on_text
(
const
Char
*
,
const
Char
*
)
{}
ParseContext
&
parse_context_
;
Context
&
context_
;
};
FMT_CONSTEXPR
void
on_arg_id
()
{
arg_id_
=
context_
.
next_arg_id
();
check_arg_id
();
}
FMT_CONSTEXPR
void
on_arg_id
(
unsigned
id
)
{
arg_id_
=
id
;
context_
.
check_arg_id
(
id
);
check_arg_id
();
struct
string_view_metadata
{
FMT_CONSTEXPR
string_view_metadata
()
:
offset_
(
0u
),
size_
(
0u
)
{}
template
<
typename
Char
>
FMT_CONSTEXPR
string_view_metadata
(
basic_string_view
<
Char
>
primary_string
,
basic_string_view
<
Char
>
view
)
:
offset_
(
view
.
data
()
-
primary_string
.
data
()),
size_
(
view
.
size
())
{}
FMT_CONSTEXPR
string_view_metadata
(
std
::
size_t
offset
,
std
::
size_t
size
)
:
offset_
(
offset
),
size_
(
size
)
{}
template
<
typename
Char
>
FMT_CONSTEXPR
basic_string_view
<
Char
>
to_view
(
const
Char
*
str
)
const
{
return
{
str
+
offset_
,
size_
};
}
FMT_CONSTEXPR
void
on_arg_id
(
basic_string_view
<
Char
>
)
{
on_error
(
"compile-time checks don't support named arguments"
);
std
::
size_t
offset_
;
std
::
size_t
size_
;
};
// An argument reference.
template
<
typename
Char
>
struct
arg_ref
{
enum
Kind
{
NONE
,
INDEX
,
NAME
};
typedef
Char
char_type
;
FMT_CONSTEXPR
arg_ref
()
:
kind
(
NONE
),
val
()
{}
FMT_CONSTEXPR
explicit
arg_ref
(
unsigned
index
)
:
kind
(
INDEX
),
val
(
index
)
{}
FMT_CONSTEXPR
explicit
arg_ref
(
string_view_metadata
name
)
:
kind
(
NAME
),
val
(
name
)
{}
FMT_CONSTEXPR
arg_ref
&
operator
=
(
unsigned
idx
)
{
kind
=
INDEX
;
val
.
index
=
idx
;
return
*
this
;
}
FMT_CONSTEXPR
void
on_replacement_field
(
const
Char
*
)
{}
Kind
kind
;
union
value
{
FMT_CONSTEXPR
value
()
:
index
(
0u
)
{}
FMT_CONSTEXPR
value
(
unsigned
id
)
:
index
(
id
)
{}
FMT_CONSTEXPR
value
(
string_view_metadata
n
)
:
name
(
n
)
{}
FMT_CONSTEXPR
const
Char
*
on_format_specs
(
const
Char
*
begin
,
const
Char
*
)
{
advance_to
(
context_
,
begin
);
return
arg_id_
<
NUM_ARGS
?
parse_funcs_
[
arg_id_
](
context_
)
:
begin
;
unsigned
index
;
string_view_metadata
name
;
}
val
;
};
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// different sets of arguments (precompilation of format strings).
template
<
typename
Char
>
struct
dynamic_format_specs
:
basic_format_specs
<
Char
>
{
arg_ref
<
Char
>
width_ref
;
arg_ref
<
Char
>
precision_ref
;
};
// Format spec handler that saves references to arguments representing dynamic
// width and precision to be resolved at formatting time.
template
<
typename
ParseContext
>
class
dynamic_specs_handler
:
public
specs_setter
<
typename
ParseContext
::
char_type
>
{
public:
typedef
typename
ParseContext
::
char_type
char_type
;
FMT_CONSTEXPR
dynamic_specs_handler
(
dynamic_format_specs
<
char_type
>&
specs
,
ParseContext
&
ctx
)
:
specs_setter
<
char_type
>
(
specs
),
specs_
(
specs
),
context_
(
ctx
)
{}
FMT_CONSTEXPR
dynamic_specs_handler
(
const
dynamic_specs_handler
&
other
)
:
specs_setter
<
char_type
>
(
other
),
specs_
(
other
.
specs_
),
context_
(
other
.
context_
)
{}
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_width
(
Id
arg_id
)
{
specs_
.
width_ref
=
make_arg_ref
(
arg_id
);
}
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_precision
(
Id
arg_id
)
{
specs_
.
precision_ref
=
make_arg_ref
(
arg_id
);
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
...
...
@@ -2108,586 +2179,513 @@ class format_string_checker {
}
private:
typedef
basic_parse_context
<
Char
,
ErrorHandler
>
parse_context_type
;
enum
{
NUM_ARGS
=
sizeof
...(
Args
)
};
typedef
arg_ref
<
char_type
>
arg_ref_type
;
FMT_CONSTEXPR
void
check_arg_id
()
{
if
(
arg_id_
>=
NUM_ARGS
)
context_
.
on_error
(
"argument index out of range"
);
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
unsigned
arg_id
)
{
context_
.
check_arg_id
(
arg_id
);
return
arg_ref_type
(
arg_id
);
}
// Format specifier parsing function.
typedef
const
Char
*
(
*
parse_func
)(
parse_context_type
&
);
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
auto_id
)
{
return
arg_ref_type
(
context_
.
next_arg_id
());
}
unsigned
arg_id_
;
parse_context_type
context_
;
parse_func
parse_funcs_
[
NUM_ARGS
>
0
?
NUM_ARGS
:
1
];
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
basic_string_view
<
char_type
>
arg_id
)
{
context_
.
check_arg_id
(
arg_id
);
basic_string_view
<
char_type
>
format_str
(
context_
.
begin
(),
context_
.
end
()
-
context_
.
begin
());
const
auto
id_metadata
=
string_view_metadata
(
format_str
,
arg_id
);
return
arg_ref_type
(
id_metadata
);
}
dynamic_format_specs
<
char_type
>&
specs_
;
ParseContext
&
context_
;
};
template
<
typename
Char
,
typename
ErrorHandler
,
typename
...
Args
>
FMT_CONSTEXPR
bool
do_check_format_string
(
basic_string_view
<
Char
>
s
,
ErrorHandler
eh
=
ErrorHandler
())
{
format_string_checker
<
Char
,
ErrorHandler
,
Args
...
>
checker
(
s
,
eh
);
parse_format_string
<
true
>
(
s
,
checker
);
return
true
;
template
<
typename
Char
,
typename
IDHandler
>
FMT_CONSTEXPR
const
Char
*
parse_arg_id
(
const
Char
*
begin
,
const
Char
*
end
,
IDHandler
&&
handler
)
{
assert
(
begin
!=
end
);
Char
c
=
*
begin
;
if
(
c
==
'}'
||
c
==
':'
)
return
handler
(),
begin
;
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
unsigned
index
=
parse_nonnegative_int
(
begin
,
end
,
handler
);
if
(
begin
==
end
||
(
*
begin
!=
'}'
&&
*
begin
!=
':'
))
return
handler
.
on_error
(
"invalid format string"
),
begin
;
handler
(
index
);
return
begin
;
}
if
(
!
is_name_start
(
c
))
return
handler
.
on_error
(
"invalid format string"
),
begin
;
auto
it
=
begin
;
do
{
++
it
;
}
while
(
it
!=
end
&&
(
is_name_start
(
c
=
*
it
)
||
(
'0'
<=
c
&&
c
<=
'9'
)));
handler
(
basic_string_view
<
Char
>
(
begin
,
to_unsigned
(
it
-
begin
)));
return
it
;
}
template
<
typename
...
Args
,
typename
S
,
enable_if_t
<
(
is_compile_string
<
S
>
::
value
),
int
>>
void
check_format_string
(
S
format_str
)
{
typedef
typename
S
::
char_type
char_t
;
FMT_CONSTEXPR_DECL
bool
invalid_format
=
internal
::
do_check_format_string
<
char_t
,
internal
::
error_handler
,
Args
...
>
(
to_string_view
(
format_str
));
(
void
)
invalid_format
;
}
// Adapts SpecHandler to IDHandler API for dynamic width.
template
<
typename
SpecHandler
,
typename
Char
>
struct
width_adapter
{
explicit
FMT_CONSTEXPR
width_adapter
(
SpecHandler
&
h
)
:
handler
(
h
)
{}
template
<
template
<
typename
>
class
Handler
,
typename
Spec
,
typename
Context
>
void
handle_dynamic_spec
(
Spec
&
value
,
arg_ref
<
typename
Context
::
char_type
>
ref
,
Context
&
ctx
,
const
typename
Context
::
char_type
*
format_str
)
{
typedef
typename
Context
::
char_type
char_type
;
switch
(
ref
.
kind
)
{
case
arg_ref
<
char_type
>
:
:
NONE
:
break
;
case
arg_ref
<
char_type
>
:
:
INDEX
:
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
arg
(
ref
.
val
.
index
),
ctx
.
error_handler
());
break
;
case
arg_ref
<
char_type
>
:
:
NAME
:
{
const
auto
arg_id
=
ref
.
val
.
name
.
to_view
(
format_str
);
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
arg
(
arg_id
),
ctx
.
error_handler
());
}
break
;
FMT_CONSTEXPR
void
operator
()()
{
handler
.
on_dynamic_width
(
auto_id
());
}
FMT_CONSTEXPR
void
operator
()(
unsigned
id
)
{
handler
.
on_dynamic_width
(
id
);
}
FMT_CONSTEXPR
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_dynamic_width
(
id
);
}
}
}
// namespace internal
/** The default argument formatter. */
template
<
typename
Range
>
class
arg_formatter
:
public
internal
::
arg_formatter_base
<
Range
>
{
private:
typedef
typename
Range
::
value_type
char_type
;
typedef
internal
::
arg_formatter_base
<
Range
>
base
;
typedef
basic_format_context
<
typename
base
::
iterator
,
char_type
>
context_type
;
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
}
context_type
&
ctx_
;
basic_parse_context
<
char_type
>*
parse_ctx_
;
SpecHandler
&
handler
;
}
;
public:
typedef
Range
range
;
typedef
typename
base
::
iterator
iterator
;
typedef
typename
base
::
format_specs
format_specs
;
// Adapts SpecHandler to IDHandler API for dynamic precision.
template
<
typename
SpecHandler
,
typename
Char
>
struct
precision_adapter
{
explicit
FMT_CONSTEXPR
precision_adapter
(
SpecHandler
&
h
)
:
handler
(
h
)
{}
/**
\rst
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
*spec* contains format specifier information for standard argument types.
\endrst
*/
explicit
arg_formatter
(
context_type
&
ctx
,
basic_parse_context
<
char_type
>*
parse_ctx
=
nullptr
,
format_specs
*
spec
=
nullptr
)
:
base
(
Range
(
ctx
.
out
()),
spec
,
ctx
.
locale
()),
ctx_
(
ctx
),
parse_ctx_
(
parse_ctx
)
{}
FMT_CONSTEXPR
void
operator
()()
{
handler
.
on_dynamic_precision
(
auto_id
());
}
FMT_CONSTEXPR
void
operator
()(
unsigned
id
)
{
handler
.
on_dynamic_precision
(
id
);
}
FMT_CONSTEXPR
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_dynamic_precision
(
id
);
}
FMT_DEPRECATED
arg_formatter
(
context_type
&
ctx
,
format_specs
&
spec
)
:
base
(
Range
(
ctx
.
out
()),
&
spec
),
ctx_
(
ctx
)
{}
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
}
using
base
::
operator
();
SpecHandler
&
handler
;
};
/** Formats an argument of a user-defined type. */
iterator
operator
()(
typename
basic_format_arg
<
context_type
>::
handle
handle
)
{
handle
.
format
(
*
parse_ctx_
,
ctx_
);
return
this
->
out
();
}
};
/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
class
FMT_API
system_error
:
public
std
::
runtime_error
{
private:
FMT_API
void
init
(
int
err_code
,
string_view
format_str
,
format_args
args
);
protected:
int
error_code_
;
system_error
()
:
std
::
runtime_error
(
""
)
{}
public:
/**
\rst
Constructs a :class:`fmt::system_error` object with a description
formatted with `fmt::format_system_error`. *message* and additional
arguments passed into the constructor are formatted similarly to
`fmt::format`.
**Example**::
// This throws a system_error with the description
// cannot open file 'madeup': No such file or directory
// or similar (system message may vary).
const char *filename = "madeup";
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template
<
typename
...
Args
>
system_error
(
int
error_code
,
string_view
message
,
const
Args
&
...
args
)
:
std
::
runtime_error
(
""
)
{
init
(
error_code
,
message
,
make_format_args
(
args
...));
}
~
system_error
()
FMT_NOEXCEPT
;
int
error_code
()
const
{
return
error_code_
;
}
};
/**
\rst
Formats an error returned by an operating system or a language runtime,
for example a file opening error, and writes it to *out* in the following
form:
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the passed message and *<system-message>* is
the system message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
FMT_API
void
format_system_error
(
internal
::
buffer
<
char
>&
out
,
int
error_code
,
fmt
::
string_view
message
)
FMT_NOEXCEPT
;
/**
This template provides operations for formatting and writing data into a
character range.
*/
template
<
typename
Range
>
class
basic_writer
{
public:
typedef
typename
Range
::
value_type
char_type
;
typedef
decltype
(
std
::
declval
<
Range
>
().
begin
())
iterator
;
typedef
basic_format_specs
<
char_type
>
format_specs
;
private:
iterator
out_
;
// Output iterator.
internal
::
locale_ref
locale_
;
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
auto
reserve
(
std
::
size_t
n
)
->
decltype
(
internal
::
reserve
(
out_
,
n
))
{
return
internal
::
reserve
(
out_
,
n
);
}
// Writes a value in the format
// <left-padding><value><right-padding>
// where <value> is written by f(it).
template
<
typename
F
>
void
write_padded
(
const
align_spec
&
spec
,
F
&&
f
)
{
unsigned
width
=
spec
.
width
();
// User-perceived width (in code points).
size_t
size
=
f
.
size
();
// The number of code units.
size_t
num_code_points
=
width
!=
0
?
f
.
width
()
:
size
;
if
(
width
<=
num_code_points
)
return
f
(
reserve
(
size
));
auto
&&
it
=
reserve
(
width
+
(
size
-
num_code_points
));
char_type
fill
=
static_cast
<
char_type
>
(
spec
.
fill
());
std
::
size_t
padding
=
width
-
num_code_points
;
if
(
spec
.
align
()
==
ALIGN_RIGHT
)
{
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
f
(
it
);
}
else
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
std
::
size_t
left_padding
=
padding
/
2
;
it
=
std
::
fill_n
(
it
,
left_padding
,
fill
);
f
(
it
);
it
=
std
::
fill_n
(
it
,
padding
-
left_padding
,
fill
);
}
else
{
f
(
it
);
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
}
}
template
<
typename
F
>
struct
padded_int_writer
{
size_t
size_
;
string_view
prefix
;
char_type
fill
;
std
::
size_t
padding
;
F
f
;
size_t
size
()
const
{
return
size_
;
}
size_t
width
()
const
{
return
size_
;
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
if
(
prefix
.
size
()
!=
0
)
it
=
internal
::
copy_str
<
char_type
>
(
prefix
.
begin
(),
prefix
.
end
(),
it
);
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
f
(
it
);
// Parses fill and alignment.
template
<
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
const
Char
*
parse_align
(
const
Char
*
begin
,
const
Char
*
end
,
Handler
&&
handler
)
{
FMT_ASSERT
(
begin
!=
end
,
""
);
alignment
align
=
ALIGN_DEFAULT
;
int
i
=
0
;
if
(
begin
+
1
!=
end
)
++
i
;
do
{
switch
(
static_cast
<
char
>
(
begin
[
i
]))
{
case
'<'
:
align
=
ALIGN_LEFT
;
break
;
case
'>'
:
align
=
ALIGN_RIGHT
;
break
;
case
'='
:
align
=
ALIGN_NUMERIC
;
break
;
case
'^'
:
align
=
ALIGN_CENTER
;
break
;
}
};
// Writes an integer in the format
// <left-padding><prefix><numeric-padding><digits><right-padding>
// where <digits> are written by f(it).
template
<
typename
Spec
,
typename
F
>
void
write_int
(
int
num_digits
,
string_view
prefix
,
const
Spec
&
spec
,
F
f
)
{
std
::
size_t
size
=
prefix
.
size
()
+
internal
::
to_unsigned
(
num_digits
);
char_type
fill
=
static_cast
<
char_type
>
(
spec
.
fill
());
std
::
size_t
padding
=
0
;
if
(
spec
.
align
()
==
ALIGN_NUMERIC
)
{
if
(
spec
.
width
()
>
size
)
{
padding
=
spec
.
width
()
-
size
;
size
=
spec
.
width
();
}
}
else
if
(
spec
.
precision
>
num_digits
)
{
size
=
prefix
.
size
()
+
internal
::
to_unsigned
(
spec
.
precision
);
padding
=
internal
::
to_unsigned
(
spec
.
precision
-
num_digits
);
fill
=
static_cast
<
char_type
>
(
'0'
);
if
(
align
!=
ALIGN_DEFAULT
)
{
if
(
i
>
0
)
{
auto
c
=
*
begin
;
if
(
c
==
'{'
)
return
handler
.
on_error
(
"invalid fill character '{'"
),
begin
;
begin
+=
2
;
handler
.
on_fill
(
c
);
}
else
++
begin
;
handler
.
on_align
(
align
);
break
;
}
align_spec
as
=
spec
;
if
(
spec
.
align
()
==
ALIGN_DEFAULT
)
as
.
align_
=
ALIGN_RIGHT
;
write_padded
(
as
,
padded_int_writer
<
F
>
{
size
,
prefix
,
fill
,
padding
,
f
});
}
}
while
(
i
--
>
0
);
return
begin
;
}
// Writes a decimal integer.
template
<
typename
Int
>
void
write_decimal
(
Int
value
)
{
typedef
typename
internal
::
int_traits
<
Int
>::
main_type
main_type
;
main_type
abs_value
=
static_cast
<
main_type
>
(
value
);
bool
is_negative
=
internal
::
is_negative
(
value
);
if
(
is_negative
)
abs_value
=
0
-
abs_value
;
int
num_digits
=
internal
::
count_digits
(
abs_value
);
auto
&&
it
=
reserve
((
is_negative
?
1
:
0
)
+
static_cast
<
size_t
>
(
num_digits
));
if
(
is_negative
)
*
it
++
=
static_cast
<
char_type
>
(
'-'
);
it
=
internal
::
format_decimal
<
char_type
>
(
it
,
abs_value
,
num_digits
);
template
<
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
const
Char
*
parse_width
(
const
Char
*
begin
,
const
Char
*
end
,
Handler
&&
handler
)
{
FMT_ASSERT
(
begin
!=
end
,
""
);
if
(
'0'
<=
*
begin
&&
*
begin
<=
'9'
)
{
handler
.
on_width
(
parse_nonnegative_int
(
begin
,
end
,
handler
));
}
else
if
(
*
begin
==
'{'
)
{
++
begin
;
if
(
begin
!=
end
)
begin
=
parse_arg_id
(
begin
,
end
,
width_adapter
<
Handler
,
Char
>
(
handler
));
if
(
begin
==
end
||
*
begin
!=
'}'
)
return
handler
.
on_error
(
"invalid format string"
),
begin
;
++
begin
;
}
return
begin
;
}
// The handle_int_type_spec handler that writes an integer.
template
<
typename
Int
,
typename
Spec
>
struct
int_writer
{
typedef
typename
internal
::
int_traits
<
Int
>::
main_type
unsigned_type
;
basic_writer
<
Range
>&
writer
;
const
Spec
&
spec
;
unsigned_type
abs_value
;
char
prefix
[
4
];
unsigned
prefix_size
;
string_view
get_prefix
()
const
{
return
string_view
(
prefix
,
prefix_size
);
}
int_writer
(
basic_writer
<
Range
>&
w
,
Int
value
,
const
Spec
&
s
)
:
writer
(
w
),
spec
(
s
),
abs_value
(
static_cast
<
unsigned_type
>
(
value
)),
prefix_size
(
0
)
{
if
(
internal
::
is_negative
(
value
))
{
prefix
[
0
]
=
'-'
;
++
prefix_size
;
abs_value
=
0
-
abs_value
;
}
else
if
(
spec
.
has
(
SIGN_FLAG
))
{
prefix
[
0
]
=
spec
.
has
(
PLUS_FLAG
)
?
'+'
:
' '
;
++
prefix_size
;
}
}
struct
dec_writer
{
unsigned_type
abs_value
;
int
num_digits
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
format_decimal
<
char_type
>
(
it
,
abs_value
,
num_digits
);
}
};
void
on_dec
()
{
int
num_digits
=
internal
::
count_digits
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
spec
,
dec_writer
{
abs_value
,
num_digits
});
}
struct
hex_writer
{
int_writer
&
self
;
int
num_digits
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
format_uint
<
4
,
char_type
>
(
it
,
self
.
abs_value
,
num_digits
,
self
.
spec
.
type
!=
'x'
);
}
};
void
on_hex
()
{
if
(
spec
.
has
(
HASH_FLAG
))
{
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
static_cast
<
char
>
(
spec
.
type
);
}
int
num_digits
=
internal
::
count_digits
<
4
>
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
spec
,
hex_writer
{
*
this
,
num_digits
});
template
<
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
const
Char
*
parse_precision
(
const
Char
*
begin
,
const
Char
*
end
,
Handler
&&
handler
)
{
++
begin
;
auto
c
=
begin
!=
end
?
*
begin
:
Char
();
if
(
'0'
<=
c
&&
c
<=
'9'
)
{
handler
.
on_precision
(
parse_nonnegative_int
(
begin
,
end
,
handler
));
}
else
if
(
c
==
'{'
)
{
++
begin
;
if
(
begin
!=
end
)
{
begin
=
parse_arg_id
(
begin
,
end
,
precision_adapter
<
Handler
,
Char
>
(
handler
));
}
if
(
begin
==
end
||
*
begin
++
!=
'}'
)
return
handler
.
on_error
(
"invalid format string"
),
begin
;
}
else
{
return
handler
.
on_error
(
"missing precision specifier"
),
begin
;
}
handler
.
end_precision
();
return
begin
;
}
template
<
int
BITS
>
struct
bin_writer
{
unsigned_type
abs_value
;
int
num_digits
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
format_uint
<
BITS
,
char_type
>
(
it
,
abs_value
,
num_digits
);
}
};
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
template
<
typename
Char
,
typename
SpecHandler
>
FMT_CONSTEXPR
const
Char
*
parse_format_specs
(
const
Char
*
begin
,
const
Char
*
end
,
SpecHandler
&&
handler
)
{
if
(
begin
==
end
||
*
begin
==
'}'
)
return
begin
;
void
on_bin
()
{
if
(
spec
.
has
(
HASH_FLAG
))
{
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
static_cast
<
char
>
(
spec
.
type
);
}
int
num_digits
=
internal
::
count_digits
<
1
>
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
spec
,
bin_writer
<
1
>
{
abs_value
,
num_digits
});
}
begin
=
parse_align
(
begin
,
end
,
handler
);
if
(
begin
==
end
)
return
begin
;
void
on_oct
()
{
int
num_digits
=
internal
::
count_digits
<
3
>
(
abs_value
);
if
(
spec
.
has
(
HASH_FLAG
)
&&
spec
.
precision
<=
num_digits
)
{
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
prefix
[
prefix_size
++
]
=
'0'
;
}
writer
.
write_int
(
num_digits
,
get_prefix
(),
spec
,
bin_writer
<
3
>
{
abs_value
,
num_digits
});
}
// Parse sign.
switch
(
static_cast
<
char
>
(
*
begin
))
{
case
'+'
:
handler
.
on_plus
();
++
begin
;
break
;
case
'-'
:
handler
.
on_minus
();
++
begin
;
break
;
case
' '
:
handler
.
on_space
();
++
begin
;
break
;
}
if
(
begin
==
end
)
return
begin
;
enum
{
SEP_SIZE
=
1
};
if
(
*
begin
==
'#'
)
{
handler
.
on_hash
();
if
(
++
begin
==
end
)
return
begin
;
}
struct
num_writer
{
unsigned_type
abs_value
;
int
size
;
char_type
sep
;
// Parse zero flag.
if
(
*
begin
==
'0'
)
{
handler
.
on_zero
();
if
(
++
begin
==
end
)
return
begin
;
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
basic_string_view
<
char_type
>
s
(
&
sep
,
SEP_SIZE
);
it
=
internal
::
format_decimal
<
char_type
>
(
it
,
abs_value
,
size
,
internal
::
add_thousands_sep
<
char_type
>
(
s
));
}
};
begin
=
parse_width
(
begin
,
end
,
handler
);
if
(
begin
==
end
)
return
begin
;
void
on_num
()
{
int
num_digits
=
internal
::
count_digits
(
abs_value
);
char_type
sep
=
internal
::
thousands_sep
<
char_type
>
(
writer
.
locale_
);
int
size
=
num_digits
+
SEP_SIZE
*
((
num_digits
-
1
)
/
3
);
writer
.
write_int
(
size
,
get_prefix
(),
spec
,
num_writer
{
abs_value
,
size
,
sep
});
}
// Parse precision.
if
(
*
begin
==
'.'
)
{
begin
=
parse_precision
(
begin
,
end
,
handler
);
}
FMT_NORETURN
void
on_error
()
{
FMT_THROW
(
format_error
(
"invalid type specifier"
)
);
}
};
// Parse type.
if
(
begin
!=
end
&&
*
begin
!=
'}'
)
handler
.
on_type
(
*
begin
++
);
return
begin
;
}
// Writes a formatted integer
.
template
<
typename
T
,
typename
Spec
>
void
write_int
(
T
value
,
const
Spec
&
spec
)
{
internal
::
handle_int_type_spec
(
spec
.
type
,
int_writer
<
T
,
Spec
>
(
*
this
,
value
,
spec
))
;
// Return the result via the out param to workaround gcc bug 77539
.
template
<
bool
IS_CONSTEXPR
,
typename
T
,
typename
Ptr
=
const
T
*
>
FMT_CONSTEXPR
bool
find
(
Ptr
first
,
Ptr
last
,
T
value
,
Ptr
&
out
)
{
for
(
out
=
first
;
out
!=
last
;
++
out
)
{
if
(
*
out
==
value
)
return
true
;
}
return
false
;
}
enum
{
INF_SIZE
=
3
};
// This is an enum to workaround a bug in MSVC.
template
<
>
inline
bool
find
<
false
,
char
>
(
const
char
*
first
,
const
char
*
last
,
char
value
,
const
char
*&
out
)
{
out
=
static_cast
<
const
char
*>
(
std
::
memchr
(
first
,
value
,
internal
::
to_unsigned
(
last
-
first
)));
return
out
!=
nullptr
;
}
struct
inf_or_nan_writer
{
char
sign
;
bool
as_percentage
;
const
char
*
str
;
template
<
typename
Handler
,
typename
Char
>
struct
id_adapter
{
FMT_CONSTEXPR
void
operator
()()
{
handler
.
on_arg_id
();
}
FMT_CONSTEXPR
void
operator
()(
unsigned
id
)
{
handler
.
on_arg_id
(
id
);
}
FMT_CONSTEXPR
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_arg_id
(
id
);
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
}
Handler
&
handler
;
};
size_t
size
()
const
{
return
static_cast
<
std
::
size_t
>
(
INF_SIZE
+
(
sign
?
1
:
0
)
+
(
as_percentage
?
1
:
0
));
template
<
bool
IS_CONSTEXPR
,
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
void
parse_format_string
(
basic_string_view
<
Char
>
format_str
,
Handler
&&
handler
)
{
struct
writer
{
FMT_CONSTEXPR
void
operator
()(
const
Char
*
begin
,
const
Char
*
end
)
{
if
(
begin
==
end
)
return
;
for
(;;)
{
const
Char
*
p
=
nullptr
;
if
(
!
find
<
IS_CONSTEXPR
>
(
begin
,
end
,
'}'
,
p
))
return
handler_
.
on_text
(
begin
,
end
);
++
p
;
if
(
p
==
end
||
*
p
!=
'}'
)
return
handler_
.
on_error
(
"unmatched '}' in format string"
);
handler_
.
on_text
(
begin
,
p
);
begin
=
p
+
1
;
}
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
if
(
sign
)
*
it
++
=
static_cast
<
char_type
>
(
sign
);
it
=
internal
::
copy_str
<
char_type
>
(
str
,
str
+
static_cast
<
std
::
size_t
>
(
INF_SIZE
),
it
);
if
(
as_percentage
)
*
it
++
=
static_cast
<
char_type
>
(
'%'
);
Handler
&
handler_
;
}
write
{
handler
};
auto
begin
=
format_str
.
data
();
auto
end
=
begin
+
format_str
.
size
();
while
(
begin
!=
end
)
{
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const
Char
*
p
=
begin
;
if
(
*
begin
!=
'{'
&&
!
find
<
IS_CONSTEXPR
>
(
begin
,
end
,
'{'
,
p
))
return
write
(
begin
,
end
);
write
(
begin
,
p
);
++
p
;
if
(
p
==
end
)
return
handler
.
on_error
(
"invalid format string"
);
if
(
static_cast
<
char
>
(
*
p
)
==
'}'
)
{
handler
.
on_arg_id
();
handler
.
on_replacement_field
(
p
);
}
else
if
(
*
p
==
'{'
)
{
handler
.
on_text
(
p
,
p
+
1
);
}
else
{
p
=
parse_arg_id
(
p
,
end
,
id_adapter
<
Handler
,
Char
>
{
handler
});
Char
c
=
p
!=
end
?
*
p
:
Char
();
if
(
c
==
'}'
)
{
handler
.
on_replacement_field
(
p
);
}
else
if
(
c
==
':'
)
{
p
=
handler
.
on_format_specs
(
p
+
1
,
end
);
if
(
p
==
end
||
*
p
!=
'}'
)
return
handler
.
on_error
(
"unknown format specifier"
);
}
else
{
return
handler
.
on_error
(
"missing '}' in format string"
);
}
}
};
struct
double_writer
{
char
sign
;
internal
::
buffer
<
char
>&
buffer
;
begin
=
p
+
1
;
}
}
size_t
size
()
const
{
return
buffer
.
size
()
+
(
sign
?
1
:
0
);
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
T
,
typename
ParseContext
>
FMT_CONSTEXPR
const
typename
ParseContext
::
char_type
*
parse_format_specs
(
ParseContext
&
ctx
)
{
using
char_type
=
typename
ParseContext
::
char_type
;
using
context
=
buffer_context
<
char_type
>
;
using
mapped_type
=
conditional_t
<
internal
::
mapped_type_constant
<
T
,
context
>::
value
!=
internal
::
custom_type
,
decltype
(
arg_mapper
<
context
>
().
map
(
std
::
declval
<
T
>
())),
T
>
;
conditional_t
<
has_formatter
<
mapped_type
,
context
>::
value
,
formatter
<
mapped_type
,
char_type
>
,
internal
::
fallback_formatter
<
T
,
char_type
>>
f
;
return
f
.
parse
(
ctx
);
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
sign
)
*
it
++
=
static_cast
<
char_type
>
(
sign
);
it
=
internal
::
copy_str
<
char_type
>
(
buffer
.
begin
(),
buffer
.
end
(),
it
);
}
};
template
<
typename
Char
,
typename
ErrorHandler
,
typename
...
Args
>
class
format_string_checker
{
public:
explicit
FMT_CONSTEXPR
format_string_checker
(
basic_string_view
<
Char
>
format_str
,
ErrorHandler
eh
)
:
arg_id_
((
std
::
numeric_limits
<
unsigned
>::
max
)()),
context_
(
format_str
,
eh
),
parse_funcs_
{
&
parse_format_specs
<
Args
,
parse_context_type
>
...}
{}
class
grisu_writer
{
private:
internal
::
buffer
<
char
>&
digits_
;
size_t
size_
;
char
sign_
;
int
exp_
;
internal
::
gen_digits_params
params_
;
FMT_CONSTEXPR
void
on_text
(
const
Char
*
,
const
Char
*
)
{}
public:
grisu_writer
(
char
sign
,
internal
::
buffer
<
char
>&
digits
,
int
exp
,
const
internal
::
gen_digits_params
&
params
)
:
digits_
(
digits
),
sign_
(
sign
),
exp_
(
exp
),
params_
(
params
)
{
int
num_digits
=
static_cast
<
int
>
(
digits
.
size
());
int
full_exp
=
num_digits
+
exp
-
1
;
int
precision
=
params
.
num_digits
>
0
?
params
.
num_digits
:
11
;
params_
.
fixed
|=
full_exp
>=
-
4
&&
full_exp
<
precision
;
auto
it
=
internal
::
grisu_prettify
<
char
>
(
digits
.
data
(),
num_digits
,
exp
,
internal
::
counting_iterator
<
char
>
(),
params_
);
size_
=
it
.
count
();
}
FMT_CONSTEXPR
void
on_arg_id
()
{
arg_id_
=
context_
.
next_arg_id
();
check_arg_id
();
}
FMT_CONSTEXPR
void
on_arg_id
(
unsigned
id
)
{
arg_id_
=
id
;
context_
.
check_arg_id
(
id
);
check_arg_id
();
}
FMT_CONSTEXPR
void
on_arg_id
(
basic_string_view
<
Char
>
)
{
on_error
(
"compile-time checks don't support named arguments"
);
}
size_t
size
()
const
{
return
size_
+
(
sign_
?
1
:
0
);
}
size_t
width
()
const
{
return
size
();
}
FMT_CONSTEXPR
void
on_replacement_field
(
const
Char
*
)
{}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
sign_
)
*
it
++
=
static_cast
<
char_type
>
(
sign_
);
int
num_digits
=
static_cast
<
int
>
(
digits_
.
size
());
it
=
internal
::
grisu_prettify
<
char_type
>
(
digits_
.
data
(),
num_digits
,
exp_
,
it
,
params_
);
}
};
FMT_CONSTEXPR
const
Char
*
on_format_specs
(
const
Char
*
begin
,
const
Char
*
)
{
advance_to
(
context_
,
begin
);
return
arg_id_
<
NUM_ARGS
?
parse_funcs_
[
arg_id_
](
context_
)
:
begin
;
}
// Formats a floating-point number (double or long double).
template
<
typename
T
>
void
write_double
(
T
value
,
const
format_specs
&
spec
);
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
context_
.
on_error
(
message
);
}
template
<
typename
Char
>
struct
str_writer
{
const
Char
*
s
;
size_t
size_
;
private:
typedef
basic_parse_context
<
Char
,
ErrorHandler
>
parse_context_type
;
enum
{
NUM_ARGS
=
sizeof
...(
Args
)
}
;
size_t
size
()
const
{
return
size_
;
}
size_t
width
()
const
{
return
internal
::
count_code_points
(
basic_string_view
<
Char
>
(
s
,
size_
));
}
FMT_CONSTEXPR
void
check_arg_id
()
{
if
(
arg_id_
>=
NUM_ARGS
)
context_
.
on_error
(
"argument index out of range"
);
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
copy_str
<
char_type
>
(
s
,
s
+
size_
,
it
);
}
};
// Format specifier parsing function.
typedef
const
Char
*
(
*
parse_func
)(
parse_context_type
&
);
template
<
typename
UIntPtr
>
struct
pointer_writer
{
UIntPtr
value
;
int
num_digits
;
unsigned
arg_id_
;
parse_context_type
context_
;
parse_func
parse_funcs_
[
NUM_ARGS
>
0
?
NUM_ARGS
:
1
];
};
size_t
size
()
const
{
return
num_digits
+
2
;
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
Char
,
typename
ErrorHandler
,
typename
...
Args
>
FMT_CONSTEXPR
bool
do_check_format_string
(
basic_string_view
<
Char
>
s
,
ErrorHandler
eh
=
ErrorHandler
())
{
format_string_checker
<
Char
,
ErrorHandler
,
Args
...
>
checker
(
s
,
eh
);
parse_format_string
<
true
>
(
s
,
checker
);
return
true
;
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
*
it
++
=
static_cast
<
char_type
>
(
'0'
);
*
it
++
=
static_cast
<
char_type
>
(
'x'
);
it
=
internal
::
format_uint
<
4
,
char_type
>
(
it
,
value
,
num_digits
);
}
};
template
<
typename
...
Args
,
typename
S
,
enable_if_t
<
(
is_compile_string
<
S
>
::
value
),
int
>>
void
check_format_string
(
S
format_str
)
{
typedef
typename
S
::
char_type
char_t
;
FMT_CONSTEXPR_DECL
bool
invalid_format
=
internal
::
do_check_format_string
<
char_t
,
internal
::
error_handler
,
Args
...
>
(
to_string_view
(
format_str
));
(
void
)
invalid_format
;
}
template
<
typename
Char
,
typename
ErrorHandler
>
friend
class
internal
::
arg_formatter_base
;
template
<
template
<
typename
>
class
Handler
,
typename
Spec
,
typename
Context
>
void
handle_dynamic_spec
(
Spec
&
value
,
arg_ref
<
typename
Context
::
char_type
>
ref
,
Context
&
ctx
,
const
typename
Context
::
char_type
*
format_str
)
{
typedef
typename
Context
::
char_type
char_type
;
switch
(
ref
.
kind
)
{
case
arg_ref
<
char_type
>
:
:
NONE
:
break
;
case
arg_ref
<
char_type
>
:
:
INDEX
:
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
arg
(
ref
.
val
.
index
),
ctx
.
error_handler
());
break
;
case
arg_ref
<
char_type
>
:
:
NAME
:
{
const
auto
arg_id
=
ref
.
val
.
name
.
to_view
(
format_str
);
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
arg
(
arg_id
),
ctx
.
error_handler
());
}
break
;
}
}
}
// namespace internal
public:
/** Constructs a ``basic_writer`` object. */
explicit
basic_writer
(
Range
out
,
internal
::
locale_ref
loc
=
internal
::
locale_ref
())
:
out_
(
out
.
begin
()),
locale_
(
loc
)
{}
template
<
typename
Range
>
using
basic_writer
FMT_DEPRECATED
=
internal
::
basic_writer
<
Range
>
;
using
writer
FMT_DEPRECATED
=
internal
::
writer
;
using
wwriter
FMT_DEPRECATED
=
internal
::
basic_writer
<
internal
::
buffer_range
<
wchar_t
>>
;
iterator
out
()
const
{
return
out_
;
}
/** The default argument formatter. */
template
<
typename
Range
>
class
arg_formatter
:
public
internal
::
arg_formatter_base
<
Range
>
{
private:
typedef
typename
Range
::
value_type
char_type
;
typedef
internal
::
arg_formatter_base
<
Range
>
base
;
typedef
basic_format_context
<
typename
base
::
iterator
,
char_type
>
context_type
;
void
write
(
int
value
)
{
write_decimal
(
value
);
}
void
write
(
long
value
)
{
write_decimal
(
value
);
}
void
write
(
long
long
value
)
{
write_decimal
(
value
);
}
context_type
&
ctx_
;
basic_parse_context
<
char_type
>*
parse_ctx_
;
void
write
(
unsigned
value
)
{
write_decimal
(
value
);
}
void
write
(
unsigned
long
value
)
{
write_decimal
(
value
);
}
void
write
(
unsigned
long
long
value
)
{
write_decimal
(
value
);
}
public:
typedef
Range
range
;
typedef
typename
base
::
iterator
iterator
;
typedef
typename
base
::
format_specs
format_specs
;
/**
\rst
Formats *value* and writes it to the buffer.
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
*spec* contains format specifier information for standard argument types.
\endrst
*/
template
<
typename
T
,
typename
FormatSpec
,
typename
...
FormatSpecs
,
FMT_ENABLE_IF
(
std
::
is_integral
<
T
>
::
value
)
>
void
write
(
T
value
,
FormatSpec
spec
,
FormatSpecs
...
specs
)
{
format_specs
s
(
spec
,
specs
...);
s
.
align_
=
ALIGN_RIGHT
;
write_int
(
value
,
s
);
}
explicit
arg_formatter
(
context_type
&
ctx
,
basic_parse_context
<
char_type
>*
parse_ctx
=
nullptr
,
format_specs
*
spec
=
nullptr
)
:
base
(
Range
(
ctx
.
out
()),
spec
,
ctx
.
locale
()),
ctx_
(
ctx
),
parse_ctx_
(
parse_ctx
)
{}
void
write
(
double
value
,
const
format_specs
&
spec
=
format_specs
())
{
write_double
(
value
,
spec
);
}
FMT_DEPRECATED
arg_formatter
(
context_type
&
ctx
,
format_specs
&
spec
)
:
base
(
Range
(
ctx
.
out
()),
&
spec
),
ctx_
(
ctx
)
{}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void
write
(
long
double
value
,
const
format_specs
&
spec
=
format_specs
())
{
write_double
(
value
,
spec
);
}
using
base
::
operator
();
/**
Writes a character to the buffer
. */
void
write
(
char
valu
e
)
{
auto
&&
it
=
reserve
(
1
);
*
it
++
=
value
;
/**
Formats an argument of a user-defined type
. */
iterator
operator
()(
typename
basic_format_arg
<
context_type
>::
handle
handl
e
)
{
handle
.
format
(
*
parse_ctx_
,
ctx_
);
return
this
->
out
()
;
}
};
template
<
typename
Char
,
FMT_ENABLE_IF
(
std
::
is_same
<
Char
,
char_type
>
::
value
)
>
void
write
(
Char
value
)
{
auto
&&
it
=
reserve
(
1
);
*
it
++
=
value
;
}
/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
class
FMT_API
system_error
:
public
std
::
runtime_error
{
private:
FMT_API
void
init
(
int
err_code
,
string_view
format_str
,
format_args
args
);
protected:
int
error_code_
;
system_error
()
:
std
::
runtime_error
(
""
)
{}
public:
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void
write
(
string_view
value
)
{
auto
&&
it
=
reserve
(
value
.
size
());
it
=
internal
::
copy_str
<
char_type
>
(
value
.
begin
(),
value
.
end
(),
it
);
}
void
write
(
wstring_view
value
)
{
static_assert
(
std
::
is_same
<
char_type
,
wchar_t
>::
value
,
""
);
auto
&&
it
=
reserve
(
value
.
size
());
it
=
std
::
copy
(
value
.
begin
(),
value
.
end
(),
it
);
}
\rst
Constructs a :class:`fmt::system_error` object with a description
formatted with `fmt::format_system_error`. *message* and additional
arguments passed into the constructor are formatted similarly to
`fmt::format`.
// Writes a formatted string.
template
<
typename
Char
>
void
write
(
const
Char
*
s
,
std
::
size_t
size
,
const
align_spec
&
spec
)
{
write_padded
(
spec
,
str_writer
<
Char
>
{
s
,
size
});
}
**Example**::
template
<
typename
Char
>
void
write
(
basic_string_view
<
Char
>
s
,
const
format_specs
&
spec
=
format_specs
())
{
const
Char
*
data
=
s
.
data
();
std
::
size_t
size
=
s
.
size
();
if
(
spec
.
precision
>=
0
&&
internal
::
to_unsigned
(
spec
.
precision
)
<
size
)
size
=
internal
::
to_unsigned
(
spec
.
precision
);
write
(
data
,
size
,
spec
);
// This throws a system_error with the description
// cannot open file 'madeup': No such file or directory
// or similar (system message may vary).
const char *filename = "madeup";
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template
<
typename
...
Args
>
system_error
(
int
error_code
,
string_view
message
,
const
Args
&
...
args
)
:
std
::
runtime_error
(
""
)
{
init
(
error_code
,
message
,
make_format_args
(
args
...));
}
~
system_error
()
FMT_NOEXCEPT
;
template
<
typename
UIntPtr
>
void
write_pointer
(
UIntPtr
value
,
const
align_spec
*
spec
)
{
int
num_digits
=
internal
::
count_digits
<
4
>
(
value
);
auto
pw
=
pointer_writer
<
UIntPtr
>
{
value
,
num_digits
};
if
(
!
spec
)
return
pw
(
reserve
(
num_digits
+
2
));
align_spec
as
=
*
spec
;
if
(
as
.
align
()
==
ALIGN_DEFAULT
)
as
.
align_
=
ALIGN_RIGHT
;
write_padded
(
as
,
pw
);
}
int
error_code
()
const
{
return
error_code_
;
}
};
/**
\rst
Formats an error returned by an operating system or a language runtime,
for example a file opening error, and writes it to *out* in the following
form:
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the passed message and *<system-message>* is
the system message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
FMT_API
void
format_system_error
(
internal
::
buffer
<
char
>&
out
,
int
error_code
,
fmt
::
string_view
message
)
FMT_NOEXCEPT
;
struct
float_spec_handler
{
char
type
;
bool
upper
;
...
...
@@ -2726,7 +2724,8 @@ struct float_spec_handler {
template
<
typename
Range
>
template
<
typename
T
>
void
basic_writer
<
Range
>::
write_double
(
T
value
,
const
format_specs
&
spec
)
{
void
internal
::
basic_writer
<
Range
>::
write_double
(
T
value
,
const
format_specs
&
spec
)
{
// Check type.
float_spec_handler
handler
(
static_cast
<
char
>
(
spec
.
type
));
internal
::
handle_float_type_spec
(
handler
.
type
,
handler
);
...
...
test/format-test.cc
View file @
ded0a3bb
...
...
@@ -34,7 +34,7 @@
using
std
::
size_t
;
using
fmt
::
basic_memory_buffer
;
using
fmt
::
basic_writer
;
using
fmt
::
internal
::
basic_writer
;
using
fmt
::
format
;
using
fmt
::
format_error
;
using
fmt
::
memory_buffer
;
...
...
@@ -100,7 +100,7 @@ template <typename Char, typename T>
::
testing
::
AssertionResult
check_write
(
const
T
&
value
,
const
char
*
type
)
{
fmt
::
basic_memory_buffer
<
Char
>
buffer
;
using
range
=
fmt
::
internal
::
buffer_range
<
Char
>
;
fmt
::
basic_writer
<
range
>
writer
(
buffer
);
basic_writer
<
range
>
writer
(
buffer
);
writer
.
write
(
value
);
std
::
basic_string
<
Char
>
actual
=
to_string
(
buffer
);
std
::
basic_string
<
Char
>
expected
;
...
...
@@ -582,7 +582,7 @@ TEST(StringViewTest, Ctor) {
TEST
(
WriterTest
,
Data
)
{
memory_buffer
buf
;
fmt
::
writer
w
(
buf
);
fmt
::
internal
::
writer
w
(
buf
);
w
.
write
(
42
);
EXPECT_EQ
(
"42"
,
to_string
(
buf
));
}
...
...
@@ -649,13 +649,13 @@ TEST(WriterTest, WriteLongDouble) {
TEST
(
WriterTest
,
WriteDoubleAtBufferBoundary
)
{
memory_buffer
buf
;
fmt
::
writer
writer
(
buf
);
fmt
::
internal
::
writer
writer
(
buf
);
for
(
int
i
=
0
;
i
<
100
;
++
i
)
writer
.
write
(
1.23456789
);
}
TEST
(
WriterTest
,
WriteDoubleWithFilledBuffer
)
{
memory_buffer
buf
;
fmt
::
writer
writer
(
buf
);
fmt
::
internal
::
writer
writer
(
buf
);
// Fill the buffer.
for
(
int
i
=
0
;
i
<
fmt
::
inline_buffer_size
;
++
i
)
writer
.
write
(
' '
);
writer
.
write
(
1.2
);
...
...
@@ -671,14 +671,10 @@ TEST(WriterTest, WriteWideChar) { CHECK_WRITE_WCHAR(L'a'); }
TEST
(
WriterTest
,
WriteString
)
{
CHECK_WRITE_CHAR
(
"abc"
);
CHECK_WRITE_WCHAR
(
"abc"
);
// The following line shouldn't compile:
// std::declval<fmt::basic_writer<fmt::buffer>>().write(L"abc");
}
TEST
(
WriterTest
,
WriteWideString
)
{
CHECK_WRITE_WCHAR
(
L"abc"
);
// The following line shouldn't compile:
// std::declval<fmt::basic_writer<fmt::wbuffer>>().write("abc");
}
TEST
(
FormatToTest
,
FormatWithoutArgs
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment