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
246bdafc
Commit
246bdafc
authored
Nov 11, 2017
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add FMT_STRING macro for compile-time strings
parent
e8055433
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1418 additions
and
1403 deletions
+1418
-1403
include/fmt/format.h
include/fmt/format.h
+1417
-1403
test/format-test.cc
test/format-test.cc
+1
-0
No files found.
include/fmt/format.h
View file @
246bdafc
...
...
@@ -2027,1636 +2027,1699 @@ class context_base : public parse_context<Char>{
public:
parse_context
<
Char
>
&
get_parse_context
()
{
return
*
this
;
}
};
}
// namespace internal
/** The default argument formatter. */
struct
format_string
{};
template
<
typename
Char
>
c
lass
arg_formatter
:
public
internal
::
arg_formatter_base
<
Char
>
{
private:
basic_context
<
Char
>
&
ctx_
;
c
onstexpr
bool
is_name_start
(
Char
c
)
{
return
(
'a'
<=
c
&&
c
<=
'z'
)
||
(
'A'
<=
c
&&
c
<=
'Z'
)
||
'_'
==
c
;
}
typedef
internal
::
arg_formatter_base
<
Char
>
Base
;
// Parses the input as an unsigned integer. This function assumes that the
// first character is a digit and presence of a non-digit character at the end.
// it: an iterator pointing to the beginning of the input range.
template
<
typename
Iterator
,
typename
ErrorHandler
>
constexpr
unsigned
parse_nonnegative_int
(
Iterator
&
it
,
ErrorHandler
&
handler
)
{
assert
(
'0'
<=
*
it
&&
*
it
<=
'9'
);
unsigned
value
=
0
;
do
{
unsigned
new_value
=
value
*
10
+
(
*
it
-
'0'
);
// Workaround for MSVC "setup_exception stack overflow" error:
auto
next
=
it
;
++
next
;
it
=
next
;
// Check if value wrapped around.
if
(
new_value
<
value
)
{
value
=
(
std
::
numeric_limits
<
unsigned
>::
max
)();
break
;
}
value
=
new_value
;
}
while
(
'0'
<=
*
it
&&
*
it
<=
'9'
);
// Convert to unsigned to prevent a warning.
unsigned
max_int
=
(
std
::
numeric_limits
<
int
>::
max
)();
if
(
value
>
max_int
)
handler
.
on_error
(
"number is too big"
);
return
value
;
}
template
<
typename
Char
,
typename
Context
>
class
custom_formatter
{
private:
basic_buffer
<
Char
>
&
buffer_
;
Context
&
ctx_
;
public:
typedef
typename
Base
::
format_specs
format_specs
;
custom_formatter
(
basic_buffer
<
Char
>
&
buffer
,
Context
&
ctx
)
:
buffer_
(
buffer
),
ctx_
(
ctx
)
{}
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the buffer to be used for output,
*ctx* is a reference to the formatting context, *spec* contains
format specifier information for standard argument types.
\endrst
*/
arg_formatter
(
basic_buffer
<
Char
>
&
buffer
,
basic_context
<
Char
>
&
ctx
,
format_specs
&
spec
)
:
internal
::
arg_formatter_base
<
Char
>
(
buffer
,
spec
),
ctx_
(
ctx
)
{}
bool
operator
()(
internal
::
custom_value
<
Char
>
custom
)
{
custom
.
format
(
buffer_
,
custom
.
value
,
&
ctx_
);
return
true
;
}
using
internal
::
arg_formatter_base
<
Char
>::
operator
();
template
<
typename
T
>
bool
operator
()(
T
)
{
return
false
;
}
};
/** Formats an argument of a custom (user-defined) type. */
void
operator
()(
internal
::
custom_value
<
Char
>
c
)
{
c
.
format
(
this
->
writer
().
buffer
(),
c
.
value
,
&
ctx_
);
}
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
Char
>
class
basic_context
:
public
internal
::
context_base
<
Char
,
basic_context
<
Char
>>
{
template
<
typename
ErrorHandler
>
class
width_checker
{
public:
/** The character type for the output. */
using
char_type
=
Char
;
explicit
constexpr
width_checker
(
ErrorHandler
&
eh
)
:
handler_
(
eh
)
{}
template
<
typename
T
>
using
formatter_type
=
formatter
<
T
,
Char
>
;
private:
internal
::
arg_map
<
basic_context
<
Char
>>
map_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
basic_context
);
constexpr
typename
std
::
enable_if
<
is_integer
<
T
>::
value
,
unsigned
long
long
>::
type
operator
()(
T
value
)
{
if
(
is_negative
(
value
))
handler_
.
on_error
(
"negative width"
)
;
return
value
;
}
typedef
internal
::
context_base
<
Char
,
basic_context
<
Char
>>
Base
;
template
<
typename
T
>
constexpr
typename
std
::
enable_if
<
!
is_integer
<
T
>::
value
,
unsigned
long
long
>::
type
operator
()(
T
)
{
handler_
.
on_error
(
"width is not integer"
);
return
0
;
}
typedef
typename
Base
::
format_arg
format_arg
;
using
Base
::
get_arg
;
private:
ErrorHandler
&
handler_
;
};
template
<
typename
ErrorHandler
>
class
precision_checker
{
public:
/**
\rst
Constructs a ``basic_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
\endrst
*/
basic_context
(
basic_string_view
<
Char
>
format_str
,
basic_args
<
basic_context
>
args
)
:
Base
(
format_str
,
args
)
{}
explicit
constexpr
precision_checker
(
ErrorHandler
&
eh
)
:
handler_
(
eh
)
{}
format_arg
next_arg
()
{
const
char
*
error
=
0
;
format_arg
arg
=
this
->
do_get_arg
(
this
->
next_arg_index
(
error
),
error
);
if
(
error
)
FMT_THROW
(
format_error
(
error
)
);
return
arg
;
template
<
typename
T
>
constexpr
typename
std
::
enable_if
<
is_integer
<
T
>::
value
,
unsigned
long
long
>::
type
operator
()(
T
value
)
{
if
(
is_negative
(
value
)
)
handler_
.
on_error
(
"negative precision"
);
return
value
;
}
format_arg
get_arg
(
unsigned
arg_index
)
{
const
char
*
error
=
0
;
format_arg
arg
=
this
->
do_get_arg
(
arg_index
,
error
);
if
(
error
)
FMT_THROW
(
format_error
(
error
));
return
arg
;
template
<
typename
T
>
constexpr
typename
std
::
enable_if
<
!
is_integer
<
T
>::
value
,
unsigned
long
long
>::
type
operator
()(
T
)
{
handler_
.
on_error
(
"precision is not integer"
);
return
0
;
}
// Checks if manual indexing is used and returns the argument with
// specified name.
format_arg
get_arg
(
basic_string_view
<
Char
>
name
);
};
/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
class
system_error
:
public
std
::
runtime_error
{
private:
void
init
(
int
err_code
,
string_view
format_str
,
args
args
);
protected:
int
error_code_
;
system_error
()
:
std
::
runtime_error
(
""
)
{}
ErrorHandler
&
handler_
;
};
// A format specifier handler that sets fields in basic_format_specs.
template
<
typename
Char
>
class
specs_setter
:
public
error_handler
{
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`.
explicit
constexpr
specs_setter
(
basic_format_specs
<
Char
>
&
specs
)
:
specs_
(
specs
)
{}
**Example**::
constexpr
void
on_align
(
alignment
align
)
{
specs_
.
align_
=
align
;
}
constexpr
void
on_fill
(
Char
fill
)
{
specs_
.
fill_
=
fill
;
}
constexpr
void
on_plus
()
{
specs_
.
flags_
|=
SIGN_FLAG
|
PLUS_FLAG
;
}
constexpr
void
on_minus
()
{
specs_
.
flags_
|=
MINUS_FLAG
;
}
constexpr
void
on_space
()
{
specs_
.
flags_
|=
SIGN_FLAG
;
}
constexpr
void
on_hash
()
{
specs_
.
flags_
|=
HASH_FLAG
;
}
// 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_args
(
args
...));
constexpr
void
on_zero
()
{
specs_
.
align_
=
ALIGN_NUMERIC
;
specs_
.
fill_
=
'0'
;
}
~
system_error
()
throw
();
constexpr
void
on_width
(
unsigned
width
)
{
specs_
.
width_
=
width
;
}
constexpr
void
on_precision
(
unsigned
precision
)
{
specs_
.
precision_
=
precision
;
}
constexpr
void
end_precision
()
{}
int
error_code
()
const
{
return
error_code_
;
}
constexpr
void
on_type
(
Char
type
)
{
specs_
.
type_
=
type
;
}
protected:
basic_format_specs
<
Char
>
&
specs_
;
};
/**
\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:
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template
<
typename
Handler
>
class
specs_checker
:
public
Handler
{
public:
constexpr
specs_checker
(
const
Handler
&
handler
,
internal
::
type
arg_type
)
:
Handler
(
handler
),
arg_type_
(
arg_type
)
{}
.. parsed-literal::
*<message>*: *<system-message>*
constexpr
void
on_align
(
alignment
align
)
{
if
(
align
==
ALIGN_NUMERIC
)
require_numeric_argument
(
'='
);
Handler
::
on_align
(
align
);
}
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
(
fmt
::
buffer
&
out
,
int
error_code
,
fmt
::
string_view
message
)
FMT_NOEXCEPT
;
constexpr
void
on_plus
()
{
check_sign
(
'+'
);
Handler
::
on_plus
();
}
/**
\rst
This template provides operations for formatting and writing data into a
character buffer. The output buffer is specified by a subclass such as
:class:`fmt::BasicMemoryWriter`.
constexpr
void
on_minus
()
{
check_sign
(
'-'
);
Handler
::
on_minus
();
}
You can use one of the following typedefs for common character types:
constexpr
void
on_space
()
{
check_sign
(
' '
);
Handler
::
on_space
();
}
+---------+-----------------------+
| Type | Definition |
+=========+=======================+
| writer | basic_writer<char> |
+---------+-----------------------+
| wwriter | basic_writer<wchar_t> |
+---------+-----------------------+
\endrst
*/
template
<
typename
Char
>
class
basic_writer
{
public:
typedef
basic_format_specs
<
Char
>
format_specs
;
private:
// Output buffer.
basic_buffer
<
Char
>
&
buffer_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
basic_writer
);
#if FMT_SECURE_SCL
typedef
stdext
::
checked_array_iterator
<
Char
*>
pointer_type
;
// Returns pointer value.
static
Char
*
get
(
pointer_type
p
)
{
return
p
.
base
();
}
#else
typedef
Char
*
pointer_type
;
static
Char
*
get
(
Char
*
p
)
{
return
p
;
}
#endif
constexpr
void
on_hash
()
{
require_numeric_argument
(
'#'
);
Handler
::
on_hash
();
}
// Fills the padding around the content and returns the pointer to the
// content area.
static
pointer_type
fill_padding
(
pointer_type
buffer
,
unsigned
total_size
,
std
::
size_t
content_size
,
wchar_t
fill
);
constexpr
void
on_zero
()
{
require_numeric_argument
(
'0'
);
Handler
::
on_zero
();
}
// Grows the buffer by n characters and returns a pointer to the newly
// allocated area.
pointer_type
grow_buffer
(
std
::
size_t
n
)
{
std
::
size_t
size
=
buffer_
.
size
();
buffer_
.
resize
(
size
+
n
);
return
internal
::
make_ptr
(
&
buffer_
[
size
],
n
);
constexpr
void
end_precision
()
{
if
(
is_integral
(
arg_type_
)
||
arg_type_
==
POINTER
)
{
report_error
(
"precision not allowed in {} format specifier"
,
arg_type_
==
POINTER
?
"pointer"
:
"integer"
);
}
}
// Writes an unsigned decimal integer.
template
<
typename
UInt
>
Char
*
write_unsigned_decimal
(
UInt
value
,
unsigned
prefix_size
=
0
)
{
unsigned
num_digits
=
internal
::
count_digits
(
value
);
Char
*
ptr
=
get
(
grow_buffer
(
prefix_size
+
num_digits
));
internal
::
format_decimal
(
ptr
+
prefix_size
,
value
,
num_digits
);
return
ptr
;
private:
template
<
typename
...
Args
>
void
report_error
(
string_view
format_str
,
const
Args
&
...
args
)
{
this
->
on_error
(
format
(
format_str
,
args
...).
c_str
());
}
// 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
);
if
(
internal
::
is_negative
(
value
))
{
abs_value
=
0
-
abs_value
;
*
write_unsigned_decimal
(
abs_value
,
1
)
=
'-'
;
}
else
{
write_unsigned_decimal
(
abs_value
,
0
);
template
<
typename
Char
>
constexpr
void
require_numeric_argument
(
Char
spec
)
{
if
(
!
is_numeric
(
arg_type_
))
{
report_error
(
"format specifier '{}' requires numeric argument"
,
static_cast
<
char
>
(
spec
));
}
}
// Prepare a buffer for integer formatting.
pointer_type
prepare_int_buffer
(
unsigned
num_digits
,
const
empty_spec
&
,
const
char
*
prefix
,
unsigned
prefix_size
)
{
unsigned
size
=
prefix_size
+
num_digits
;
pointer_type
p
=
grow_buffer
(
size
);
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
return
p
+
size
-
1
;
template
<
typename
Char
>
constexpr
void
check_sign
(
Char
sign
)
{
require_numeric_argument
(
sign
);
if
(
is_integral
(
arg_type_
)
&&
arg_type_
!=
INT
&&
arg_type_
!=
LONG_LONG
&&
arg_type_
!=
CHAR
)
{
report_error
(
"format specifier '{}' requires signed argument"
,
static_cast
<
char
>
(
sign
));
}
}
template
<
typename
Spec
>
pointer_type
prepare_int_buffer
(
unsigned
num_digits
,
const
Spec
&
spec
,
const
char
*
prefix
,
unsigned
prefix_size
);
internal
::
type
arg_type_
;
};
// Writes a formatted integer.
template
<
typename
T
,
typename
Spec
>
void
write_int
(
T
value
,
const
Spec
&
spec
);
template
<
template
<
typename
>
class
Handler
,
typename
T
,
typename
Context
>
constexpr
void
set_dynamic_spec
(
T
&
value
,
basic_arg
<
Context
>
arg
)
{
error_handler
eh
;
unsigned
long
long
big_value
=
visit
(
Handler
<
error_handler
>
(
eh
),
arg
);
if
(
big_value
>
(
std
::
numeric_limits
<
int
>::
max
)())
eh
.
on_error
(
"number is too big"
);
value
=
static_cast
<
int
>
(
big_value
);
}
// Formats a floating-point number (double or long double).
template
<
typename
T
>
void
write_double
(
T
value
,
const
format_specs
&
spec
);
struct
auto_id
{};
// Writes a formatted string.
template
<
typename
StrChar
>
pointer_type
write_str
(
const
StrChar
*
s
,
std
::
size_t
size
,
const
align_spec
&
spec
);
// The standard format specifier handler with checking.
template
<
typename
Context
>
class
specs_handler
:
public
specs_setter
<
typename
Context
::
char_type
>
{
public:
typedef
typename
Context
::
char_type
char_type
;
template
<
typename
StrChar
>
void
write_str
(
basic_string_view
<
StrChar
>
str
,
const
format_specs
&
spec
);
constexpr
specs_handler
(
basic_format_specs
<
char_type
>
&
specs
,
Context
&
ctx
)
:
specs_setter
<
char_type
>
(
specs
),
context_
(
ctx
)
{}
// Appends floating-point length specifier to the format string.
// The second argument is only used for overload resolution.
void
append_float_length
(
Char
*&
format_ptr
,
long
double
)
{
*
format_ptr
++
=
'L'
;
template
<
typename
Id
>
constexpr
void
on_dynamic_width
(
Id
arg_id
)
{
set_dynamic_spec
<
width_checker
>
(
this
->
specs_
.
width_
,
get_arg
(
arg_id
));
}
template
<
typename
T
>
void
append_float_length
(
Char
*&
,
T
)
{}
template
<
typename
Id
>
constexpr
void
on_dynamic_precision
(
Id
arg_id
)
{
set_dynamic_spec
<
precision_checker
>
(
this
->
specs_
.
precision_
,
get_arg
(
arg_id
));
}
template
<
typename
Char_
>
friend
class
internal
::
arg_formatter_base
;
private:
constexpr
basic_arg
<
Context
>
get_arg
(
auto_id
)
{
return
context_
.
next_arg
();
}
public:
/**
Constructs a ``basic_writer`` object.
*/
explicit
basic_writer
(
basic_buffer
<
Char
>
&
b
)
:
buffer_
(
b
)
{
}
template
<
typename
Id
>
constexpr
basic_arg
<
Context
>
get_arg
(
Id
arg_id
)
{
context_
.
check_arg_id
(
arg_id
);
return
context_
.
get_arg
(
arg_id
);
}
/**
\rst
Destroys the ``basic_writer`` object.
\endrst
*/
virtual
~
basic_writer
()
{}
Context
&
context_
;
};
/**
Returns the total number of characters written.
*/
std
::
size_t
size
()
const
{
return
buffer_
.
size
();
}
// An argument reference.
template
<
typename
Char
>
struct
arg_ref
{
enum
Kind
{
NONE
,
INDEX
,
NAME
};
/**
Returns a pointer to the output buffer content. No terminating null
character is appended.
*/
const
Char
*
data
()
const
FMT_NOEXCEPT
{
return
&
buffer_
[
0
];
}
constexpr
arg_ref
()
:
kind
(
NONE
),
index
(
0
)
{}
constexpr
explicit
arg_ref
(
unsigned
index
)
:
kind
(
INDEX
),
index
(
index
)
{}
explicit
arg_ref
(
basic_string_view
<
Char
>
name
)
:
kind
(
NAME
),
name
(
name
)
{}
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const
Char
*
c_str
()
const
{
std
::
size_t
size
=
buffer_
.
size
();
buffer_
.
reserve
(
size
+
1
);
buffer_
[
size
]
=
'\0'
;
return
&
buffer_
[
0
];
constexpr
arg_ref
&
operator
=
(
unsigned
index
)
{
kind
=
INDEX
;
this
->
index
=
index
;
return
*
this
;
}
/**
\rst
Returns the content of the output buffer as an `std::string`.
\endrst
*/
std
::
basic_string
<
Char
>
str
()
const
{
return
std
::
basic_string
<
Char
>
(
&
buffer_
[
0
],
buffer_
.
size
());
}
Kind
kind
;
union
{
unsigned
index
;
basic_string_view
<
Char
>
name
;
};
};
void
write
(
int
value
)
{
write_decimal
(
value
);
}
void
write
(
long
value
)
{
write_decimal
(
value
);
}
void
write
(
long
long
value
)
{
write_decimal
(
value
);
}
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// differents 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
;
};
/**
\rst
Formats *value* and writes it to the buffer.
\endrst
*/
template
<
typename
T
,
typename
...
FormatSpecs
>
typename
std
::
enable_if
<
std
::
is_integral
<
T
>::
value
,
void
>::
type
write
(
T
value
,
FormatSpecs
...
specs
)
{
write_int
(
value
,
format_specs
(
specs
...));
}
// 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:
using
char_type
=
typename
ParseContext
::
char_type
;
void
write
(
double
value
)
{
write_double
(
value
,
format_specs
());
constexpr
dynamic_specs_handler
(
dynamic_format_specs
<
char_type
>
&
specs
,
ParseContext
&
ctx
)
:
specs_setter
<
char_type
>
(
specs
),
specs_
(
specs
),
context_
(
ctx
)
{}
template
<
typename
Id
>
constexpr
void
on_dynamic_width
(
Id
arg_id
)
{
specs_
.
width_ref
=
make_arg_ref
(
arg_id
);
}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void
write
(
long
double
value
)
{
write_double
(
value
,
format_specs
());
template
<
typename
Id
>
constexpr
void
on_dynamic_precision
(
Id
arg_id
)
{
specs_
.
precision_ref
=
make_arg_ref
(
arg_id
);
}
/**
Writes a character to the buffer.
*/
void
write
(
char
value
)
{
buffer_
.
push_back
(
value
);
private:
using
arg_ref_type
=
arg_ref
<
char_type
>
;
template
<
typename
Id
>
constexpr
arg_ref_type
make_arg_ref
(
Id
arg_id
)
{
context_
.
check_arg_id
(
arg_id
);
return
arg_ref_type
(
arg_id
);
}
void
write
(
wchar_t
value
)
{
internal
::
require_wchar
<
Char
>
();
buffer_
.
push_back
(
value
);
constexpr
arg_ref_type
make_arg_ref
(
auto_id
)
{
const
char
*
error
=
0
;
auto
index
=
context_
.
next_arg_index
(
error
);
if
(
error
)
FMT_THROW
(
format_error
(
error
));
return
arg_ref_type
(
index
);
}
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void
write
(
string_view
value
)
{
const
char
*
str
=
value
.
data
();
buffer_
.
append
(
str
,
str
+
value
.
size
());
dynamic_format_specs
<
char_type
>
&
specs_
;
ParseContext
&
context_
;
};
template
<
typename
Iterator
,
typename
IDHandler
>
constexpr
Iterator
parse_arg_id
(
Iterator
it
,
IDHandler
&&
handler
)
{
using
char_type
=
typename
std
::
iterator_traits
<
Iterator
>::
value_type
;
char_type
c
=
*
it
;
if
(
c
==
'}'
||
c
==
':'
)
{
handler
();
return
it
;
}
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
unsigned
index
=
parse_nonnegative_int
(
it
,
handler
);
if
(
*
it
!=
'}'
&&
*
it
!=
':'
)
{
handler
.
on_error
(
"invalid format string"
);
return
it
;
}
handler
(
index
);
return
it
;
}
if
(
!
is_name_start
(
c
))
{
handler
.
on_error
(
"invalid format string"
);
return
it
;
}
auto
start
=
it
;
do
{
c
=
*++
it
;
}
while
(
is_name_start
(
c
)
||
(
'0'
<=
c
&&
c
<=
'9'
));
handler
(
basic_string_view
<
char_type
>
(
pointer_from
(
start
),
it
-
start
));
return
it
;
}
void
write
(
basic_string_view
<
wchar_t
>
value
)
{
internal
::
require_wchar
<
Char
>
();
const
wchar_t
*
str
=
value
.
data
();
buffer_
.
append
(
str
,
str
+
value
.
size
());
// Adapts SpecHandler to IDHandler API for dynamic width.
template
<
typename
SpecHandler
,
typename
Char
>
struct
width_adapter
{
explicit
constexpr
width_adapter
(
SpecHandler
&
h
)
:
handler
(
h
)
{}
constexpr
void
operator
()()
{
handler
.
on_dynamic_width
(
auto_id
());
}
constexpr
void
operator
()(
unsigned
id
)
{
handler
.
on_dynamic_width
(
id
);
}
constexpr
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_dynamic_width
(
id
);
}
template
<
typename
...
FormatSpecs
>
void
write
(
basic_string_view
<
Char
>
str
,
FormatSpecs
...
specs
)
{
write_str
(
str
,
format_specs
(
specs
...));
constexpr
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
}
SpecHandler
&
handler
;
};
// Adapts SpecHandler to IDHandler API for dynamic precision.
template
<
typename
SpecHandler
,
typename
Char
>
struct
precision_adapter
{
explicit
constexpr
precision_adapter
(
SpecHandler
&
h
)
:
handler
(
h
)
{}
constexpr
void
operator
()()
{
handler
.
on_dynamic_precision
(
auto_id
());
}
constexpr
void
operator
()(
unsigned
id
)
{
handler
.
on_dynamic_precision
(
id
);
}
constexpr
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_dynamic_precision
(
id
);
}
void
clear
()
FMT_NOEXCEPT
{
buffer_
.
resize
(
0
);
}
constexpr
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
}
basic_buffer
<
Char
>
&
buffer
()
FMT_NOEXCEPT
{
return
buffer_
;
}
SpecHandler
&
handler
;
};
template
<
typename
Char
>
template
<
typename
StrChar
>
typename
basic_writer
<
Char
>::
pointer_type
basic_writer
<
Char
>::
write_str
(
const
StrChar
*
s
,
std
::
size_t
size
,
const
align_spec
&
spec
)
{
pointer_type
out
=
pointer_type
();
if
(
spec
.
width
()
>
size
)
{
out
=
grow_buffer
(
spec
.
width
());
Char
fill
=
internal
::
char_traits
<
Char
>::
cast
(
spec
.
fill
());
if
(
spec
.
align
()
==
ALIGN_RIGHT
)
{
std
::
uninitialized_fill_n
(
out
,
spec
.
width
()
-
size
,
fill
);
out
+=
spec
.
width
()
-
size
;
}
else
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
out
=
fill_padding
(
out
,
spec
.
width
(),
size
,
fill
);
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
// it: an iterator pointing to the beginning of a null-terminated range of
// characters, possibly emulated via null_terminating_iterator, representing
// format specifiers.
template
<
typename
Iterator
,
typename
SpecHandler
>
constexpr
Iterator
parse_format_specs
(
Iterator
it
,
SpecHandler
&&
handler
)
{
using
char_type
=
typename
std
::
iterator_traits
<
Iterator
>::
value_type
;
// Parse fill and alignment.
if
(
char_type
c
=
*
it
)
{
alignment
align
=
ALIGN_DEFAULT
;
int
i
=
1
;
do
{
auto
p
=
it
+
i
;
switch
(
*
p
)
{
case
'<'
:
align
=
ALIGN_LEFT
;
break
;
case
'>'
:
align
=
ALIGN_RIGHT
;
break
;
case
'='
:
align
=
ALIGN_NUMERIC
;
break
;
case
'^'
:
align
=
ALIGN_CENTER
;
break
;
}
if
(
align
!=
ALIGN_DEFAULT
)
{
handler
.
on_align
(
align
);
if
(
p
!=
it
)
{
if
(
c
==
'}'
)
break
;
if
(
c
==
'{'
)
{
handler
.
on_error
(
"invalid fill character '{'"
);
return
it
;
}
it
+=
2
;
handler
.
on_fill
(
c
);
}
else
++
it
;
break
;
}
}
while
(
--
i
>=
0
);
}
// Parse sign.
switch
(
*
it
)
{
case
'+'
:
handler
.
on_plus
();
++
it
;
break
;
case
'-'
:
handler
.
on_minus
();
++
it
;
break
;
case
' '
:
handler
.
on_space
();
++
it
;
break
;
}
if
(
*
it
==
'#'
)
{
handler
.
on_hash
();
++
it
;
}
// Parse zero flag.
if
(
*
it
==
'0'
)
{
handler
.
on_zero
();
++
it
;
}
// Parse width.
if
(
'0'
<=
*
it
&&
*
it
<=
'9'
)
{
handler
.
on_width
(
parse_nonnegative_int
(
it
,
handler
));
}
else
if
(
*
it
==
'{'
)
{
it
=
parse_arg_id
(
it
+
1
,
width_adapter
<
SpecHandler
,
char_type
>
(
handler
));
if
(
*
it
++
!=
'}'
)
{
handler
.
on_error
(
"invalid format string"
);
return
it
;
}
}
// Parse precision.
if
(
*
it
==
'.'
)
{
++
it
;
if
(
'0'
<=
*
it
&&
*
it
<=
'9'
)
{
handler
.
on_precision
(
parse_nonnegative_int
(
it
,
handler
));
}
else
if
(
*
it
==
'{'
)
{
it
=
parse_arg_id
(
it
+
1
,
precision_adapter
<
SpecHandler
,
char_type
>
(
handler
));
if
(
*
it
++
!=
'}'
)
{
handler
.
on_error
(
"invalid format string"
);
return
it
;
}
}
else
{
std
::
uninitialized_fill_n
(
out
+
size
,
spec
.
width
()
-
size
,
fill
);
handler
.
on_error
(
"missing precision specifier"
);
return
it
;
}
}
else
{
out
=
grow_buffer
(
size
);
handler
.
end_precision
();
}
std
::
uninitialized_copy
(
s
,
s
+
size
,
out
);
return
out
;
}
template
<
typename
Char
>
template
<
typename
StrChar
>
void
basic_writer
<
Char
>::
write_str
(
basic_string_view
<
StrChar
>
s
,
const
format_specs
&
spec
)
{
// Check if StrChar is convertible to Char.
internal
::
char_traits
<
Char
>::
convert
(
StrChar
());
if
(
spec
.
type_
&&
spec
.
type_
!=
's'
)
internal
::
report_unknown_type
(
spec
.
type_
,
"string"
);
const
StrChar
*
str_value
=
s
.
data
();
std
::
size_t
str_size
=
s
.
size
();
if
(
str_size
==
0
&&
!
str_value
)
FMT_THROW
(
format_error
(
"string pointer is null"
));
std
::
size_t
precision
=
static_cast
<
std
::
size_t
>
(
spec
.
precision_
);
if
(
spec
.
precision_
>=
0
&&
precision
<
str_size
)
str_size
=
precision
;
write_str
(
str_value
,
str_size
,
spec
);
// Parse type.
if
(
*
it
!=
'}'
&&
*
it
)
handler
.
on_type
(
*
it
++
);
return
it
;
}
template
<
typename
Char
>
typename
basic_writer
<
Char
>::
pointer_type
basic_writer
<
Char
>::
fill_padding
(
pointer_type
buffer
,
unsigned
total_size
,
std
::
size_t
content_size
,
wchar_t
fill
)
{
std
::
size_t
padding
=
total_size
-
content_size
;
std
::
size_t
left_padding
=
padding
/
2
;
Char
fill_char
=
internal
::
char_traits
<
Char
>::
cast
(
fill
);
std
::
uninitialized_fill_n
(
buffer
,
left_padding
,
fill_char
);
buffer
+=
left_padding
;
pointer_type
content
=
buffer
;
std
::
uninitialized_fill_n
(
buffer
+
content_size
,
padding
-
left_padding
,
fill_char
);
return
content
;
}
template
<
typename
Handler
,
typename
Char
>
struct
id_adapter
{
constexpr
explicit
id_adapter
(
Handler
&
h
)
:
handler
(
h
)
{}
template
<
typename
Char
>
template
<
typename
Spec
>
typename
basic_writer
<
Char
>::
pointer_type
basic_writer
<
Char
>::
prepare_int_buffer
(
unsigned
num_digits
,
const
Spec
&
spec
,
const
char
*
prefix
,
unsigned
prefix_size
)
{
unsigned
width
=
spec
.
width
();
alignment
align
=
spec
.
align
();
Char
fill
=
internal
::
char_traits
<
Char
>::
cast
(
spec
.
fill
());
if
(
spec
.
precision
()
>
static_cast
<
int
>
(
num_digits
))
{
// Octal prefix '0' is counted as a digit, so ignore it if precision
// is specified.
if
(
prefix_size
>
0
&&
prefix
[
prefix_size
-
1
]
==
'0'
)
--
prefix_size
;
unsigned
number_size
=
prefix_size
+
internal
::
to_unsigned
(
spec
.
precision
());
align_spec
subspec
(
number_size
,
'0'
,
ALIGN_NUMERIC
);
if
(
number_size
>=
width
)
return
prepare_int_buffer
(
num_digits
,
subspec
,
prefix
,
prefix_size
);
buffer_
.
reserve
(
width
);
unsigned
fill_size
=
width
-
number_size
;
if
(
align
!=
ALIGN_LEFT
)
{
pointer_type
p
=
grow_buffer
(
fill_size
);
std
::
uninitialized_fill
(
p
,
p
+
fill_size
,
fill
);
}
pointer_type
result
=
prepare_int_buffer
(
num_digits
,
subspec
,
prefix
,
prefix_size
);
if
(
align
==
ALIGN_LEFT
)
{
pointer_type
p
=
grow_buffer
(
fill_size
);
std
::
uninitialized_fill
(
p
,
p
+
fill_size
,
fill
);
}
return
result
;
constexpr
void
operator
()()
{
handler
.
on_arg_id
();
}
constexpr
void
operator
()(
unsigned
id
)
{
handler
.
on_arg_id
(
id
);
}
constexpr
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_arg_id
(
id
);
}
unsigned
size
=
prefix_size
+
num_digits
;
if
(
width
<=
size
)
{
pointer_type
p
=
grow_buffer
(
size
);
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
return
p
+
size
-
1
;
constexpr
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
}
pointer_type
p
=
grow_buffer
(
width
);
pointer_type
end
=
p
+
width
;
if
(
align
==
ALIGN_LEFT
)
{
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
p
+=
size
;
std
::
uninitialized_fill
(
p
,
end
,
fill
);
}
else
if
(
align
==
ALIGN_CENTER
)
{
p
=
fill_padding
(
p
,
width
,
size
,
fill
);
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
p
+=
size
;
}
else
{
if
(
align
==
ALIGN_NUMERIC
)
{
if
(
prefix_size
!=
0
)
{
p
=
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
size
-=
prefix_size
;
}
Handler
&
handler
;
};
template
<
typename
Iterator
,
typename
Handler
>
constexpr
void
parse_format_string
(
Iterator
it
,
Handler
&&
handler
)
{
using
char_type
=
typename
std
::
iterator_traits
<
Iterator
>::
value_type
;
auto
start
=
it
;
while
(
*
it
)
{
char_type
ch
=
*
it
++
;
if
(
ch
!=
'{'
&&
ch
!=
'}'
)
continue
;
if
(
*
it
==
ch
)
{
handler
.
on_text
(
start
,
it
);
start
=
++
it
;
continue
;
}
if
(
ch
==
'}'
)
{
handler
.
on_error
(
"unmatched '}' in format string"
);
return
;
}
handler
.
on_text
(
start
,
it
-
1
);
it
=
parse_arg_id
(
it
,
id_adapter
<
Handler
,
char_type
>
(
handler
));
if
(
*
it
==
'}'
)
{
handler
.
on_replacement_field
(
it
);
}
else
if
(
*
it
==
':'
)
{
++
it
;
it
=
handler
.
on_format_specs
(
it
);
if
(
*
it
!=
'}'
)
handler
.
on_error
(
"unknown format specifier"
);
}
else
{
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
end
-
size
);
handler
.
on_error
(
"missing '}' in format string"
);
}
std
::
uninitialized_fill
(
p
,
end
-
size
,
fill
);
p
=
end
;
start
=
++
it
;
}
return
p
-
1
;
handler
.
on_text
(
start
,
it
)
;
}
template
<
typename
Char
>
template
<
typename
T
,
typename
Spec
>
void
basic_writer
<
Char
>::
write_int
(
T
value
,
const
Spec
&
spec
)
{
struct
spec_handler
{
basic_writer
<
Char
>
&
writer
;
const
Spec
&
spec
;
unsigned
prefix_size
=
0
;
typedef
typename
internal
::
int_traits
<
T
>::
main_type
UnsignedType
;
UnsignedType
abs_value
;
char
prefix
[
4
]
=
""
;
template
<
typename
Char
,
typename
T
>
constexpr
const
Char
*
parse_format_specs
(
parse_context
<
Char
>
&
ctx
)
{
formatter
<
T
,
Char
>
f
;
return
f
.
parse
(
ctx
);
}
spec_handler
(
basic_writer
<
Char
>
&
w
,
T
value
,
const
Spec
&
s
)
:
writer
(
w
),
abs_value
(
static_cast
<
UnsignedType
>
(
value
)),
spec
(
s
)
{
if
(
internal
::
is_negative
(
value
))
{
prefix
[
0
]
=
'-'
;
++
prefix_size
;
abs_value
=
0
-
abs_value
;
}
else
if
(
spec
.
flag
(
SIGN_FLAG
))
{
prefix
[
0
]
=
spec
.
flag
(
PLUS_FLAG
)
?
'+'
:
' '
;
++
prefix_size
;
}
}
template
<
typename
Char
,
typename
...
Args
>
struct
format_string_checker
{
public:
explicit
constexpr
format_string_checker
(
const
Char
*
end
)
:
end_
(
end
)
{}
void
on_dec
()
{
unsigned
num_digits
=
internal
::
count_digits
(
abs_value
);
pointer_type
p
=
writer
.
prepare_int_buffer
(
num_digits
,
spec
,
prefix
,
prefix_size
)
+
1
;
internal
::
format_decimal
(
get
(
p
),
abs_value
,
0
);
}
constexpr
void
on_text
(
const
Char
*
,
const
Char
*
)
{}
void
on_hex
()
{
UnsignedType
n
=
abs_value
;
if
(
spec
.
flag
(
HASH_FLAG
))
{
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
spec
.
type
();
}
unsigned
num_digits
=
0
;
do
{
++
num_digits
;
}
while
((
n
>>=
4
)
!=
0
);
Char
*
p
=
get
(
writer
.
prepare_int_buffer
(
num_digits
,
spec
,
prefix
,
prefix_size
));
n
=
abs_value
;
const
char
*
digits
=
spec
.
type
()
==
'x'
?
"0123456789abcdef"
:
"0123456789ABCDEF"
;
do
{
*
p
--
=
digits
[
n
&
0xf
];
}
while
((
n
>>=
4
)
!=
0
);
}
constexpr
void
on_arg_id
()
{
++
arg_index_
;
check_arg_index
();
}
constexpr
void
on_arg_id
(
unsigned
index
)
{
arg_index_
=
index
;
check_arg_index
();
}
constexpr
void
on_arg_id
(
basic_string_view
<
Char
>
)
{}
void
on_bin
()
{
UnsignedType
n
=
abs_value
;
if
(
spec
.
flag
(
HASH_FLAG
))
{
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
spec
.
type
();
}
unsigned
num_digits
=
0
;
do
{
++
num_digits
;
}
while
((
n
>>=
1
)
!=
0
);
Char
*
p
=
get
(
writer
.
prepare_int_buffer
(
num_digits
,
spec
,
prefix
,
prefix_size
));
n
=
abs_value
;
do
{
*
p
--
=
static_cast
<
Char
>
(
'0'
+
(
n
&
1
));
}
while
((
n
>>=
1
)
!=
0
);
}
constexpr
void
on_replacement_field
(
const
Char
*
)
{}
void
on_oct
()
{
UnsignedType
n
=
abs_value
;
if
(
spec
.
flag
(
HASH_FLAG
))
prefix
[
prefix_size
++
]
=
'0'
;
unsigned
num_digits
=
0
;
do
{
++
num_digits
;
}
while
((
n
>>=
3
)
!=
0
);
Char
*
p
=
get
(
writer
.
prepare_int_buffer
(
num_digits
,
spec
,
prefix
,
prefix_size
));
n
=
abs_value
;
do
{
*
p
--
=
static_cast
<
Char
>
(
'0'
+
(
n
&
7
));
}
while
((
n
>>=
3
)
!=
0
);
}
constexpr
const
Char
*
on_format_specs
(
const
Char
*
s
)
{
parse_context
<
Char
>
ctx
(
basic_string_view
<
Char
>
(
s
,
end_
-
s
));
return
parse_funcs_
[
arg_index_
](
ctx
);
}
void
on_num
()
{
unsigned
num_digits
=
internal
::
count_digits
(
abs_value
);
std
::
locale
loc
=
writer
.
buffer_
.
locale
();
Char
thousands_sep
=
std
::
use_facet
<
std
::
numpunct
<
Char
>>
(
loc
).
thousands_sep
();
fmt
::
basic_string_view
<
Char
>
sep
(
&
thousands_sep
,
1
);
unsigned
size
=
static_cast
<
unsigned
>
(
num_digits
+
sep
.
size
()
*
((
num_digits
-
1
)
/
3
));
pointer_type
p
=
writer
.
prepare_int_buffer
(
size
,
spec
,
prefix
,
prefix_size
)
+
1
;
internal
::
format_decimal
(
get
(
p
),
abs_value
,
0
,
internal
::
add_thousands_sep
<
Char
>
(
sep
));
}
// This function is intentionally not constexpr to give a compile-time error.
void
on_error
(
const
char
*
);
void
on_error
()
{
internal
::
report_unknown_type
(
spec
.
type
(),
spec
.
flag
(
CHAR_FLAG
)
?
"char"
:
"integer"
);
}
private:
constexpr
void
check_arg_index
()
{
if
(
arg_index_
<
0
||
arg_index_
>=
sizeof
...(
Args
))
on_error
(
"argument index out of range"
);
}
// Format specifier parsing function.
using
parse_func
=
const
Char
*
(
*
)(
parse_context
<
Char
>
&
);
const
Char
*
end_
;
int
arg_index_
=
-
1
;
parse_func
parse_funcs_
[
sizeof
...(
Args
)]
=
{
&
parse_format_specs
<
Char
,
Args
>
...
};
internal
::
handle_integral_type_spec
(
spec
.
type
(),
spec_handler
(
*
this
,
value
,
spec
));
};
template
<
typename
Char
,
typename
...
Args
>
constexpr
bool
check_format_string
(
basic_string_view
<
Char
>
s
)
{
format_string_checker
<
Char
,
Args
...
>
checker
(
s
.
end
());
internal
::
parse_format_string
(
s
.
begin
(),
checker
);
return
true
;
}
template
<
typename
Char
>
// Specifies whether to format T using the standard formatter.
// It is not possible to use get_type in formatter specialization directly
// because of a bug in MSVC.
template
<
typename
T
>
void
basic_writer
<
Char
>::
write_double
(
T
value
,
const
format_specs
&
spec
)
{
// Check type.
char
type
=
spec
.
type
();
bool
upper
=
false
;
switch
(
type
)
{
case
0
:
type
=
'g'
;
struct
format_type
:
std
::
integral_constant
<
bool
,
get_type
<
T
>
()
!=
CUSTOM
>
{};
// Specifies whether to format enums.
template
<
typename
T
,
typename
Enable
=
void
>
struct
format_enum
:
std
::
integral_constant
<
bool
,
std
::
is_enum
<
T
>::
value
>
{};
template
<
template
<
typename
>
class
Handler
,
typename
Spec
,
typename
Char
>
void
handle_dynamic_spec
(
Spec
&
value
,
arg_ref
<
Char
>
ref
,
basic_context
<
Char
>
&
ctx
)
{
switch
(
ref
.
kind
)
{
case
arg_ref
<
Char
>
:
:
NONE
:
break
;
case
'e'
:
case
'f'
:
case
'g'
:
case
'a'
:
case
arg_ref
<
Char
>
:
:
INDEX
:
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
get_arg
(
ref
.
index
));
break
;
case
'F'
:
#if FMT_MSC_VER
// MSVC's printf doesn't support 'F'.
type
=
'f'
;
#endif
// Fall through.
case
'E'
:
case
'G'
:
case
'A'
:
upper
=
true
;
break
;
default:
internal
::
report_unknown_type
(
type
,
"double"
);
case
arg_ref
<
Char
>
:
:
NAME
:
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
get_arg
(
ref
.
name
));
break
;
}
}
}
// namespace internal
char
sign
=
0
;
// Use isnegative instead of value < 0 because the latter is always
// false for NaN.
if
(
internal
::
fputil
::
isnegative
(
static_cast
<
double
>
(
value
)))
{
sign
=
'-'
;
value
=
-
value
;
}
else
if
(
spec
.
flag
(
SIGN_FLAG
))
{
sign
=
spec
.
flag
(
PLUS_FLAG
)
?
'+'
:
' '
;
}
if
(
internal
::
fputil
::
isnotanumber
(
value
))
{
// Format NaN ourselves because sprintf's output is not consistent
// across platforms.
std
::
size_t
nan_size
=
4
;
const
char
*
nan
=
upper
?
" NAN"
:
" nan"
;
if
(
!
sign
)
{
--
nan_size
;
++
nan
;
}
pointer_type
out
=
write_str
(
nan
,
nan_size
,
spec
);
if
(
sign
)
*
out
=
sign
;
return
;
}
/** The default argument formatter. */
template
<
typename
Char
>
class
arg_formatter
:
public
internal
::
arg_formatter_base
<
Char
>
{
private:
basic_context
<
Char
>
&
ctx_
;
if
(
internal
::
fputil
::
isinfinity
(
value
))
{
// Format infinity ourselves because sprintf's output is not consistent
// across platforms.
std
::
size_t
inf_size
=
4
;
const
char
*
inf
=
upper
?
" INF"
:
" inf"
;
if
(
!
sign
)
{
--
inf_size
;
++
inf
;
}
pointer_type
out
=
write_str
(
inf
,
inf_size
,
spec
);
if
(
sign
)
*
out
=
sign
;
return
;
}
typedef
internal
::
arg_formatter_base
<
Char
>
Base
;
std
::
size_t
offset
=
buffer_
.
size
();
unsigned
width
=
spec
.
width
();
if
(
sign
)
{
buffer_
.
reserve
(
buffer_
.
size
()
+
(
width
>
1u
?
width
:
1u
));
if
(
width
>
0
)
--
width
;
++
offset
;
}
public:
typedef
typename
Base
::
format_specs
format_specs
;
// Build format string.
enum
{
MAX_FORMAT_SIZE
=
10
};
// longest format: %#-*.*Lg
Char
format
[
MAX_FORMAT_SIZE
];
Char
*
format_ptr
=
format
;
*
format_ptr
++
=
'%'
;
unsigned
width_for_sprintf
=
width
;
if
(
spec
.
flag
(
HASH_FLAG
))
*
format_ptr
++
=
'#'
;
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
width_for_sprintf
=
0
;
}
else
{
if
(
spec
.
align
()
==
ALIGN_LEFT
)
*
format_ptr
++
=
'-'
;
if
(
width
!=
0
)
*
format_ptr
++
=
'*'
;
}
if
(
spec
.
precision
()
>=
0
)
{
*
format_ptr
++
=
'.'
;
*
format_ptr
++
=
'*'
;
}
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the buffer to be used for output,
*ctx* is a reference to the formatting context, *spec* contains
format specifier information for standard argument types.
\endrst
*/
arg_formatter
(
basic_buffer
<
Char
>
&
buffer
,
basic_context
<
Char
>
&
ctx
,
format_specs
&
spec
)
:
internal
::
arg_formatter_base
<
Char
>
(
buffer
,
spec
),
ctx_
(
ctx
)
{}
append_float_length
(
format_ptr
,
value
);
*
format_ptr
++
=
type
;
*
format_ptr
=
'\0'
;
using
internal
::
arg_formatter_base
<
Char
>::
operator
();
// Format using snprintf.
Char
fill
=
internal
::
char_traits
<
Char
>::
cast
(
spec
.
fill
());
unsigned
n
=
0
;
Char
*
start
=
0
;
for
(;;)
{
std
::
size_t
buffer_size
=
buffer_
.
capacity
()
-
offset
;
#if FMT_MSC_VER
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
// space for at least one extra character to make the size non-zero.
// Note that the buffer's capacity will increase by more than 1.
if
(
buffer_size
==
0
)
{
buffer_
.
reserve
(
offset
+
1
);
buffer_size
=
buffer_
.
capacity
()
-
offset
;
}
#endif
start
=
&
buffer_
[
offset
];
int
result
=
internal
::
char_traits
<
Char
>::
format_float
(
start
,
buffer_size
,
format
,
width_for_sprintf
,
spec
.
precision
(),
value
);
if
(
result
>=
0
)
{
n
=
internal
::
to_unsigned
(
result
);
if
(
offset
+
n
<
buffer_
.
capacity
())
break
;
// The buffer is large enough - continue with formatting.
buffer_
.
reserve
(
offset
+
n
+
1
);
}
else
{
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buffer_
.
reserve
(
buffer_
.
capacity
()
+
1
);
}
}
if
(
sign
)
{
if
((
spec
.
align
()
!=
ALIGN_RIGHT
&&
spec
.
align
()
!=
ALIGN_DEFAULT
)
||
*
start
!=
' '
)
{
*
(
start
-
1
)
=
sign
;
sign
=
0
;
}
else
{
*
(
start
-
1
)
=
fill
;
}
++
n
;
}
if
(
spec
.
align
()
==
ALIGN_CENTER
&&
spec
.
width
()
>
n
)
{
width
=
spec
.
width
();
pointer_type
p
=
grow_buffer
(
width
);
std
::
memmove
(
get
(
p
)
+
(
width
-
n
)
/
2
,
get
(
p
),
n
*
sizeof
(
Char
));
fill_padding
(
p
,
spec
.
width
(),
n
,
fill
);
return
;
}
if
(
spec
.
fill
()
!=
' '
||
sign
)
{
while
(
*
start
==
' '
)
*
start
++
=
fill
;
if
(
sign
)
*
(
start
-
1
)
=
sign
;
/** Formats an argument of a custom (user-defined) type. */
void
operator
()(
internal
::
custom_value
<
Char
>
c
)
{
c
.
format
(
this
->
writer
().
buffer
(),
c
.
value
,
&
ctx_
);
}
grow_buffer
(
n
);
}
};
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API
void
report_system_error
(
int
error_code
,
string_view
message
)
FMT_NOEXCEPT
;
template
<
typename
Char
>
class
basic_context
:
public
internal
::
context_base
<
Char
,
basic_context
<
Char
>>
{
public:
/** The character type for the output. */
using
char_type
=
Char
;
#if FMT_USE_WINDOWS_H
template
<
typename
T
>
using
formatter_type
=
formatter
<
T
,
Char
>
;
/** A Windows error. */
class
windows_error
:
public
system_error
{
private:
FMT_API
void
init
(
int
error_code
,
string_view
format_str
,
args
args
);
internal
::
arg_map
<
basic_context
<
Char
>>
map_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
basic_context
);
typedef
internal
::
context_base
<
Char
,
basic_context
<
Char
>>
Base
;
typedef
typename
Base
::
format_arg
format_arg
;
using
Base
::
get_arg
;
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
Constructs a ``basic_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
\endrst
*/
basic_context
(
basic_string_view
<
Char
>
format_str
,
basic_args
<
basic_context
>
args
)
:
Base
(
format_str
,
args
)
{}
.. parsed-literal::
*<message>*: *<system-message>*
format_arg
next_arg
()
{
const
char
*
error
=
0
;
format_arg
arg
=
this
->
do_get_arg
(
this
->
next_arg_index
(
error
),
error
);
if
(
error
)
FMT_THROW
(
format_error
(
error
));
return
arg
;
}
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
format_arg
get_arg
(
unsigned
arg_index
)
{
const
char
*
error
=
0
;
format_arg
arg
=
this
->
do_get_arg
(
arg_index
,
error
);
if
(
error
)
FMT_THROW
(
format_error
(
error
));
return
arg
;
}
// Checks if manual indexing is used and returns the argument with
// specified name.
format_arg
get_arg
(
basic_string_view
<
Char
>
name
);
};
/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
class
system_error
:
public
std
::
runtime_error
{
private:
void
init
(
int
err_code
,
string_view
format_str
,
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
windows
_error with the description
// cannot open file 'madeup':
The system cannot find the file specified.
// 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";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template
<
typename
...
Args
>
windows_error
(
int
error_code
,
string_view
message
,
const
Args
&
...
args
)
{
system_error
(
int
error_code
,
string_view
message
,
const
Args
&
...
args
)
:
std
::
runtime_error
(
""
)
{
init
(
error_code
,
message
,
make_args
(
args
...));
}
~
system_error
()
throw
();
int
error_code
()
const
{
return
error_code_
;
}
};
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API
void
report_windows_error
(
int
error_code
,
string_view
message
)
FMT_NOEXCEPT
;
#endif
enum
Color
{
BLACK
,
RED
,
GREEN
,
YELLOW
,
BLUE
,
MAGENTA
,
CYAN
,
WHITE
};
FMT_API
void
vprint_colored
(
Color
c
,
string_view
format
,
args
args
);
/**
Formats a string and prints it to stdout using ANSI escape sequences
to specify color (experimental).
Example:
print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template
<
typename
...
Args
>
inline
void
print_colored
(
Color
c
,
string_view
format_str
,
const
Args
&
...
args
)
{
vprint_colored
(
c
,
format_str
,
make_args
(
args
...));
}
template
<
typename
ArgFormatter
,
typename
Char
,
typename
Context
>
void
vformat_to
(
basic_buffer
<
Char
>
&
buffer
,
basic_string_view
<
Char
>
format_str
,
basic_args
<
Context
>
args
);
inline
void
vformat_to
(
buffer
&
buf
,
string_view
format_str
,
args
args
)
{
vformat_to
<
arg_formatter
<
char
>>
(
buf
,
format_str
,
args
);
}
inline
void
vformat_to
(
wbuffer
&
buf
,
wstring_view
format_str
,
wargs
args
)
{
vformat_to
<
arg_formatter
<
wchar_t
>>
(
buf
,
format_str
,
args
);
}
template
<
typename
...
Args
>
inline
void
format_to
(
buffer
&
buf
,
string_view
format_str
,
const
Args
&
...
args
)
{
vformat_to
(
buf
,
format_str
,
make_args
(
args
...));
}
template
<
typename
...
Args
>
inline
void
format_to
(
wbuffer
&
buf
,
wstring_view
format_str
,
const
Args
&
...
args
)
{
vformat_to
(
buf
,
format_str
,
make_args
<
wcontext
>
(
args
...));
}
inline
std
::
string
vformat
(
string_view
format_str
,
args
args
)
{
memory_buffer
buffer
;
vformat_to
(
buffer
,
format_str
,
args
);
return
to_string
(
buffer
);
}
/**
\rst
Formats arguments and returns the result as a string.
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:
**Example**::
.. parsed-literal::
*<message>*: *<system-message>*
std::string message = format("The answer is {}", 42);
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
*/
template
<
typename
...
Args
>
inline
std
::
string
format
(
string_view
format_str
,
const
Args
&
...
args
)
{
return
vformat
(
format_str
,
make_args
(
args
...));
}
inline
std
::
wstring
vformat
(
wstring_view
format_str
,
wargs
args
)
{
wmemory_buffer
buffer
;
vformat_to
(
buffer
,
format_str
,
args
);
return
to_string
(
buffer
);
}
template
<
typename
...
Args
>
inline
std
::
wstring
format
(
wstring_view
format_str
,
const
Args
&
...
args
)
{
return
vformat
(
format_str
,
make_args
<
wcontext
>
(
args
...));
}
FMT_API
void
vprint
(
std
::
FILE
*
f
,
string_view
format_str
,
args
args
);
*/
FMT_API
void
format_system_error
(
fmt
::
buffer
&
out
,
int
error_code
,
fmt
::
string_view
message
)
FMT_NOEXCEPT
;
/**
\rst
Prints formatted data to the file *f*.
This template provides operations for formatting and writing data into a
character buffer. The output buffer is specified by a subclass such as
:class:`fmt::BasicMemoryWriter`.
**Example**::
You can use one of the following typedefs for common character types:
+---------+-----------------------+
| Type | Definition |
+=========+=======================+
| writer | basic_writer<char> |
+---------+-----------------------+
| wwriter | basic_writer<wchar_t> |
+---------+-----------------------+
print(stderr, "Don't {}!", "panic");
\endrst
*/
template
<
typename
...
Args
>
inline
void
print
(
std
::
FILE
*
f
,
string_view
format_str
,
const
Args
&
...
args
)
{
vprint
(
f
,
format_str
,
make_args
(
args
...));
}
template
<
typename
Char
>
class
basic_writer
{
public:
typedef
basic_format_specs
<
Char
>
format_specs
;
FMT_API
void
vprint
(
string_view
format_str
,
args
args
);
private:
// Output buffer.
basic_buffer
<
Char
>
&
buffer_
;
/**
\rst
Prints formatted data to ``stdout``.
FMT_DISALLOW_COPY_AND_ASSIGN
(
basic_writer
);
**Example**::
#if FMT_SECURE_SCL
typedef
stdext
::
checked_array_iterator
<
Char
*>
pointer_type
;
// Returns pointer value.
static
Char
*
get
(
pointer_type
p
)
{
return
p
.
base
();
}
#else
typedef
Char
*
pointer_type
;
static
Char
*
get
(
Char
*
p
)
{
return
p
;
}
#endif
print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template
<
typename
...
Args
>
inline
void
print
(
string_view
format_str
,
const
Args
&
...
args
)
{
vprint
(
format_str
,
make_args
(
args
...));
}
// Fills the padding around the content and returns the pointer to the
// content area.
static
pointer_type
fill_padding
(
pointer_type
buffer
,
unsigned
total_size
,
std
::
size_t
content_size
,
wchar_t
fill
);
/**
Fast integer formatter.
*/
class
FormatInt
{
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
enum
{
BUFFER_SIZE
=
std
::
numeric_limits
<
unsigned
long
long
>::
digits10
+
3
};
mutable
char
buffer_
[
BUFFER_SIZE
];
char
*
str_
;
// Grows the buffer by n characters and returns a pointer to the newly
// allocated area.
pointer_type
grow_buffer
(
std
::
size_t
n
)
{
std
::
size_t
size
=
buffer_
.
size
();
buffer_
.
resize
(
size
+
n
);
return
internal
::
make_ptr
(
&
buffer_
[
size
],
n
);
}
// Formats value in reverse and returns the number of digits.
char
*
format_decimal
(
unsigned
long
long
value
)
{
char
*
buffer_end
=
buffer_
+
BUFFER_SIZE
-
1
;
while
(
value
>=
100
)
{
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
unsigned
index
=
static_cast
<
unsigned
>
((
value
%
100
)
*
2
);
value
/=
100
;
*--
buffer_end
=
internal
::
data
::
DIGITS
[
index
+
1
];
*--
buffer_end
=
internal
::
data
::
DIGITS
[
index
];
}
if
(
value
<
10
)
{
*--
buffer_end
=
static_cast
<
char
>
(
'0'
+
value
);
return
buffer_end
;
}
unsigned
index
=
static_cast
<
unsigned
>
(
value
*
2
);
*--
buffer_end
=
internal
::
data
::
DIGITS
[
index
+
1
];
*--
buffer_end
=
internal
::
data
::
DIGITS
[
index
];
return
buffer_end
;
// Writes an unsigned decimal integer.
template
<
typename
UInt
>
Char
*
write_unsigned_decimal
(
UInt
value
,
unsigned
prefix_size
=
0
)
{
unsigned
num_digits
=
internal
::
count_digits
(
value
);
Char
*
ptr
=
get
(
grow_buffer
(
prefix_size
+
num_digits
));
internal
::
format_decimal
(
ptr
+
prefix_size
,
value
,
num_digits
);
return
ptr
;
}
void
FormatSigned
(
long
long
value
)
{
unsigned
long
long
abs_value
=
static_cast
<
unsigned
long
long
>
(
value
);
bool
negative
=
value
<
0
;
if
(
negative
)
// 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
);
if
(
internal
::
is_negative
(
value
))
{
abs_value
=
0
-
abs_value
;
str_
=
format_decimal
(
abs_value
);
if
(
negative
)
*--
str_
=
'-'
;
*
write_unsigned_decimal
(
abs_value
,
1
)
=
'-'
;
}
else
{
write_unsigned_decimal
(
abs_value
,
0
);
}
}
public:
explicit
FormatInt
(
int
value
)
{
FormatSigned
(
value
);
}
explicit
FormatInt
(
long
value
)
{
FormatSigned
(
value
);
}
explicit
FormatInt
(
long
long
value
)
{
FormatSigned
(
value
);
}
explicit
FormatInt
(
unsigned
value
)
:
str_
(
format_decimal
(
value
))
{}
explicit
FormatInt
(
unsigned
long
value
)
:
str_
(
format_decimal
(
value
))
{}
explicit
FormatInt
(
unsigned
long
long
value
)
:
str_
(
format_decimal
(
value
))
{}
// Prepare a buffer for integer formatting.
pointer_type
prepare_int_buffer
(
unsigned
num_digits
,
const
empty_spec
&
,
const
char
*
prefix
,
unsigned
prefix_size
)
{
unsigned
size
=
prefix_size
+
num_digits
;
pointer_type
p
=
grow_buffer
(
size
);
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
return
p
+
size
-
1
;
}
/** Returns the number of characters written to the output buffer. */
std
::
size_t
size
()
const
{
return
internal
::
to_unsigned
(
buffer_
-
str_
+
BUFFER_SIZE
-
1
);
template
<
typename
Spec
>
pointer_type
prepare_int_buffer
(
unsigned
num_digits
,
const
Spec
&
spec
,
const
char
*
prefix
,
unsigned
prefix_size
);
// Writes a formatted integer.
template
<
typename
T
,
typename
Spec
>
void
write_int
(
T
value
,
const
Spec
&
spec
);
// Formats a floating-point number (double or long double).
template
<
typename
T
>
void
write_double
(
T
value
,
const
format_specs
&
spec
);
// Writes a formatted string.
template
<
typename
StrChar
>
pointer_type
write_str
(
const
StrChar
*
s
,
std
::
size_t
size
,
const
align_spec
&
spec
);
template
<
typename
StrChar
>
void
write_str
(
basic_string_view
<
StrChar
>
str
,
const
format_specs
&
spec
);
// Appends floating-point length specifier to the format string.
// The second argument is only used for overload resolution.
void
append_float_length
(
Char
*&
format_ptr
,
long
double
)
{
*
format_ptr
++
=
'L'
;
}
template
<
typename
T
>
void
append_float_length
(
Char
*&
,
T
)
{}
template
<
typename
Char_
>
friend
class
internal
::
arg_formatter_base
;
public:
/**
Returns a pointer to the output buffer content. No terminating null
character is appended.
Constructs a ``basic_writer`` object.
*/
const
char
*
data
()
const
{
return
str_
;
}
explicit
basic_writer
(
basic_buffer
<
Char
>
&
b
)
:
buffer_
(
b
)
{
}
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const
char
*
c_str
()
const
{
buffer_
[
BUFFER_SIZE
-
1
]
=
'\0'
;
return
str_
;
\rst
Destroys the ``basic_writer`` object.
\endrst
*/
virtual
~
basic_writer
()
{}
/**
Returns the total number of characters written.
*/
std
::
size_t
size
()
const
{
return
buffer_
.
size
();
}
/**
Returns a pointer to the output buffer content. No terminating null
character is appended.
*/
const
Char
*
data
()
const
FMT_NOEXCEPT
{
return
&
buffer_
[
0
];
}
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const
Char
*
c_str
()
const
{
std
::
size_t
size
=
buffer_
.
size
();
buffer_
.
reserve
(
size
+
1
);
buffer_
[
size
]
=
'\0'
;
return
&
buffer_
[
0
];
}
/**
\rst
Returns the content of the output buffer as an `
`std::string`
`.
Returns the content of the output buffer as an `
std::string
`.
\endrst
*/
std
::
string
str
()
const
{
return
std
::
string
(
str_
,
size
());
}
};
std
::
basic_string
<
Char
>
str
()
const
{
return
std
::
basic_string
<
Char
>
(
&
buffer_
[
0
],
buffer_
.
size
());
}
// Formats a decimal integer value writing into buffer and returns
// a pointer to the end of the formatted string. This function doesn't
// write a terminating null character.
template
<
typename
T
>
inline
void
format_decimal
(
char
*&
buffer
,
T
value
)
{
typedef
typename
internal
::
int_traits
<
T
>::
main_type
main_type
;
main_type
abs_value
=
static_cast
<
main_type
>
(
value
);
if
(
internal
::
is_negative
(
value
))
{
*
buffer
++
=
'-'
;
abs_value
=
0
-
abs_value
;
void
write
(
int
value
)
{
write_decimal
(
value
);
}
if
(
abs_value
<
100
)
{
if
(
abs_value
<
10
)
{
*
buffer
++
=
static_cast
<
char
>
(
'0'
+
abs_value
);
return
;
}
unsigned
index
=
static_cast
<
unsigned
>
(
abs_value
*
2
);
*
buffer
++
=
internal
::
data
::
DIGITS
[
index
];
*
buffer
++
=
internal
::
data
::
DIGITS
[
index
+
1
];
return
;
void
write
(
long
value
)
{
write_decimal
(
value
);
}
void
write
(
long
long
value
)
{
write_decimal
(
value
);
}
unsigned
num_digits
=
internal
::
count_digits
(
abs_value
);
internal
::
format_decimal
(
buffer
,
abs_value
,
num_digits
);
buffer
+=
num_digits
;
}
/**
\rst
Returns a named argument for formatting functions.
**Example**::
print("Elapsed time: {s:.2f} seconds", arg("s", 1.23));
\endrst
*/
template
<
typename
T
>
inline
internal
::
named_arg
<
context
>
arg
(
string_view
name
,
const
T
&
arg
)
{
return
internal
::
named_arg
<
context
>
(
name
,
arg
);
}
/**
\rst
Formats *value* and writes it to the buffer.
\endrst
*/
template
<
typename
T
,
typename
...
FormatSpecs
>
typename
std
::
enable_if
<
std
::
is_integral
<
T
>::
value
,
void
>::
type
write
(
T
value
,
FormatSpecs
...
specs
)
{
write_int
(
value
,
format_specs
(
specs
...));
}
template
<
typename
T
>
inline
internal
::
named_arg
<
wcontext
>
arg
(
wstring_view
name
,
const
T
&
arg
)
{
return
internal
::
named_arg
<
wcontext
>
(
name
,
arg
);
}
void
write
(
double
value
)
{
write_double
(
value
,
format_specs
());
}
// The following two functions are deleted intentionally to disable
// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``.
template
<
typename
Context
>
void
arg
(
string_view
,
const
internal
::
named_arg
<
Context
>&
)
FMT_DELETED_OR_UNDEFINED
;
template
<
typename
Context
>
void
arg
(
wstring_view
,
const
internal
::
named_arg
<
Context
>&
)
FMT_DELETED_OR_UNDEFINED
;
}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void
write
(
long
double
value
)
{
write_double
(
value
,
format_specs
())
;
}
namespace
fmt
{
namespace
internal
{
template
<
typename
Char
>
constexpr
bool
is_name_start
(
Char
c
)
{
return
(
'a'
<=
c
&&
c
<=
'z'
)
||
(
'A'
<=
c
&&
c
<=
'Z'
)
||
'_'
==
c
;
}
/**
Writes a character to the buffer.
*/
void
write
(
char
value
)
{
buffer_
.
push_back
(
value
)
;
}
// Parses the input as an unsigned integer. This function assumes that the
// first character is a digit and presence of a non-digit character at the end.
// it: an iterator pointing to the beginning of the input range.
template
<
typename
Iterator
,
typename
ErrorHandler
>
constexpr
unsigned
parse_nonnegative_int
(
Iterator
&
it
,
ErrorHandler
&
handler
)
{
assert
(
'0'
<=
*
it
&&
*
it
<=
'9'
);
unsigned
value
=
0
;
do
{
unsigned
new_value
=
value
*
10
+
(
*
it
-
'0'
);
// Workaround for MSVC "setup_exception stack overflow" error:
auto
next
=
it
;
++
next
;
it
=
next
;
// Check if value wrapped around.
if
(
new_value
<
value
)
{
value
=
(
std
::
numeric_limits
<
unsigned
>::
max
)();
break
;
}
value
=
new_value
;
}
while
(
'0'
<=
*
it
&&
*
it
<=
'9'
);
// Convert to unsigned to prevent a warning.
unsigned
max_int
=
(
std
::
numeric_limits
<
int
>::
max
)();
if
(
value
>
max_int
)
handler
.
on_error
(
"number is too big"
);
return
value
;
}
void
write
(
wchar_t
value
)
{
internal
::
require_wchar
<
Char
>
();
buffer_
.
push_back
(
value
);
}
template
<
typename
Char
,
typename
Context
>
class
custom_formatter
{
private:
basic_buffer
<
Char
>
&
buffer_
;
Context
&
ctx_
;
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void
write
(
string_view
value
)
{
const
char
*
str
=
value
.
data
();
buffer_
.
append
(
str
,
str
+
value
.
size
());
}
public:
custom_formatter
(
basic_buffer
<
Char
>
&
buffer
,
Context
&
ctx
)
:
buffer_
(
buffer
),
ctx_
(
ctx
)
{}
void
write
(
basic_string_view
<
wchar_t
>
value
)
{
internal
::
require_wchar
<
Char
>
();
const
wchar_t
*
str
=
value
.
data
();
buffer_
.
append
(
str
,
str
+
value
.
size
());
}
bool
operator
()(
internal
::
custom_value
<
Char
>
custom
)
{
custom
.
format
(
buffer_
,
custom
.
value
,
&
ctx_
);
return
true
;
template
<
typename
...
FormatSpecs
>
void
write
(
basic_string_view
<
Char
>
str
,
FormatSpecs
...
specs
)
{
write_str
(
str
,
format_specs
(
specs
...))
;
}
template
<
typename
T
>
bool
operator
()(
T
)
{
return
false
;
}
};
void
clear
()
FMT_NOEXCEPT
{
buffer_
.
resize
(
0
);
}
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
};
basic_buffer
<
Char
>
&
buffer
()
FMT_NOEXCEPT
{
return
buffer_
;
}
};
template
<
typename
ErrorHandle
r
>
class
width_checker
{
public:
explicit
constexpr
width_checker
(
ErrorHandler
&
eh
)
:
handler_
(
eh
)
{}
template
<
typename
T
>
constexpr
typename
std
::
enable_if
<
is_integer
<
T
>::
value
,
unsigned
long
long
>::
type
operator
()(
T
value
)
{
if
(
is_negative
(
value
))
handler_
.
on_error
(
"negative width"
);
return
valu
e
;
}
template
<
typename
T
>
constexpr
typename
std
::
enable_if
<
!
is_integer
<
T
>::
value
,
unsigned
long
long
>::
type
operator
()(
T
)
{
handler_
.
on_error
(
"width is not integer"
);
return
0
;
template
<
typename
Cha
r
>
template
<
typename
StrChar
>
typename
basic_writer
<
Char
>::
pointer_type
basic_writer
<
Char
>::
write_str
(
const
StrChar
*
s
,
std
::
size_t
size
,
const
align_spec
&
spec
)
{
pointer_type
out
=
pointer_type
();
if
(
spec
.
width
()
>
size
)
{
out
=
grow_buffer
(
spec
.
width
());
Char
fill
=
internal
::
char_traits
<
Char
>::
cast
(
spec
.
fill
());
if
(
spec
.
align
()
==
ALIGN_RIGHT
)
{
std
::
uninitialized_fill_n
(
out
,
spec
.
width
()
-
size
,
fill
);
out
+=
spec
.
width
()
-
siz
e
;
}
else
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
out
=
fill_padding
(
out
,
spec
.
width
(),
size
,
fill
);
}
else
{
std
::
uninitialized_fill_n
(
out
+
size
,
spec
.
width
()
-
size
,
fill
);
}
}
else
{
out
=
grow_buffer
(
size
)
;
}
std
::
uninitialized_copy
(
s
,
s
+
size
,
out
);
return
out
;
}
private:
ErrorHandler
&
handler_
;
};
template
<
typename
Char
>
template
<
typename
StrChar
>
void
basic_writer
<
Char
>::
write_str
(
basic_string_view
<
StrChar
>
s
,
const
format_specs
&
spec
)
{
// Check if StrChar is convertible to Char.
internal
::
char_traits
<
Char
>::
convert
(
StrChar
());
if
(
spec
.
type_
&&
spec
.
type_
!=
's'
)
internal
::
report_unknown_type
(
spec
.
type_
,
"string"
);
const
StrChar
*
str_value
=
s
.
data
();
std
::
size_t
str_size
=
s
.
size
();
if
(
str_size
==
0
&&
!
str_value
)
FMT_THROW
(
format_error
(
"string pointer is null"
));
std
::
size_t
precision
=
static_cast
<
std
::
size_t
>
(
spec
.
precision_
);
if
(
spec
.
precision_
>=
0
&&
precision
<
str_size
)
str_size
=
precision
;
write_str
(
str_value
,
str_size
,
spec
);
}
template
<
typename
ErrorHandler
>
class
precision_checker
{
public:
explicit
constexpr
precision_checker
(
ErrorHandler
&
eh
)
:
handler_
(
eh
)
{}
template
<
typename
Char
>
typename
basic_writer
<
Char
>::
pointer_type
basic_writer
<
Char
>::
fill_padding
(
pointer_type
buffer
,
unsigned
total_size
,
std
::
size_t
content_size
,
wchar_t
fill
)
{
std
::
size_t
padding
=
total_size
-
content_size
;
std
::
size_t
left_padding
=
padding
/
2
;
Char
fill_char
=
internal
::
char_traits
<
Char
>::
cast
(
fill
);
std
::
uninitialized_fill_n
(
buffer
,
left_padding
,
fill_char
);
buffer
+=
left_padding
;
pointer_type
content
=
buffer
;
std
::
uninitialized_fill_n
(
buffer
+
content_size
,
padding
-
left_padding
,
fill_char
);
return
content
;
}
template
<
typename
T
>
constexpr
typename
std
::
enable_if
<
is_integer
<
T
>::
value
,
unsigned
long
long
>::
type
operator
()(
T
value
)
{
if
(
is_negative
(
value
))
handler_
.
on_error
(
"negative precision"
);
return
value
;
template
<
typename
Char
>
template
<
typename
Spec
>
typename
basic_writer
<
Char
>::
pointer_type
basic_writer
<
Char
>::
prepare_int_buffer
(
unsigned
num_digits
,
const
Spec
&
spec
,
const
char
*
prefix
,
unsigned
prefix_size
)
{
unsigned
width
=
spec
.
width
();
alignment
align
=
spec
.
align
();
Char
fill
=
internal
::
char_traits
<
Char
>::
cast
(
spec
.
fill
());
if
(
spec
.
precision
()
>
static_cast
<
int
>
(
num_digits
))
{
// Octal prefix '0' is counted as a digit, so ignore it if precision
// is specified.
if
(
prefix_size
>
0
&&
prefix
[
prefix_size
-
1
]
==
'0'
)
--
prefix_size
;
unsigned
number_size
=
prefix_size
+
internal
::
to_unsigned
(
spec
.
precision
());
align_spec
subspec
(
number_size
,
'0'
,
ALIGN_NUMERIC
);
if
(
number_size
>=
width
)
return
prepare_int_buffer
(
num_digits
,
subspec
,
prefix
,
prefix_size
);
buffer_
.
reserve
(
width
);
unsigned
fill_size
=
width
-
number_size
;
if
(
align
!=
ALIGN_LEFT
)
{
pointer_type
p
=
grow_buffer
(
fill_size
);
std
::
uninitialized_fill
(
p
,
p
+
fill_size
,
fill
);
}
pointer_type
result
=
prepare_int_buffer
(
num_digits
,
subspec
,
prefix
,
prefix_size
);
if
(
align
==
ALIGN_LEFT
)
{
pointer_type
p
=
grow_buffer
(
fill_size
);
std
::
uninitialized_fill
(
p
,
p
+
fill_size
,
fill
);
}
return
result
;
}
template
<
typename
T
>
constexpr
typename
std
::
enable_if
<
!
is_integer
<
T
>::
value
,
unsigned
long
long
>::
type
operator
()(
T
)
{
handler_
.
on_error
(
"precision is not integer"
);
return
0
;
unsigned
size
=
prefix_size
+
num_digits
;
if
(
width
<=
size
)
{
pointer_type
p
=
grow_buffer
(
size
);
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
return
p
+
size
-
1
;
}
pointer_type
p
=
grow_buffer
(
width
);
pointer_type
end
=
p
+
width
;
if
(
align
==
ALIGN_LEFT
)
{
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
p
+=
size
;
std
::
uninitialized_fill
(
p
,
end
,
fill
);
}
else
if
(
align
==
ALIGN_CENTER
)
{
p
=
fill_padding
(
p
,
width
,
size
,
fill
);
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
p
+=
size
;
}
else
{
if
(
align
==
ALIGN_NUMERIC
)
{
if
(
prefix_size
!=
0
)
{
p
=
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
p
);
size
-=
prefix_size
;
}
}
else
{
std
::
uninitialized_copy
(
prefix
,
prefix
+
prefix_size
,
end
-
size
);
}
std
::
uninitialized_fill
(
p
,
end
-
size
,
fill
);
p
=
end
;
}
return
p
-
1
;
}
private:
ErrorHandler
&
handler_
;
};
// A format specifier handler that sets fields in basic_format_specs.
template
<
typename
Char
>
class
specs_setter
:
public
error_handler
{
public:
explicit
constexpr
specs_setter
(
basic_format_specs
<
Char
>
&
specs
)
:
specs_
(
specs
)
{}
template
<
typename
T
,
typename
Spec
>
void
basic_writer
<
Char
>::
write_int
(
T
value
,
const
Spec
&
spec
)
{
struct
spec_handler
{
basic_writer
<
Char
>
&
writer
;
const
Spec
&
spec
;
unsigned
prefix_size
=
0
;
typedef
typename
internal
::
int_traits
<
T
>::
main_type
UnsignedType
;
UnsignedType
abs_value
;
char
prefix
[
4
]
=
""
;
constexpr
void
on_align
(
alignment
align
)
{
specs_
.
align_
=
align
;
}
constexpr
void
on_fill
(
Char
fill
)
{
specs_
.
fill_
=
fill
;
}
constexpr
void
on_plus
()
{
specs_
.
flags_
|=
SIGN_FLAG
|
PLUS_FLAG
;
}
constexpr
void
on_minus
()
{
specs_
.
flags_
|=
MINUS_FLAG
;
}
constexpr
void
on_space
()
{
specs_
.
flags_
|=
SIGN_FLAG
;
}
constexpr
void
on_hash
()
{
specs_
.
flags_
|=
HASH_FLAG
;
}
spec_handler
(
basic_writer
<
Char
>
&
w
,
T
value
,
const
Spec
&
s
)
:
writer
(
w
),
abs_value
(
static_cast
<
UnsignedType
>
(
value
)),
spec
(
s
)
{
if
(
internal
::
is_negative
(
value
))
{
prefix
[
0
]
=
'-'
;
++
prefix_size
;
abs_value
=
0
-
abs_value
;
}
else
if
(
spec
.
flag
(
SIGN_FLAG
))
{
prefix
[
0
]
=
spec
.
flag
(
PLUS_FLAG
)
?
'+'
:
' '
;
++
prefix_size
;
}
}
constexpr
void
on_zero
()
{
specs_
.
align_
=
ALIGN_NUMERIC
;
specs_
.
fill_
=
'0'
;
}
void
on_dec
()
{
unsigned
num_digits
=
internal
::
count_digits
(
abs_value
);
pointer_type
p
=
writer
.
prepare_int_buffer
(
num_digits
,
spec
,
prefix
,
prefix_size
)
+
1
;
internal
::
format_decimal
(
get
(
p
),
abs_value
,
0
);
}
constexpr
void
on_width
(
unsigned
width
)
{
specs_
.
width_
=
width
;
}
constexpr
void
on_precision
(
unsigned
precision
)
{
specs_
.
precision_
=
precision
;
}
constexpr
void
end_precision
()
{}
void
on_hex
()
{
UnsignedType
n
=
abs_value
;
if
(
spec
.
flag
(
HASH_FLAG
))
{
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
spec
.
type
();
}
unsigned
num_digits
=
0
;
do
{
++
num_digits
;
}
while
((
n
>>=
4
)
!=
0
);
Char
*
p
=
get
(
writer
.
prepare_int_buffer
(
num_digits
,
spec
,
prefix
,
prefix_size
));
n
=
abs_value
;
const
char
*
digits
=
spec
.
type
()
==
'x'
?
"0123456789abcdef"
:
"0123456789ABCDEF"
;
do
{
*
p
--
=
digits
[
n
&
0xf
];
}
while
((
n
>>=
4
)
!=
0
);
}
constexpr
void
on_type
(
Char
type
)
{
specs_
.
type_
=
type
;
}
void
on_bin
()
{
UnsignedType
n
=
abs_value
;
if
(
spec
.
flag
(
HASH_FLAG
))
{
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
spec
.
type
();
}
unsigned
num_digits
=
0
;
do
{
++
num_digits
;
}
while
((
n
>>=
1
)
!=
0
);
Char
*
p
=
get
(
writer
.
prepare_int_buffer
(
num_digits
,
spec
,
prefix
,
prefix_size
));
n
=
abs_value
;
do
{
*
p
--
=
static_cast
<
Char
>
(
'0'
+
(
n
&
1
));
}
while
((
n
>>=
1
)
!=
0
);
}
protected:
basic_format_specs
<
Char
>
&
specs_
;
};
void
on_oct
()
{
UnsignedType
n
=
abs_value
;
if
(
spec
.
flag
(
HASH_FLAG
))
prefix
[
prefix_size
++
]
=
'0'
;
unsigned
num_digits
=
0
;
do
{
++
num_digits
;
}
while
((
n
>>=
3
)
!=
0
);
Char
*
p
=
get
(
writer
.
prepare_int_buffer
(
num_digits
,
spec
,
prefix
,
prefix_size
));
n
=
abs_value
;
do
{
*
p
--
=
static_cast
<
Char
>
(
'0'
+
(
n
&
7
));
}
while
((
n
>>=
3
)
!=
0
);
}
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template
<
typename
Handler
>
class
specs_checker
:
public
Handler
{
public:
constexpr
specs_checker
(
const
Handler
&
handler
,
internal
::
type
arg_type
)
:
Handler
(
handler
),
arg_type_
(
arg_type
)
{}
void
on_num
()
{
unsigned
num_digits
=
internal
::
count_digits
(
abs_value
);
std
::
locale
loc
=
writer
.
buffer_
.
locale
();
Char
thousands_sep
=
std
::
use_facet
<
std
::
numpunct
<
Char
>>
(
loc
).
thousands_sep
();
fmt
::
basic_string_view
<
Char
>
sep
(
&
thousands_sep
,
1
);
unsigned
size
=
static_cast
<
unsigned
>
(
num_digits
+
sep
.
size
()
*
((
num_digits
-
1
)
/
3
));
pointer_type
p
=
writer
.
prepare_int_buffer
(
size
,
spec
,
prefix
,
prefix_size
)
+
1
;
internal
::
format_decimal
(
get
(
p
),
abs_value
,
0
,
internal
::
add_thousands_sep
<
Char
>
(
sep
));
}
constexpr
void
on_align
(
alignment
align
)
{
if
(
align
==
ALIGN_NUMERIC
)
require_numeric_argument
(
'='
);
Handler
::
on_align
(
align
);
void
on_error
()
{
internal
::
report_unknown_type
(
spec
.
type
(),
spec
.
flag
(
CHAR_FLAG
)
?
"char"
:
"integer"
);
}
};
internal
::
handle_integral_type_spec
(
spec
.
type
(),
spec_handler
(
*
this
,
value
,
spec
));
}
template
<
typename
Char
>
template
<
typename
T
>
void
basic_writer
<
Char
>::
write_double
(
T
value
,
const
format_specs
&
spec
)
{
// Check type.
char
type
=
spec
.
type
();
bool
upper
=
false
;
switch
(
type
)
{
case
0
:
type
=
'g'
;
break
;
case
'e'
:
case
'f'
:
case
'g'
:
case
'a'
:
break
;
case
'F'
:
#if FMT_MSC_VER
// MSVC's printf doesn't support 'F'.
type
=
'f'
;
#endif
// Fall through.
case
'E'
:
case
'G'
:
case
'A'
:
upper
=
true
;
break
;
default:
internal
::
report_unknown_type
(
type
,
"double"
);
break
;
}
constexpr
void
on_plus
()
{
check_sign
(
'+'
);
Handler
::
on_plus
();
char
sign
=
0
;
// Use isnegative instead of value < 0 because the latter is always
// false for NaN.
if
(
internal
::
fputil
::
isnegative
(
static_cast
<
double
>
(
value
)))
{
sign
=
'-'
;
value
=
-
value
;
}
else
if
(
spec
.
flag
(
SIGN_FLAG
))
{
sign
=
spec
.
flag
(
PLUS_FLAG
)
?
'+'
:
' '
;
}
constexpr
void
on_minus
()
{
check_sign
(
'-'
);
Handler
::
on_minus
();
if
(
internal
::
fputil
::
isnotanumber
(
value
))
{
// Format NaN ourselves because sprintf's output is not consistent
// across platforms.
std
::
size_t
nan_size
=
4
;
const
char
*
nan
=
upper
?
" NAN"
:
" nan"
;
if
(
!
sign
)
{
--
nan_size
;
++
nan
;
}
pointer_type
out
=
write_str
(
nan
,
nan_size
,
spec
);
if
(
sign
)
*
out
=
sign
;
return
;
}
constexpr
void
on_space
()
{
check_sign
(
' '
);
Handler
::
on_space
();
if
(
internal
::
fputil
::
isinfinity
(
value
))
{
// Format infinity ourselves because sprintf's output is not consistent
// across platforms.
std
::
size_t
inf_size
=
4
;
const
char
*
inf
=
upper
?
" INF"
:
" inf"
;
if
(
!
sign
)
{
--
inf_size
;
++
inf
;
}
pointer_type
out
=
write_str
(
inf
,
inf_size
,
spec
);
if
(
sign
)
*
out
=
sign
;
return
;
}
constexpr
void
on_hash
()
{
require_numeric_argument
(
'#'
);
Handler
::
on_hash
();
std
::
size_t
offset
=
buffer_
.
size
();
unsigned
width
=
spec
.
width
();
if
(
sign
)
{
buffer_
.
reserve
(
buffer_
.
size
()
+
(
width
>
1u
?
width
:
1u
));
if
(
width
>
0
)
--
width
;
++
offset
;
}
constexpr
void
on_zero
()
{
require_numeric_argument
(
'0'
);
Handler
::
on_zero
();
// Build format string.
enum
{
MAX_FORMAT_SIZE
=
10
};
// longest format: %#-*.*Lg
Char
format
[
MAX_FORMAT_SIZE
];
Char
*
format_ptr
=
format
;
*
format_ptr
++
=
'%'
;
unsigned
width_for_sprintf
=
width
;
if
(
spec
.
flag
(
HASH_FLAG
))
*
format_ptr
++
=
'#'
;
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
width_for_sprintf
=
0
;
}
else
{
if
(
spec
.
align
()
==
ALIGN_LEFT
)
*
format_ptr
++
=
'-'
;
if
(
width
!=
0
)
*
format_ptr
++
=
'*'
;
}
constexpr
void
end_precision
()
{
if
(
is_integral
(
arg_type_
)
||
arg_type_
==
POINTER
)
{
report_error
(
"precision not allowed in {} format specifier"
,
arg_type_
==
POINTER
?
"pointer"
:
"integer"
);
}
if
(
spec
.
precision
()
>=
0
)
{
*
format_ptr
++
=
'.'
;
*
format_ptr
++
=
'*'
;
}
private:
template
<
typename
...
Args
>
void
report_error
(
string_view
format_str
,
const
Args
&
...
args
)
{
this
->
on_error
(
format
(
format_str
,
args
...).
c_str
());
}
append_float_length
(
format_ptr
,
value
);
*
format_ptr
++
=
type
;
*
format_ptr
=
'\0'
;
template
<
typename
Char
>
constexpr
void
require_numeric_argument
(
Char
spec
)
{
if
(
!
is_numeric
(
arg_type_
))
{
report_error
(
"format specifier '{}' requires numeric argument"
,
static_cast
<
char
>
(
spec
));
// Format using snprintf.
Char
fill
=
internal
::
char_traits
<
Char
>::
cast
(
spec
.
fill
());
unsigned
n
=
0
;
Char
*
start
=
0
;
for
(;;)
{
std
::
size_t
buffer_size
=
buffer_
.
capacity
()
-
offset
;
#if FMT_MSC_VER
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
// space for at least one extra character to make the size non-zero.
// Note that the buffer's capacity will increase by more than 1.
if
(
buffer_size
==
0
)
{
buffer_
.
reserve
(
offset
+
1
);
buffer_size
=
buffer_
.
capacity
()
-
offset
;
}
#endif
start
=
&
buffer_
[
offset
];
int
result
=
internal
::
char_traits
<
Char
>::
format_float
(
start
,
buffer_size
,
format
,
width_for_sprintf
,
spec
.
precision
(),
value
);
if
(
result
>=
0
)
{
n
=
internal
::
to_unsigned
(
result
);
if
(
offset
+
n
<
buffer_
.
capacity
())
break
;
// The buffer is large enough - continue with formatting.
buffer_
.
reserve
(
offset
+
n
+
1
);
}
else
{
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buffer_
.
reserve
(
buffer_
.
capacity
()
+
1
);
}
}
template
<
typename
Char
>
constexpr
void
check_sign
(
Char
sign
)
{
require_numeric_argument
(
sign
);
if
(
is_integral
(
arg_type_
)
&&
arg_type_
!=
INT
&&
arg_type_
!=
LONG_LONG
&&
arg_type_
!=
CHAR
)
{
report_error
(
"format specifier '{}' requires signed argument"
,
static_cast
<
char
>
(
sign
));
if
(
sign
)
{
if
((
spec
.
align
()
!=
ALIGN_RIGHT
&&
spec
.
align
()
!=
ALIGN_DEFAULT
)
||
*
start
!=
' '
)
{
*
(
start
-
1
)
=
sign
;
sign
=
0
;
}
else
{
*
(
start
-
1
)
=
fill
;
}
++
n
;
}
if
(
spec
.
align
()
==
ALIGN_CENTER
&&
spec
.
width
()
>
n
)
{
width
=
spec
.
width
();
pointer_type
p
=
grow_buffer
(
width
);
std
::
memmove
(
get
(
p
)
+
(
width
-
n
)
/
2
,
get
(
p
),
n
*
sizeof
(
Char
));
fill_padding
(
p
,
spec
.
width
(),
n
,
fill
);
return
;
}
if
(
spec
.
fill
()
!=
' '
||
sign
)
{
while
(
*
start
==
' '
)
*
start
++
=
fill
;
if
(
sign
)
*
(
start
-
1
)
=
sign
;
}
grow_buffer
(
n
);
}
internal
::
type
arg_type_
;
};
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API
void
report_system_error
(
int
error_code
,
string_view
message
)
FMT_NOEXCEPT
;
template
<
template
<
typename
>
class
Handler
,
typename
T
,
typename
Context
>
constexpr
void
set_dynamic_spec
(
T
&
value
,
basic_arg
<
Context
>
arg
)
{
error_handler
eh
;
unsigned
long
long
big_value
=
visit
(
Handler
<
error_handler
>
(
eh
),
arg
);
if
(
big_value
>
(
std
::
numeric_limits
<
int
>::
max
)())
eh
.
on_error
(
"number is too big"
);
value
=
static_cast
<
int
>
(
big_value
);
}
#if FMT_USE_WINDOWS_H
struct
auto_id
{};
/** A Windows error. */
class
windows_error
:
public
system_error
{
private:
FMT_API
void
init
(
int
error_code
,
string_view
format_str
,
args
args
);
// The standard format specifier handler with checking.
template
<
typename
Context
>
class
specs_handler
:
public
specs_setter
<
typename
Context
::
char_type
>
{
public:
typedef
typename
Context
::
char_type
char_type
;
constexpr
specs_handler
(
basic_format_specs
<
char_type
>
&
specs
,
Context
&
ctx
)
:
specs_setter
<
char_type
>
(
specs
),
context_
(
ctx
)
{}
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
template
<
typename
Id
>
constexpr
void
on_dynamic_width
(
Id
arg_id
)
{
set_dynamic_spec
<
width_checker
>
(
this
->
specs_
.
width_
,
get_arg
(
arg_id
));
}
.. parsed-literal::
*<message>*: *<system-message>*
template
<
typename
Id
>
constexpr
void
on_dynamic_precision
(
Id
arg_id
)
{
set_dynamic_spec
<
precision_checker
>
(
this
->
specs_
.
precision_
,
get_arg
(
arg_id
));
}
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
private:
constexpr
basic_arg
<
Context
>
get_arg
(
auto_id
)
{
return
context_
.
next_arg
();
}
**Example**::
template
<
typename
Id
>
constexpr
basic_arg
<
Context
>
get_arg
(
Id
arg_id
)
{
context_
.
check_arg_id
(
arg_id
);
return
context_
.
get_arg
(
arg_id
);
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template
<
typename
...
Args
>
windows_error
(
int
error_code
,
string_view
message
,
const
Args
&
...
args
)
{
init
(
error_code
,
message
,
make_args
(
args
...));
}
Context
&
context_
;
};
//
An argument reference
.
template
<
typename
Char
>
struct
arg_ref
{
enum
Kind
{
NONE
,
INDEX
,
NAME
}
;
//
Reports a Windows error without throwing an exception
.
// Can be used to report errors from destructors.
FMT_API
void
report_windows_error
(
int
error_code
,
string_view
message
)
FMT_NOEXCEPT
;
constexpr
arg_ref
()
:
kind
(
NONE
),
index
(
0
)
{}
constexpr
explicit
arg_ref
(
unsigned
index
)
:
kind
(
INDEX
),
index
(
index
)
{}
explicit
arg_ref
(
basic_string_view
<
Char
>
name
)
:
kind
(
NAME
),
name
(
name
)
{}
#endif
constexpr
arg_ref
&
operator
=
(
unsigned
index
)
{
kind
=
INDEX
;
this
->
index
=
index
;
return
*
this
;
}
enum
Color
{
BLACK
,
RED
,
GREEN
,
YELLOW
,
BLUE
,
MAGENTA
,
CYAN
,
WHITE
};
Kind
kind
;
union
{
unsigned
index
;
basic_string_view
<
Char
>
name
;
};
};
FMT_API
void
vprint_colored
(
Color
c
,
string_view
format
,
args
args
);
// Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow re-using the same parsed specifiers with
// differents 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
;
};
/**
Formats a string and prints it to stdout using ANSI escape sequences
to specify color (experimental).
Example:
print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template
<
typename
...
Args
>
inline
void
print_colored
(
Color
c
,
string_view
format_str
,
const
Args
&
...
args
)
{
vprint_colored
(
c
,
format_str
,
make_args
(
args
...));
}
// 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:
using
char_type
=
typename
ParseContext
::
char_type
;
template
<
typename
ArgFormatter
,
typename
Char
,
typename
Context
>
void
vformat_to
(
basic_buffer
<
Char
>
&
buffer
,
basic_string_view
<
Char
>
format_str
,
basic_args
<
Context
>
args
);
constexpr
dynamic_specs_handler
(
dynamic_format_specs
<
char_type
>
&
specs
,
ParseContext
&
ctx
)
:
specs_setter
<
char_type
>
(
specs
),
specs_
(
specs
),
context_
(
ctx
)
{
}
inline
void
vformat_to
(
buffer
&
buf
,
string_view
format_str
,
args
args
)
{
vformat_to
<
arg_formatter
<
char
>>
(
buf
,
format_str
,
args
);
}
template
<
typename
Id
>
constexpr
void
on_dynamic_width
(
Id
arg_id
)
{
specs_
.
width_ref
=
make_arg_ref
(
arg_id
);
}
inline
void
vformat_to
(
wbuffer
&
buf
,
wstring_view
format_str
,
wargs
args
)
{
vformat_to
<
arg_formatter
<
wchar_t
>>
(
buf
,
format_str
,
args
);
}
template
<
typename
Id
>
constexpr
void
on_dynamic_precision
(
Id
arg_id
)
{
specs_
.
precision_ref
=
make_arg_ref
(
arg_id
);
}
template
<
typename
...
Args
>
inline
void
format_to
(
buffer
&
buf
,
string_view
format_str
,
const
Args
&
...
args
)
{
vformat_to
(
buf
,
format_str
,
make_args
(
args
...));
}
private:
using
arg_ref_type
=
arg_ref
<
char_type
>
;
template
<
typename
...
Args
>
inline
void
format_to
(
wbuffer
&
buf
,
wstring_view
format_str
,
const
Args
&
...
args
)
{
vformat_to
(
buf
,
format_str
,
make_args
<
wcontext
>
(
args
...));
}
template
<
typename
Id
>
constexpr
arg_ref_type
make_arg_ref
(
Id
arg_id
)
{
context_
.
check_arg_id
(
arg_id
);
return
arg_ref_type
(
arg_id
);
}
inline
std
::
string
vformat
(
string_view
format_str
,
args
args
)
{
memory_buffer
buffer
;
vformat_to
(
buffer
,
format_str
,
args
);
return
to_string
(
buffer
);
}
constexpr
arg_ref_type
make_arg_ref
(
auto_id
)
{
const
char
*
error
=
0
;
auto
index
=
context_
.
next_arg_index
(
error
);
if
(
error
)
FMT_THROW
(
format_error
(
error
));
return
arg_ref_type
(
index
);
}
/**
\rst
Formats arguments and returns the result as a string.
dynamic_format_specs
<
char_type
>
&
specs_
;
ParseContext
&
context_
;
};
**Example**::
template
<
typename
Iterator
,
typename
IDHandler
>
constexpr
Iterator
parse_arg_id
(
Iterator
it
,
IDHandler
&&
handler
)
{
using
char_type
=
typename
std
::
iterator_traits
<
Iterator
>::
value_type
;
char_type
c
=
*
it
;
if
(
c
==
'}'
||
c
==
':'
)
{
handler
();
return
it
;
}
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
unsigned
index
=
parse_nonnegative_int
(
it
,
handler
);
if
(
*
it
!=
'}'
&&
*
it
!=
':'
)
{
handler
.
on_error
(
"invalid format string"
);
return
it
;
}
handler
(
index
);
return
it
;
}
if
(
!
is_name_start
(
c
))
{
handler
.
on_error
(
"invalid format string"
);
return
it
;
}
auto
start
=
it
;
do
{
c
=
*++
it
;
}
while
(
is_name_start
(
c
)
||
(
'0'
<=
c
&&
c
<=
'9'
));
handler
(
basic_string_view
<
char_type
>
(
pointer_from
(
start
),
it
-
start
));
return
it
;
std::string message = format("The answer is {}", 42);
\endrst
*/
template
<
typename
...
Args
>
inline
std
::
string
format
(
string_view
format_str
,
const
Args
&
...
args
)
{
return
vformat
(
format_str
,
make_args
(
args
...));
}
// Adapts SpecHandler to IDHandler API for dynamic width.
template
<
typename
SpecHandler
,
typename
Char
>
struct
width_adapter
{
explicit
constexpr
width_adapter
(
SpecHandler
&
h
)
:
handler
(
h
)
{}
template
<
typename
String
,
typename
...
Args
>
inline
typename
std
::
enable_if
<
std
::
is_base_of
<
internal
::
format_string
,
String
>::
value
,
std
::
string
>::
type
format
(
String
format_str
,
const
Args
&
...
args
)
{
constexpr
bool
invalid_format
=
internal
::
check_format_string
<
char
,
Args
...
>
(
string_view
(
format_str
.
value
(),
format_str
.
size
()));
return
vformat
(
format_str
.
value
(),
make_args
(
args
...));
}
constexpr
void
operator
()()
{
handler
.
on_dynamic_width
(
auto_id
());
}
constexpr
void
operator
()(
unsigned
id
)
{
handler
.
on_dynamic_width
(
id
);
}
constexpr
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_dynamic_width
(
id
);
}
inline
std
::
wstring
vformat
(
wstring_view
format_str
,
wargs
args
)
{
wmemory_buffer
buffer
;
vformat_to
(
buffer
,
format_str
,
args
);
return
to_string
(
buffer
);
}
constexpr
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
}
template
<
typename
...
Args
>
inline
std
::
wstring
format
(
wstring_view
format_str
,
const
Args
&
...
args
)
{
return
vformat
(
format_str
,
make_args
<
wcontext
>
(
args
...));
}
SpecHandler
&
handler
;
};
FMT_API
void
vprint
(
std
::
FILE
*
f
,
string_view
format_str
,
args
args
);
// Adapts SpecHandler to IDHandler API for dynamic precision.
template
<
typename
SpecHandler
,
typename
Char
>
struct
precision_adapter
{
explicit
constexpr
precision_adapter
(
SpecHandler
&
h
)
:
handler
(
h
)
{}
/**
\rst
Prints formatted data to the file *f*.
constexpr
void
operator
()()
{
handler
.
on_dynamic_precision
(
auto_id
());
}
constexpr
void
operator
()(
unsigned
id
)
{
handler
.
on_dynamic_precision
(
id
);
}
constexpr
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_dynamic_precision
(
id
);
}
**Example**::
constexpr
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
}
print(stderr, "Don't {}!", "panic");
\endrst
*/
template
<
typename
...
Args
>
inline
void
print
(
std
::
FILE
*
f
,
string_view
format_str
,
const
Args
&
...
args
)
{
vprint
(
f
,
format_str
,
make_args
(
args
...));
}
SpecHandler
&
handler
;
};
FMT_API
void
vprint
(
string_view
format_str
,
args
args
);
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
// it: an iterator pointing to the beginning of a null-terminated range of
// characters, possibly emulated via null_terminating_iterator, representing
// format specifiers.
template
<
typename
Iterator
,
typename
SpecHandler
>
constexpr
Iterator
parse_format_specs
(
Iterator
it
,
SpecHandler
&&
handler
)
{
using
char_type
=
typename
std
::
iterator_traits
<
Iterator
>::
value_type
;
// Parse fill and alignment.
if
(
char_type
c
=
*
it
)
{
alignment
align
=
ALIGN_DEFAULT
;
int
i
=
1
;
do
{
auto
p
=
it
+
i
;
switch
(
*
p
)
{
case
'<'
:
align
=
ALIGN_LEFT
;
break
;
case
'>'
:
align
=
ALIGN_RIGHT
;
break
;
case
'='
:
align
=
ALIGN_NUMERIC
;
break
;
case
'^'
:
align
=
ALIGN_CENTER
;
break
;
}
if
(
align
!=
ALIGN_DEFAULT
)
{
handler
.
on_align
(
align
);
if
(
p
!=
it
)
{
if
(
c
==
'}'
)
break
;
if
(
c
==
'{'
)
{
handler
.
on_error
(
"invalid fill character '{'"
);
return
it
;
}
it
+=
2
;
handler
.
on_fill
(
c
);
}
else
++
it
;
break
;
}
}
while
(
--
i
>=
0
);
}
/**
\rst
Prints formatted data to ``stdout``.
// Parse sign.
switch
(
*
it
)
{
case
'+'
:
handler
.
on_plus
();
++
it
;
break
;
case
'-'
:
handler
.
on_minus
();
++
it
;
break
;
case
' '
:
handler
.
on_space
();
++
it
;
break
;
}
**Example**::
if
(
*
it
==
'#'
)
{
handler
.
on_hash
();
++
it
;
}
print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template
<
typename
...
Args
>
inline
void
print
(
string_view
format_str
,
const
Args
&
...
args
)
{
vprint
(
format_str
,
make_args
(
args
...));
}
// Parse zero flag.
if
(
*
it
==
'0'
)
{
handler
.
on_zero
();
++
it
;
}
/**
Fast integer formatter.
*/
class
FormatInt
{
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
enum
{
BUFFER_SIZE
=
std
::
numeric_limits
<
unsigned
long
long
>::
digits10
+
3
};
mutable
char
buffer_
[
BUFFER_SIZE
];
char
*
str_
;
// Parse width.
if
(
'0'
<=
*
it
&&
*
it
<=
'9'
)
{
handler
.
on_width
(
parse_nonnegative_int
(
it
,
handler
));
}
else
if
(
*
it
==
'{'
)
{
it
=
parse_arg_id
(
it
+
1
,
width_adapter
<
SpecHandler
,
char_type
>
(
handler
));
if
(
*
it
++
!=
'}'
)
{
handler
.
on_error
(
"invalid format string"
);
return
it
;
// Formats value in reverse and returns the number of digits.
char
*
format_decimal
(
unsigned
long
long
value
)
{
char
*
buffer_end
=
buffer_
+
BUFFER_SIZE
-
1
;
while
(
value
>=
100
)
{
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
unsigned
index
=
static_cast
<
unsigned
>
((
value
%
100
)
*
2
);
value
/=
100
;
*--
buffer_end
=
internal
::
data
::
DIGITS
[
index
+
1
];
*--
buffer_end
=
internal
::
data
::
DIGITS
[
index
];
}
}
// Parse precision.
if
(
*
it
==
'.'
)
{
++
it
;
if
(
'0'
<=
*
it
&&
*
it
<=
'9'
)
{
handler
.
on_precision
(
parse_nonnegative_int
(
it
,
handler
));
}
else
if
(
*
it
==
'{'
)
{
it
=
parse_arg_id
(
it
+
1
,
precision_adapter
<
SpecHandler
,
char_type
>
(
handler
));
if
(
*
it
++
!=
'}'
)
{
handler
.
on_error
(
"invalid format string"
);
return
it
;
}
}
else
{
handler
.
on_error
(
"missing precision specifier"
);
return
it
;
if
(
value
<
10
)
{
*--
buffer_end
=
static_cast
<
char
>
(
'0'
+
value
);
return
buffer_end
;
}
handler
.
end_precision
();
unsigned
index
=
static_cast
<
unsigned
>
(
value
*
2
);
*--
buffer_end
=
internal
::
data
::
DIGITS
[
index
+
1
];
*--
buffer_end
=
internal
::
data
::
DIGITS
[
index
];
return
buffer_end
;
}
// Parse type.
if
(
*
it
!=
'}'
&&
*
it
)
handler
.
on_type
(
*
it
++
);
return
it
;
}
void
FormatSigned
(
long
long
value
)
{
unsigned
long
long
abs_value
=
static_cast
<
unsigned
long
long
>
(
value
);
bool
negative
=
value
<
0
;
if
(
negative
)
abs_value
=
0
-
abs_value
;
str_
=
format_decimal
(
abs_value
);
if
(
negative
)
*--
str_
=
'-'
;
}
template
<
typename
Handler
,
typename
Char
>
struct
id_adapter
{
constexpr
explicit
id_adapter
(
Handler
&
h
)
:
handler
(
h
)
{}
public:
explicit
FormatInt
(
int
value
)
{
FormatSigned
(
value
);
}
explicit
FormatInt
(
long
value
)
{
FormatSigned
(
value
);
}
explicit
FormatInt
(
long
long
value
)
{
FormatSigned
(
value
);
}
explicit
FormatInt
(
unsigned
value
)
:
str_
(
format_decimal
(
value
))
{}
explicit
FormatInt
(
unsigned
long
value
)
:
str_
(
format_decimal
(
value
))
{}
explicit
FormatInt
(
unsigned
long
long
value
)
:
str_
(
format_decimal
(
value
))
{}
constexpr
void
operator
()()
{
handler
.
on_arg_id
();
}
constexpr
void
operator
()(
unsigned
id
)
{
handler
.
on_arg_id
(
id
);
}
constexpr
void
operator
()(
basic_string_view
<
Char
>
id
)
{
handler
.
on_arg_id
(
id
);
/** Returns the number of characters written to the output buffer. */
std
::
size_t
size
()
const
{
return
internal
::
to_unsigned
(
buffer_
-
str_
+
BUFFER_SIZE
-
1
);
}
constexpr
void
on_error
(
const
char
*
message
)
{
handler
.
on_error
(
message
);
/**
Returns a pointer to the output buffer content. No terminating null
character is appended.
*/
const
char
*
data
()
const
{
return
str_
;
}
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const
char
*
c_str
()
const
{
buffer_
[
BUFFER_SIZE
-
1
]
=
'\0'
;
return
str_
;
}
Handler
&
handler
;
/**
\rst
Returns the content of the output buffer as an ``std::string``.
\endrst
*/
std
::
string
str
()
const
{
return
std
::
string
(
str_
,
size
());
}
};
template
<
typename
Iterator
,
typename
Handler
>
constexpr
void
parse_format_string
(
Iterator
it
,
Handler
&&
handler
)
{
using
char_type
=
typename
std
::
iterator_traits
<
Iterator
>::
value_type
;
auto
start
=
it
;
while
(
*
it
)
{
char_type
ch
=
*
it
++
;
if
(
ch
!=
'{'
&&
ch
!=
'}'
)
continue
;
if
(
*
it
==
ch
)
{
handler
.
on_text
(
start
,
it
)
;
start
=
++
it
;
continue
;
}
if
(
ch
==
'}'
)
{
handler
.
on_error
(
"unmatched '}' in format string"
);
// Formats a decimal integer value writing into buffer and returns
// a pointer to the end of the formatted string. This function doesn't
// write a terminating null character.
template
<
typename
T
>
inline
void
format_decimal
(
char
*&
buffer
,
T
value
)
{
typedef
typename
internal
::
int_traits
<
T
>::
main_type
main_type
;
main_type
abs_value
=
static_cast
<
main_type
>
(
value
)
;
if
(
internal
::
is_negative
(
value
)
)
{
*
buffer
++
=
'-'
;
abs_value
=
0
-
abs_value
;
}
if
(
abs_value
<
100
)
{
if
(
abs_value
<
10
)
{
*
buffer
++
=
static_cast
<
char
>
(
'0'
+
abs_value
);
return
;
}
handler
.
on_text
(
start
,
it
-
1
);
it
=
parse_arg_id
(
it
,
id_adapter
<
Handler
,
char_type
>
(
handler
));
if
(
*
it
==
'}'
)
{
handler
.
on_replacement_field
(
it
);
}
else
if
(
*
it
==
':'
)
{
++
it
;
it
=
handler
.
on_format_specs
(
it
);
if
(
*
it
!=
'}'
)
handler
.
on_error
(
"unknown format specifier"
);
}
else
{
handler
.
on_error
(
"missing '}' in format string"
);
}
start
=
++
it
;
unsigned
index
=
static_cast
<
unsigned
>
(
abs_value
*
2
);
*
buffer
++
=
internal
::
data
::
DIGITS
[
index
];
*
buffer
++
=
internal
::
data
::
DIGITS
[
index
+
1
];
return
;
}
handler
.
on_text
(
start
,
it
);
unsigned
num_digits
=
internal
::
count_digits
(
abs_value
);
internal
::
format_decimal
(
buffer
,
abs_value
,
num_digits
);
buffer
+=
num_digits
;
}
// Specifies whether to format T using the standard formatter.
// It is not possible to use get_type in formatter specialization directly
// because of a bug in MSVC.
template
<
typename
T
>
struct
format_type
:
std
::
integral_constant
<
bool
,
get_type
<
T
>
()
!=
CUSTOM
>
{};
/**
\rst
Returns a named argument for formatting functions.
// Specifies whether to format enums.
template
<
typename
T
,
typename
Enable
=
void
>
struct
format_enum
:
std
::
integral_constant
<
bool
,
std
::
is_enum
<
T
>::
value
>
{};
**Example**::
template
<
template
<
typename
>
class
Handler
,
typename
Spec
,
typename
Char
>
void
handle_dynamic_spec
(
Spec
&
value
,
arg_ref
<
Char
>
ref
,
basic_context
<
Char
>
&
ctx
)
{
switch
(
ref
.
kind
)
{
case
arg_ref
<
Char
>
:
:
NONE
:
break
;
case
arg_ref
<
Char
>
:
:
INDEX
:
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
get_arg
(
ref
.
index
));
break
;
case
arg_ref
<
Char
>
:
:
NAME
:
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
get_arg
(
ref
.
name
));
break
;
}
print("Elapsed time: {s:.2f} seconds", arg("s", 1.23));
\endrst
*/
template
<
typename
T
>
inline
internal
::
named_arg
<
context
>
arg
(
string_view
name
,
const
T
&
arg
)
{
return
internal
::
named_arg
<
context
>
(
name
,
arg
);
}
}
// namespace internal
template
<
typename
T
>
inline
internal
::
named_arg
<
wcontext
>
arg
(
wstring_view
name
,
const
T
&
arg
)
{
return
internal
::
named_arg
<
wcontext
>
(
name
,
arg
);
}
// The following two functions are deleted intentionally to disable
// nested named arguments as in ``format("{}", arg("a", arg("b", 42)))``.
template
<
typename
Context
>
void
arg
(
string_view
,
const
internal
::
named_arg
<
Context
>&
)
FMT_DELETED_OR_UNDEFINED
;
template
<
typename
Context
>
void
arg
(
wstring_view
,
const
internal
::
named_arg
<
Context
>&
)
FMT_DELETED_OR_UNDEFINED
;
// Formatter of objects of type T.
template
<
typename
T
,
typename
Char
>
...
...
@@ -3849,73 +3912,17 @@ inline const void *ptr(const T *p) { return p; }
namespace
fmt
{
namespace
internal
{
template
<
typename
Char
,
typename
T
>
constexpr
const
Char
*
parse_format_specs
(
parse_context
<
Char
>
&
ctx
)
{
formatter
<
T
,
Char
>
f
;
return
f
.
parse
(
ctx
);
}
# if FMT_UDL_TEMPLATE
template
<
typename
Char
,
typename
...
Args
>
struct
udl_format_handler
{
public:
explicit
constexpr
udl_format_handler
(
const
Char
*
end
)
:
end_
(
end
)
{}
constexpr
void
on_text
(
const
Char
*
,
const
Char
*
)
{}
constexpr
void
on_arg_id
()
{
++
arg_index_
;
check_arg_index
();
}
constexpr
void
on_arg_id
(
unsigned
index
)
{
arg_index_
=
index
;
check_arg_index
();
}
constexpr
void
on_arg_id
(
basic_string_view
<
Char
>
)
{}
constexpr
void
on_replacement_field
(
const
Char
*
)
{}
constexpr
const
Char
*
on_format_specs
(
const
Char
*
s
)
{
parse_context
<
Char
>
ctx
(
basic_string_view
<
Char
>
(
s
,
end_
-
s
));
return
parse_funcs_
[
arg_index_
](
ctx
);
}
// This function is intentionally not constexpr to give a compile-time error.
void
on_error
(
const
char
*
);
private:
constexpr
void
check_arg_index
()
{
if
(
arg_index_
<
0
||
arg_index_
>=
sizeof
...(
Args
))
on_error
(
"argument index out of range"
);
}
// Format specifier parsing function.
using
parse_func
=
const
Char
*
(
*
)(
parse_context
<
Char
>
&
);
const
Char
*
end_
;
int
arg_index_
=
-
1
;
parse_func
parse_funcs_
[
sizeof
...(
Args
)]
=
{
&
parse_format_specs
<
Char
,
Args
>
...
};
};
template
<
typename
Char
,
Char
...
CHARS
>
class
udl_formatter
{
public:
template
<
typename
...
Args
>
std
::
basic_string
<
Char
>
operator
()(
const
Args
&
...
args
)
const
{
constexpr
Char
s
[]
=
{
CHARS
...,
'\0'
};
constexpr
bool
invalid_format
=
check_format
<
Args
...
>
(
s
);
constexpr
bool
invalid_format
=
check_format_string
<
Char
,
Args
...
>
(
basic_string_view
<
Char
>
(
s
,
sizeof
...(
CHARS
)));
return
format
(
s
,
args
...);
}
private:
template
<
typename
...
Args
>
static
constexpr
bool
check_format
(
const
Char
*
s
)
{
udl_format_handler
<
Char
,
Args
...
>
handler
(
s
+
sizeof
...(
CHARS
));
internal
::
parse_format_string
(
s
,
handler
);
return
true
;
}
};
# else
template
<
typename
Char
>
...
...
@@ -3984,6 +3991,13 @@ operator"" _a(const wchar_t *s, std::size_t) { return {s}; }
}
// namespace fmt
#endif // FMT_USE_USER_DEFINED_LITERALS
#define FMT_STRING(s) [] { \
struct S : fmt::internal::format_string { \
static constexpr auto value() { return s; } \
static constexpr size_t size() { return sizeof(s); } \
}; \
return S{}; \
}()
#ifdef FMT_HEADER_ONLY
# define FMT_FUNC inline
...
...
test/format-test.cc
View file @
246bdafc
...
...
@@ -1815,4 +1815,5 @@ TEST(FormatTest, ConstexprParseFormatString) {
TEST
(
FormatTest
,
UdlTemplate
)
{
EXPECT_EQ
(
"foo"
,
"foo"
_format
());
EXPECT_EQ
(
" 42"
,
"{0:10}"
_format
(
42
));
EXPECT_EQ
(
"42"
,
fmt
::
format
(
FMT_STRING
(
"{}"
),
42
));
}
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