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
72d51e0b
Commit
72d51e0b
authored
Jun 08, 2016
by
Glen Stark
Committed by
Victor Zverovich
Jun 09, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implemented #335: custom printf support
parent
6ccb5673
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
573 additions
and
472 deletions
+573
-472
fmt/CMakeLists.txt
fmt/CMakeLists.txt
+2
-1
fmt/format.cc
fmt/format.cc
+1
-392
fmt/format.h
fmt/format.h
+4
-78
fmt/ostream.cc
fmt/ostream.cc
+1
-0
fmt/printf.h
fmt/printf.h
+497
-0
test/CMakeLists.txt
test/CMakeLists.txt
+1
-0
test/custom-formatter-test.cc
test/custom-formatter-test.cc
+65
-0
test/format-impl-test.cc
test/format-impl-test.cc
+1
-1
test/printf-test.cc
test/printf-test.cc
+1
-0
No files found.
fmt/CMakeLists.txt
View file @
72d51e0b
# Define the fmt library, its includes and the needed defines.
# format.cc is added to FMT_HEADERS for the header-only configuration.
set
(
FMT_HEADERS format.h format.cc ostream.h ostream.cc string.h time.h
)
set
(
FMT_HEADERS format.h format.cc ostream.h ostream.cc printf.h
string.h time.h
)
if
(
HAVE_OPEN
)
set
(
FMT_HEADERS
${
FMT_HEADERS
}
posix.h
)
set
(
FMT_SOURCES
${
FMT_SOURCES
}
posix.cc
)
...
...
fmt/format.cc
View file @
72d51e0b
...
...
@@ -26,6 +26,7 @@
*/
#include "fmt/format.h"
#include "fmt/printf.h"
#include <string.h>
...
...
@@ -100,27 +101,6 @@ inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template
<
bool
IsSigned
>
struct
IntChecker
{
template
<
typename
T
>
static
bool
fits_in_int
(
T
value
)
{
unsigned
max
=
INT_MAX
;
return
value
<=
max
;
}
static
bool
fits_in_int
(
bool
)
{
return
true
;
}
};
template
<
>
struct
IntChecker
<
true
>
{
template
<
typename
T
>
static
bool
fits_in_int
(
T
value
)
{
return
value
>=
INT_MIN
&&
value
<=
INT_MAX
;
}
static
bool
fits_in_int
(
int
)
{
return
true
;
}
};
const
char
RESET_COLOR
[]
=
"
\x1b
[0m"
;
typedef
void
(
*
FormatFunc
)(
Writer
&
,
int
,
StringRef
);
...
...
@@ -225,136 +205,6 @@ void report_error(FormatFunc func, int error_code,
std
::
fwrite
(
full_message
.
data
(),
full_message
.
size
(),
1
,
stderr
);
std
::
fputc
(
'\n'
,
stderr
);
}
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class
IsZeroInt
:
public
ArgVisitor
<
IsZeroInt
,
bool
>
{
public:
template
<
typename
T
>
bool
visit_any_int
(
T
value
)
{
return
value
==
0
;
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class
WidthHandler
:
public
ArgVisitor
<
WidthHandler
,
unsigned
>
{
private:
FormatSpec
&
spec_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
WidthHandler
);
public:
explicit
WidthHandler
(
FormatSpec
&
spec
)
:
spec_
(
spec
)
{}
void
report_unhandled_arg
()
{
FMT_THROW
(
FormatError
(
"width is not integer"
));
}
template
<
typename
T
>
unsigned
visit_any_int
(
T
value
)
{
typedef
typename
internal
::
IntTraits
<
T
>::
MainType
UnsignedType
;
UnsignedType
width
=
static_cast
<
UnsignedType
>
(
value
);
if
(
internal
::
is_negative
(
value
))
{
spec_
.
align_
=
ALIGN_LEFT
;
width
=
0
-
width
;
}
if
(
width
>
INT_MAX
)
FMT_THROW
(
FormatError
(
"number is too big"
));
return
static_cast
<
unsigned
>
(
width
);
}
};
class
PrecisionHandler
:
public
ArgVisitor
<
PrecisionHandler
,
int
>
{
public:
void
report_unhandled_arg
()
{
FMT_THROW
(
FormatError
(
"precision is not integer"
));
}
template
<
typename
T
>
int
visit_any_int
(
T
value
)
{
if
(
!
IntChecker
<
std
::
numeric_limits
<
T
>::
is_signed
>::
fits_in_int
(
value
))
FMT_THROW
(
FormatError
(
"number is too big"
));
return
static_cast
<
int
>
(
value
);
}
};
template
<
typename
T
,
typename
U
>
struct
is_same
{
enum
{
value
=
0
};
};
template
<
typename
T
>
struct
is_same
<
T
,
T
>
{
enum
{
value
=
1
};
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template
<
typename
T
=
void
>
class
ArgConverter
:
public
ArgVisitor
<
ArgConverter
<
T
>
,
void
>
{
private:
internal
::
Arg
&
arg_
;
wchar_t
type_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
ArgConverter
);
public:
ArgConverter
(
internal
::
Arg
&
arg
,
wchar_t
type
)
:
arg_
(
arg
),
type_
(
type
)
{}
void
visit_bool
(
bool
value
)
{
if
(
type_
!=
's'
)
visit_any_int
(
value
);
}
template
<
typename
U
>
void
visit_any_int
(
U
value
)
{
bool
is_signed
=
type_
==
'd'
||
type_
==
'i'
;
using
internal
::
Arg
;
typedef
typename
internal
::
Conditional
<
is_same
<
T
,
void
>::
value
,
U
,
T
>::
type
TargetType
;
if
(
sizeof
(
TargetType
)
<=
sizeof
(
int
))
{
// Extra casts are used to silence warnings.
if
(
is_signed
)
{
arg_
.
type
=
Arg
::
INT
;
arg_
.
int_value
=
static_cast
<
int
>
(
static_cast
<
TargetType
>
(
value
));
}
else
{
arg_
.
type
=
Arg
::
UINT
;
typedef
typename
internal
::
MakeUnsigned
<
TargetType
>::
Type
Unsigned
;
arg_
.
uint_value
=
static_cast
<
unsigned
>
(
static_cast
<
Unsigned
>
(
value
));
}
}
else
{
if
(
is_signed
)
{
arg_
.
type
=
Arg
::
LONG_LONG
;
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_
.
long_long_value
=
static_cast
<
LongLong
>
(
value
);
}
else
{
arg_
.
type
=
Arg
::
ULONG_LONG
;
arg_
.
ulong_long_value
=
static_cast
<
typename
internal
::
MakeUnsigned
<
U
>::
Type
>
(
value
);
}
}
}
};
// Converts an integer argument to char for printf.
class
CharConverter
:
public
ArgVisitor
<
CharConverter
,
void
>
{
private:
internal
::
Arg
&
arg_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
CharConverter
);
public:
explicit
CharConverter
(
internal
::
Arg
&
arg
)
:
arg_
(
arg
)
{}
template
<
typename
T
>
void
visit_any_int
(
T
value
)
{
arg_
.
type
=
internal
::
Arg
::
CHAR
;
arg_
.
int_value
=
static_cast
<
char
>
(
value
);
}
};
}
// namespace
namespace
internal
{
...
...
@@ -365,75 +215,6 @@ FMT_FUNC void format_system_error(
Writer
&
out
,
int
error_code
,
StringRef
message
)
FMT_NOEXCEPT
{
fmt
::
format_system_error
(
out
,
error_code
,
message
);
}
template
<
typename
Char
>
class
PrintfArgFormatter
:
public
ArgFormatterBase
<
PrintfArgFormatter
<
Char
>
,
Char
>
{
void
write_null_pointer
()
{
this
->
spec
().
type_
=
0
;
this
->
write
(
"(nil)"
);
}
typedef
ArgFormatterBase
<
PrintfArgFormatter
<
Char
>
,
Char
>
Base
;
public:
PrintfArgFormatter
(
BasicWriter
<
Char
>
&
w
,
FormatSpec
&
s
)
:
ArgFormatterBase
<
PrintfArgFormatter
<
Char
>
,
Char
>
(
w
,
s
)
{}
void
visit_bool
(
bool
value
)
{
FormatSpec
&
fmt_spec
=
this
->
spec
();
if
(
fmt_spec
.
type_
!=
's'
)
return
this
->
visit_any_int
(
value
);
fmt_spec
.
type_
=
0
;
this
->
write
(
value
);
}
void
visit_char
(
int
value
)
{
const
FormatSpec
&
fmt_spec
=
this
->
spec
();
BasicWriter
<
Char
>
&
w
=
this
->
writer
();
if
(
fmt_spec
.
type_
&&
fmt_spec
.
type_
!=
'c'
)
w
.
write_int
(
value
,
fmt_spec
);
typedef
typename
BasicWriter
<
Char
>::
CharPtr
CharPtr
;
CharPtr
out
=
CharPtr
();
if
(
fmt_spec
.
width_
>
1
)
{
Char
fill
=
' '
;
out
=
w
.
grow_buffer
(
fmt_spec
.
width_
);
if
(
fmt_spec
.
align_
!=
ALIGN_LEFT
)
{
std
::
fill_n
(
out
,
fmt_spec
.
width_
-
1
,
fill
);
out
+=
fmt_spec
.
width_
-
1
;
}
else
{
std
::
fill_n
(
out
+
1
,
fmt_spec
.
width_
-
1
,
fill
);
}
}
else
{
out
=
w
.
grow_buffer
(
1
);
}
*
out
=
static_cast
<
Char
>
(
value
);
}
void
visit_cstring
(
const
char
*
value
)
{
if
(
value
)
Base
::
visit_cstring
(
value
);
else
if
(
this
->
spec
().
type_
==
'p'
)
write_null_pointer
();
else
this
->
write
(
"(null)"
);
}
void
visit_pointer
(
const
void
*
value
)
{
if
(
value
)
return
Base
::
visit_pointer
(
value
);
this
->
spec
().
type_
=
0
;
write_null_pointer
();
}
void
visit_custom
(
Arg
::
CustomValue
c
)
{
BasicFormatter
<
Char
>
formatter
(
ArgList
(),
this
->
writer
());
const
Char
format_str
[]
=
{
'}'
,
0
};
const
Char
*
format
=
format_str
;
c
.
format
(
&
formatter
,
c
.
value
,
&
format
);
}
};
}
// namespace internal
}
// namespace fmt
...
...
@@ -686,178 +467,6 @@ FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg(
return
arg
;
}
template
<
typename
Char
>
void
fmt
::
internal
::
PrintfFormatter
<
Char
>::
parse_flags
(
FormatSpec
&
spec
,
const
Char
*&
s
)
{
for
(;;)
{
switch
(
*
s
++
)
{
case
'-'
:
spec
.
align_
=
ALIGN_LEFT
;
break
;
case
'+'
:
spec
.
flags_
|=
SIGN_FLAG
|
PLUS_FLAG
;
break
;
case
'0'
:
spec
.
fill_
=
'0'
;
break
;
case
' '
:
spec
.
flags_
|=
SIGN_FLAG
;
break
;
case
'#'
:
spec
.
flags_
|=
HASH_FLAG
;
break
;
default:
--
s
;
return
;
}
}
}
template
<
typename
Char
>
Arg
fmt
::
internal
::
PrintfFormatter
<
Char
>::
get_arg
(
const
Char
*
s
,
unsigned
arg_index
)
{
(
void
)
s
;
const
char
*
error
=
0
;
Arg
arg
=
arg_index
==
UINT_MAX
?
next_arg
(
error
)
:
FormatterBase
::
get_arg
(
arg_index
-
1
,
error
);
if
(
error
)
FMT_THROW
(
FormatError
(
!*
s
?
"invalid format string"
:
error
));
return
arg
;
}
template
<
typename
Char
>
unsigned
fmt
::
internal
::
PrintfFormatter
<
Char
>::
parse_header
(
const
Char
*&
s
,
FormatSpec
&
spec
)
{
unsigned
arg_index
=
UINT_MAX
;
Char
c
=
*
s
;
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned
value
=
parse_nonnegative_int
(
s
);
if
(
*
s
==
'$'
)
{
// value is an argument index
++
s
;
arg_index
=
value
;
}
else
{
if
(
c
==
'0'
)
spec
.
fill_
=
'0'
;
if
(
value
!=
0
)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec
.
width_
=
value
;
return
arg_index
;
}
}
}
parse_flags
(
spec
,
s
);
// Parse width.
if
(
*
s
>=
'0'
&&
*
s
<=
'9'
)
{
spec
.
width_
=
parse_nonnegative_int
(
s
);
}
else
if
(
*
s
==
'*'
)
{
++
s
;
spec
.
width_
=
WidthHandler
(
spec
).
visit
(
get_arg
(
s
));
}
return
arg_index
;
}
template
<
typename
Char
>
void
fmt
::
internal
::
PrintfFormatter
<
Char
>::
format
(
BasicWriter
<
Char
>
&
writer
,
BasicCStringRef
<
Char
>
format_str
)
{
const
Char
*
start
=
format_str
.
c_str
();
const
Char
*
s
=
start
;
while
(
*
s
)
{
Char
c
=
*
s
++
;
if
(
c
!=
'%'
)
continue
;
if
(
*
s
==
c
)
{
write
(
writer
,
start
,
s
);
start
=
++
s
;
continue
;
}
write
(
writer
,
start
,
s
-
1
);
FormatSpec
spec
;
spec
.
align_
=
ALIGN_RIGHT
;
// Parse argument index, flags and width.
unsigned
arg_index
=
parse_header
(
s
,
spec
);
// Parse precision.
if
(
*
s
==
'.'
)
{
++
s
;
if
(
'0'
<=
*
s
&&
*
s
<=
'9'
)
{
spec
.
precision_
=
static_cast
<
int
>
(
parse_nonnegative_int
(
s
));
}
else
if
(
*
s
==
'*'
)
{
++
s
;
spec
.
precision_
=
PrecisionHandler
().
visit
(
get_arg
(
s
));
}
}
Arg
arg
=
get_arg
(
s
,
arg_index
);
if
(
spec
.
flag
(
HASH_FLAG
)
&&
IsZeroInt
().
visit
(
arg
))
spec
.
flags_
&=
~
to_unsigned
<
int
>
(
HASH_FLAG
);
if
(
spec
.
fill_
==
'0'
)
{
if
(
arg
.
type
<=
Arg
::
LAST_NUMERIC_TYPE
)
spec
.
align_
=
ALIGN_NUMERIC
;
else
spec
.
fill_
=
' '
;
// Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
switch
(
*
s
++
)
{
case
'h'
:
if
(
*
s
==
'h'
)
ArgConverter
<
signed
char
>
(
arg
,
*++
s
).
visit
(
arg
);
else
ArgConverter
<
short
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
'l'
:
if
(
*
s
==
'l'
)
ArgConverter
<
fmt
::
LongLong
>
(
arg
,
*++
s
).
visit
(
arg
);
else
ArgConverter
<
long
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
'j'
:
ArgConverter
<
intmax_t
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
'z'
:
ArgConverter
<
std
::
size_t
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
't'
:
ArgConverter
<
std
::
ptrdiff_t
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
'L'
:
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break
;
default:
--
s
;
ArgConverter
<
void
>
(
arg
,
*
s
).
visit
(
arg
);
}
// Parse type.
if
(
!*
s
)
FMT_THROW
(
FormatError
(
"invalid format string"
));
spec
.
type_
=
static_cast
<
char
>
(
*
s
++
);
if
(
arg
.
type
<=
Arg
::
LAST_INTEGER_TYPE
)
{
// Normalize type.
switch
(
spec
.
type_
)
{
case
'i'
:
case
'u'
:
spec
.
type_
=
'd'
;
break
;
case
'c'
:
// TODO: handle wchar_t
CharConverter
(
arg
).
visit
(
arg
);
break
;
}
}
start
=
s
;
// Format argument.
internal
::
PrintfArgFormatter
<
Char
>
(
writer
,
spec
).
visit
(
arg
);
}
write
(
writer
,
start
,
s
);
}
FMT_FUNC
void
fmt
::
report_system_error
(
int
error_code
,
fmt
::
StringRef
message
)
FMT_NOEXCEPT
{
// 'fmt::' is for bcc32.
...
...
fmt/format.h
View file @
72d51e0b
...
...
@@ -1318,8 +1318,8 @@ class RuntimeError : public std::runtime_error {
RuntimeError
()
:
std
::
runtime_error
(
""
)
{}
};
template
<
typename
Char
>
class
PrintfArgFormatter
;
template
<
typename
Impl
,
typename
Char
>
class
Basic
PrintfArgFormatter
;
template
<
typename
Char
>
class
ArgMap
;
...
...
@@ -1937,26 +1937,6 @@ class FormatterBase {
w
<<
BasicStringRef
<
Char
>
(
start
,
internal
::
to_unsigned
(
end
-
start
));
}
};
// A printf formatter.
template
<
typename
Char
>
class
PrintfFormatter
:
private
FormatterBase
{
private:
void
parse_flags
(
FormatSpec
&
spec
,
const
Char
*&
s
);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
Arg
get_arg
(
const
Char
*
s
,
unsigned
arg_index
=
(
std
::
numeric_limits
<
unsigned
>::
max
)());
// Parses argument index, flags and width and returns the argument index.
unsigned
parse_header
(
const
Char
*&
s
,
FormatSpec
&
spec
);
public:
explicit
PrintfFormatter
(
const
ArgList
&
args
)
:
FormatterBase
(
args
)
{}
FMT_API
void
format
(
BasicWriter
<
Char
>
&
writer
,
BasicCStringRef
<
Char
>
format_str
);
};
}
// namespace internal
/**
...
...
@@ -2420,7 +2400,8 @@ class BasicWriter {
template
<
typename
Impl
,
typename
Char_
>
friend
class
internal
::
ArgFormatterBase
;
friend
class
internal
::
PrintfArgFormatter
<
Char
>
;
template
<
typename
Impl
,
typename
Char_
>
friend
class
internal
::
BasicPrintfArgFormatter
;
protected:
/**
...
...
@@ -3187,56 +3168,6 @@ FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args);
*/
FMT_API
void
print
(
CStringRef
format_str
,
ArgList
args
);
template
<
typename
Char
>
void
printf
(
BasicWriter
<
Char
>
&
w
,
BasicCStringRef
<
Char
>
format
,
ArgList
args
)
{
internal
::
PrintfFormatter
<
Char
>
(
args
).
format
(
w
,
format
);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline
std
::
string
sprintf
(
CStringRef
format
,
ArgList
args
)
{
MemoryWriter
w
;
printf
(
w
,
format
,
args
);
return
w
.
str
();
}
inline
std
::
wstring
sprintf
(
WCStringRef
format
,
ArgList
args
)
{
WMemoryWriter
w
;
printf
(
w
,
format
,
args
);
return
w
.
str
();
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API
int
fprintf
(
std
::
FILE
*
f
,
CStringRef
format
,
ArgList
args
);
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline
int
printf
(
CStringRef
format
,
ArgList
args
)
{
return
fprintf
(
stdout
,
format
,
args
);
}
/**
Fast integer formatter.
*/
...
...
@@ -3502,12 +3433,7 @@ FMT_VARIADIC(std::string, format, CStringRef)
FMT_VARIADIC_W
(
std
::
wstring
,
format
,
WCStringRef
)
FMT_VARIADIC
(
void
,
print
,
CStringRef
)
FMT_VARIADIC
(
void
,
print
,
std
::
FILE
*
,
CStringRef
)
FMT_VARIADIC
(
void
,
print_colored
,
Color
,
CStringRef
)
FMT_VARIADIC
(
std
::
string
,
sprintf
,
CStringRef
)
FMT_VARIADIC_W
(
std
::
wstring
,
sprintf
,
WCStringRef
)
FMT_VARIADIC
(
int
,
printf
,
CStringRef
)
FMT_VARIADIC
(
int
,
fprintf
,
std
::
FILE
*
,
CStringRef
)
namespace
internal
{
template
<
typename
Char
>
...
...
fmt/ostream.cc
View file @
72d51e0b
...
...
@@ -26,6 +26,7 @@
*/
#include "fmt/ostream.h"
#include "fmt/printf.h"
namespace
fmt
{
...
...
fmt/printf.h
0 → 100644
View file @
72d51e0b
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "fmt/format.h"
namespace
fmt
{
namespace
internal
{
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template
<
bool
IsSigned
>
struct
IntChecker
{
template
<
typename
T
>
static
bool
fits_in_int
(
T
value
)
{
unsigned
max
=
std
::
numeric_limits
<
int
>::
max
();
return
value
<=
max
;
}
static
bool
fits_in_int
(
bool
)
{
return
true
;
}
};
template
<
>
struct
IntChecker
<
true
>
{
template
<
typename
T
>
static
bool
fits_in_int
(
T
value
)
{
return
value
>=
std
::
numeric_limits
<
int
>::
min
()
&&
value
<=
std
::
numeric_limits
<
int
>::
max
();
}
static
bool
fits_in_int
(
int
)
{
return
true
;
}
};
class
PrecisionHandler
:
public
ArgVisitor
<
PrecisionHandler
,
int
>
{
public:
void
report_unhandled_arg
()
{
FMT_THROW
(
FormatError
(
"precision is not integer"
));
}
template
<
typename
T
>
int
visit_any_int
(
T
value
)
{
if
(
!
IntChecker
<
std
::
numeric_limits
<
T
>::
is_signed
>::
fits_in_int
(
value
))
FMT_THROW
(
FormatError
(
"number is too big"
));
return
static_cast
<
int
>
(
value
);
}
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class
IsZeroInt
:
public
ArgVisitor
<
IsZeroInt
,
bool
>
{
public:
template
<
typename
T
>
bool
visit_any_int
(
T
value
)
{
return
value
==
0
;
}
};
template
<
typename
T
,
typename
U
>
struct
is_same
{
enum
{
value
=
0
};
};
template
<
typename
T
>
struct
is_same
<
T
,
T
>
{
enum
{
value
=
1
};
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template
<
typename
T
=
void
>
class
ArgConverter
:
public
ArgVisitor
<
ArgConverter
<
T
>
,
void
>
{
private:
internal
::
Arg
&
arg_
;
wchar_t
type_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
ArgConverter
);
public:
ArgConverter
(
internal
::
Arg
&
arg
,
wchar_t
type
)
:
arg_
(
arg
),
type_
(
type
)
{}
void
visit_bool
(
bool
value
)
{
if
(
type_
!=
's'
)
visit_any_int
(
value
);
}
template
<
typename
U
>
void
visit_any_int
(
U
value
)
{
bool
is_signed
=
type_
==
'd'
||
type_
==
'i'
;
using
internal
::
Arg
;
typedef
typename
internal
::
Conditional
<
is_same
<
T
,
void
>::
value
,
U
,
T
>::
type
TargetType
;
if
(
sizeof
(
TargetType
)
<=
sizeof
(
int
))
{
// Extra casts are used to silence warnings.
if
(
is_signed
)
{
arg_
.
type
=
Arg
::
INT
;
arg_
.
int_value
=
static_cast
<
int
>
(
static_cast
<
TargetType
>
(
value
));
}
else
{
arg_
.
type
=
Arg
::
UINT
;
typedef
typename
internal
::
MakeUnsigned
<
TargetType
>::
Type
Unsigned
;
arg_
.
uint_value
=
static_cast
<
unsigned
>
(
static_cast
<
Unsigned
>
(
value
));
}
}
else
{
if
(
is_signed
)
{
arg_
.
type
=
Arg
::
LONG_LONG
;
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_
.
long_long_value
=
static_cast
<
LongLong
>
(
value
);
}
else
{
arg_
.
type
=
Arg
::
ULONG_LONG
;
arg_
.
ulong_long_value
=
static_cast
<
typename
internal
::
MakeUnsigned
<
U
>::
Type
>
(
value
);
}
}
}
};
// Converts an integer argument to char for printf.
class
CharConverter
:
public
ArgVisitor
<
CharConverter
,
void
>
{
private:
internal
::
Arg
&
arg_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
CharConverter
);
public:
explicit
CharConverter
(
internal
::
Arg
&
arg
)
:
arg_
(
arg
)
{}
template
<
typename
T
>
void
visit_any_int
(
T
value
)
{
arg_
.
type
=
internal
::
Arg
::
CHAR
;
arg_
.
int_value
=
static_cast
<
char
>
(
value
);
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class
WidthHandler
:
public
ArgVisitor
<
WidthHandler
,
unsigned
>
{
private:
FormatSpec
&
spec_
;
FMT_DISALLOW_COPY_AND_ASSIGN
(
WidthHandler
);
public:
explicit
WidthHandler
(
FormatSpec
&
spec
)
:
spec_
(
spec
)
{}
void
report_unhandled_arg
()
{
FMT_THROW
(
FormatError
(
"width is not integer"
));
}
template
<
typename
T
>
unsigned
visit_any_int
(
T
value
)
{
typedef
typename
internal
::
IntTraits
<
T
>::
MainType
UnsignedType
;
UnsignedType
width
=
static_cast
<
UnsignedType
>
(
value
);
if
(
internal
::
is_negative
(
value
))
{
spec_
.
align_
=
ALIGN_LEFT
;
width
=
0
-
width
;
}
if
(
width
>
std
::
numeric_limits
<
int
>::
max
())
FMT_THROW
(
FormatError
(
"number is too big"
));
return
static_cast
<
unsigned
>
(
width
);
}
};
template
<
typename
Impl
,
typename
Char
>
class
BasicPrintfArgFormatter
:
public
ArgFormatterBase
<
Impl
,
Char
>
{
private:
void
write_null_pointer
()
{
this
->
spec
().
type_
=
0
;
this
->
write
(
"(nil)"
);
}
typedef
ArgFormatterBase
<
Impl
,
Char
>
Base
;
public:
BasicPrintfArgFormatter
(
BasicWriter
<
Char
>
&
w
,
FormatSpec
&
s
)
:
ArgFormatterBase
<
Impl
,
Char
>
(
w
,
s
)
{}
void
visit_bool
(
bool
value
)
{
FormatSpec
&
fmt_spec
=
this
->
spec
();
if
(
fmt_spec
.
type_
!=
's'
)
return
this
->
visit_any_int
(
value
);
fmt_spec
.
type_
=
0
;
this
->
write
(
value
);
}
void
visit_char
(
int
value
)
{
const
FormatSpec
&
fmt_spec
=
this
->
spec
();
BasicWriter
<
Char
>
&
w
=
this
->
writer
();
if
(
fmt_spec
.
type_
&&
fmt_spec
.
type_
!=
'c'
)
w
.
write_int
(
value
,
fmt_spec
);
typedef
typename
BasicWriter
<
Char
>::
CharPtr
CharPtr
;
CharPtr
out
=
CharPtr
();
if
(
fmt_spec
.
width_
>
1
)
{
Char
fill
=
' '
;
out
=
w
.
grow_buffer
(
fmt_spec
.
width_
);
if
(
fmt_spec
.
align_
!=
ALIGN_LEFT
)
{
std
::
fill_n
(
out
,
fmt_spec
.
width_
-
1
,
fill
);
out
+=
fmt_spec
.
width_
-
1
;
}
else
{
std
::
fill_n
(
out
+
1
,
fmt_spec
.
width_
-
1
,
fill
);
}
}
else
{
out
=
w
.
grow_buffer
(
1
);
}
*
out
=
static_cast
<
Char
>
(
value
);
}
void
visit_cstring
(
const
char
*
value
)
{
if
(
value
)
Base
::
visit_cstring
(
value
);
else
if
(
this
->
spec
().
type_
==
'p'
)
write_null_pointer
();
else
this
->
write
(
"(null)"
);
}
void
visit_pointer
(
const
void
*
value
)
{
if
(
value
)
return
Base
::
visit_pointer
(
value
);
this
->
spec
().
type_
=
0
;
write_null_pointer
();
}
void
visit_custom
(
Arg
::
CustomValue
c
)
{
BasicFormatter
<
Char
>
formatter
(
ArgList
(),
this
->
writer
());
const
Char
format_str
[]
=
{
'}'
,
0
};
const
Char
*
format
=
format_str
;
c
.
format
(
&
formatter
,
c
.
value
,
&
format
);
}
};
/** The default printf argument formatter. */
template
<
typename
Char
>
class
PrintfArgFormatter
:
public
BasicPrintfArgFormatter
<
PrintfArgFormatter
<
Char
>
,
Char
>
{
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter
(
BasicWriter
<
Char
>
&
w
,
FormatSpec
&
s
)
:
BasicPrintfArgFormatter
<
PrintfArgFormatter
<
Char
>
,
Char
>
(
w
,
s
)
{}
};
// A printf formatter.
template
<
typename
Char
,
typename
ArgFormatter
=
PrintfArgFormatter
<
Char
>
>
class
PrintfFormatter
:
private
FormatterBase
{
private:
void
parse_flags
(
FormatSpec
&
spec
,
const
Char
*&
s
);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
Arg
get_arg
(
const
Char
*
s
,
unsigned
arg_index
=
(
std
::
numeric_limits
<
unsigned
>::
max
)());
// Parses argument index, flags and width and returns the argument index.
unsigned
parse_header
(
const
Char
*&
s
,
FormatSpec
&
spec
);
public:
explicit
PrintfFormatter
(
const
ArgList
&
args
)
:
FormatterBase
(
args
)
{}
FMT_API
void
format
(
BasicWriter
<
Char
>
&
writer
,
BasicCStringRef
<
Char
>
format_str
);
};
template
<
typename
Char
,
typename
AF
>
void
PrintfFormatter
<
Char
,
AF
>::
parse_flags
(
FormatSpec
&
spec
,
const
Char
*&
s
)
{
for
(;;)
{
switch
(
*
s
++
)
{
case
'-'
:
spec
.
align_
=
ALIGN_LEFT
;
break
;
case
'+'
:
spec
.
flags_
|=
SIGN_FLAG
|
PLUS_FLAG
;
break
;
case
'0'
:
spec
.
fill_
=
'0'
;
break
;
case
' '
:
spec
.
flags_
|=
SIGN_FLAG
;
break
;
case
'#'
:
spec
.
flags_
|=
HASH_FLAG
;
break
;
default:
--
s
;
return
;
}
}
}
template
<
typename
Char
,
typename
AF
>
Arg
PrintfFormatter
<
Char
,
AF
>::
get_arg
(
const
Char
*
s
,
unsigned
arg_index
)
{
(
void
)
s
;
const
char
*
error
=
0
;
Arg
arg
=
arg_index
==
std
::
numeric_limits
<
unsigned
>::
max
()
?
next_arg
(
error
)
:
FormatterBase
::
get_arg
(
arg_index
-
1
,
error
);
if
(
error
)
FMT_THROW
(
FormatError
(
!*
s
?
"invalid format string"
:
error
));
return
arg
;
}
template
<
typename
Char
,
typename
AF
>
unsigned
PrintfFormatter
<
Char
,
AF
>::
parse_header
(
const
Char
*&
s
,
FormatSpec
&
spec
)
{
unsigned
arg_index
=
std
::
numeric_limits
<
unsigned
>::
max
();
Char
c
=
*
s
;
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned
value
=
parse_nonnegative_int
(
s
);
if
(
*
s
==
'$'
)
{
// value is an argument index
++
s
;
arg_index
=
value
;
}
else
{
if
(
c
==
'0'
)
spec
.
fill_
=
'0'
;
if
(
value
!=
0
)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec
.
width_
=
value
;
return
arg_index
;
}
}
}
parse_flags
(
spec
,
s
);
// Parse width.
if
(
*
s
>=
'0'
&&
*
s
<=
'9'
)
{
spec
.
width_
=
parse_nonnegative_int
(
s
);
}
else
if
(
*
s
==
'*'
)
{
++
s
;
spec
.
width_
=
WidthHandler
(
spec
).
visit
(
get_arg
(
s
));
}
return
arg_index
;
}
template
<
typename
Char
,
typename
AF
>
void
PrintfFormatter
<
Char
,
AF
>::
format
(
BasicWriter
<
Char
>
&
writer
,
BasicCStringRef
<
Char
>
format_str
)
{
const
Char
*
start
=
format_str
.
c_str
();
const
Char
*
s
=
start
;
while
(
*
s
)
{
Char
c
=
*
s
++
;
if
(
c
!=
'%'
)
continue
;
if
(
*
s
==
c
)
{
write
(
writer
,
start
,
s
);
start
=
++
s
;
continue
;
}
write
(
writer
,
start
,
s
-
1
);
FormatSpec
spec
;
spec
.
align_
=
ALIGN_RIGHT
;
// Parse argument index, flags and width.
unsigned
arg_index
=
parse_header
(
s
,
spec
);
// Parse precision.
if
(
*
s
==
'.'
)
{
++
s
;
if
(
'0'
<=
*
s
&&
*
s
<=
'9'
)
{
spec
.
precision_
=
static_cast
<
int
>
(
parse_nonnegative_int
(
s
));
}
else
if
(
*
s
==
'*'
)
{
++
s
;
spec
.
precision_
=
PrecisionHandler
().
visit
(
get_arg
(
s
));
}
}
Arg
arg
=
get_arg
(
s
,
arg_index
);
if
(
spec
.
flag
(
HASH_FLAG
)
&&
IsZeroInt
().
visit
(
arg
))
spec
.
flags_
&=
~
to_unsigned
<
int
>
(
HASH_FLAG
);
if
(
spec
.
fill_
==
'0'
)
{
if
(
arg
.
type
<=
Arg
::
LAST_NUMERIC_TYPE
)
spec
.
align_
=
ALIGN_NUMERIC
;
else
spec
.
fill_
=
' '
;
// Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
switch
(
*
s
++
)
{
case
'h'
:
if
(
*
s
==
'h'
)
ArgConverter
<
signed
char
>
(
arg
,
*++
s
).
visit
(
arg
);
else
ArgConverter
<
short
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
'l'
:
if
(
*
s
==
'l'
)
ArgConverter
<
fmt
::
LongLong
>
(
arg
,
*++
s
).
visit
(
arg
);
else
ArgConverter
<
long
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
'j'
:
ArgConverter
<
intmax_t
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
'z'
:
ArgConverter
<
std
::
size_t
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
't'
:
ArgConverter
<
std
::
ptrdiff_t
>
(
arg
,
*
s
).
visit
(
arg
);
break
;
case
'L'
:
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break
;
default:
--
s
;
ArgConverter
<
void
>
(
arg
,
*
s
).
visit
(
arg
);
}
// Parse type.
if
(
!*
s
)
FMT_THROW
(
FormatError
(
"invalid format string"
));
spec
.
type_
=
static_cast
<
char
>
(
*
s
++
);
if
(
arg
.
type
<=
Arg
::
LAST_INTEGER_TYPE
)
{
// Normalize type.
switch
(
spec
.
type_
)
{
case
'i'
:
case
'u'
:
spec
.
type_
=
'd'
;
break
;
case
'c'
:
// TODO: handle wchar_t
CharConverter
(
arg
).
visit
(
arg
);
break
;
}
}
start
=
s
;
// Format argument.
AF
(
writer
,
spec
).
visit
(
arg
);
}
write
(
writer
,
start
,
s
);
}
}
// namespace internal
template
<
typename
Char
>
void
printf
(
BasicWriter
<
Char
>
&
w
,
BasicCStringRef
<
Char
>
format
,
ArgList
args
)
{
internal
::
PrintfFormatter
<
Char
>
(
args
).
format
(
w
,
format
);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline
std
::
string
sprintf
(
CStringRef
format
,
ArgList
args
)
{
MemoryWriter
w
;
printf
(
w
,
format
,
args
);
return
w
.
str
();
}
FMT_VARIADIC
(
std
::
string
,
sprintf
,
CStringRef
)
inline
std
::
wstring
sprintf
(
WCStringRef
format
,
ArgList
args
)
{
WMemoryWriter
w
;
printf
(
w
,
format
,
args
);
return
w
.
str
();
}
FMT_VARIADIC_W
(
std
::
wstring
,
sprintf
,
WCStringRef
)
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API
int
fprintf
(
std
::
FILE
*
f
,
CStringRef
format
,
ArgList
args
);
FMT_VARIADIC
(
int
,
fprintf
,
std
::
FILE
*
,
CStringRef
)
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline
int
printf
(
CStringRef
format
,
ArgList
args
)
{
return
fprintf
(
stdout
,
format
,
args
);
}
FMT_VARIADIC
(
int
,
printf
,
CStringRef
)
}
// namespace fmt
#endif // FMT_PRINTF_H_
test/CMakeLists.txt
View file @
72d51e0b
...
...
@@ -80,6 +80,7 @@ add_fmt_test(printf-test)
add_fmt_test
(
string-test
)
add_fmt_test
(
util-test mock-allocator.h
)
add_fmt_test
(
macro-test
)
add_fmt_test
(
custom-formatter-test
)
# Enable stricter options for one test to make sure that the header is free of
# warnings.
...
...
test/custom-formatter-test.cc
0 → 100644
View file @
72d51e0b
/*
Custom argument formatter tests
Copyright (c) 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "fmt/printf.h"
#include "gtest-extra.h"
using
fmt
::
internal
::
BasicPrintfArgFormatter
;
// A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0.
class
CustomArgFormatter
:
public
fmt
::
BasicArgFormatter
<
CustomArgFormatter
,
char
>
{
public:
CustomArgFormatter
(
fmt
::
BasicFormatter
<
char
,
CustomArgFormatter
>
&
f
,
fmt
::
FormatSpec
&
s
,
const
char
*
fmt
)
:
fmt
::
BasicArgFormatter
<
CustomArgFormatter
,
char
>
(
f
,
s
,
fmt
)
{}
void
visit_double
(
double
value
)
{
if
(
round
(
value
*
pow
(
10
,
spec
().
precision
()))
==
0
)
value
=
0
;
fmt
::
BasicArgFormatter
<
CustomArgFormatter
,
char
>::
visit_double
(
value
);
}
};
// A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0.
class
CustomPAF
:
public
BasicPrintfArgFormatter
<
CustomPAF
,
char
>
{
public:
CustomPAF
(
fmt
::
BasicWriter
<
char
>
&
writer
,
fmt
::
FormatSpec
&
spec
)
:
BasicPrintfArgFormatter
<
CustomPAF
,
char
>
(
writer
,
spec
)
{}
void
visit_double
(
double
value
)
{
if
(
round
(
value
*
pow
(
10
,
spec
().
precision
()))
==
0
)
value
=
0
;
BasicPrintfArgFormatter
<
CustomPAF
,
char
>::
visit_double
(
value
);
}
};
std
::
string
custom_format
(
const
char
*
format_str
,
fmt
::
ArgList
args
)
{
fmt
::
MemoryWriter
writer
;
// Pass custom argument formatter as a template arg to BasicFormatter.
fmt
::
BasicFormatter
<
char
,
CustomArgFormatter
>
formatter
(
args
,
writer
);
formatter
.
format
(
format_str
);
return
writer
.
str
();
}
FMT_VARIADIC
(
std
::
string
,
custom_format
,
const
char
*
)
std
::
string
custom_sprintf
(
const
char
*
fstr
,
fmt
::
ArgList
args
){
fmt
::
MemoryWriter
writer
;
fmt
::
internal
::
PrintfFormatter
<
char
,
CustomPAF
>
pfer
(
args
);
pfer
.
format
(
writer
,
fstr
);
return
writer
.
str
();
}
FMT_VARIADIC
(
std
::
string
,
custom_sprintf
,
const
char
*
);
TEST
(
CustomFormatterTest
,
Format
)
{
EXPECT_EQ
(
"0.00"
,
custom_format
(
"{:.2f}"
,
-
.00001
));
EXPECT_EQ
(
"0.00"
,
custom_sprintf
(
"%.2f"
,
-
.00001
));
}
test/format-impl-test.cc
View file @
72d51e0b
...
...
@@ -46,7 +46,7 @@ TEST(FormatTest, ArgConverter) {
Arg
arg
=
Arg
();
arg
.
type
=
Arg
::
LONG_LONG
;
arg
.
long_long_value
=
std
::
numeric_limits
<
fmt
::
LongLong
>::
max
();
fmt
::
ArgConverter
<
fmt
::
LongLong
>
(
arg
,
'd'
).
visit
(
arg
);
fmt
::
internal
::
ArgConverter
<
fmt
::
LongLong
>
(
arg
,
'd'
).
visit
(
arg
);
EXPECT_EQ
(
Arg
::
LONG_LONG
,
arg
.
type
);
}
...
...
test/printf-test.cc
View file @
72d51e0b
...
...
@@ -29,6 +29,7 @@
#include <climits>
#include <cstring>
#include "fmt/printf.h"
#include "fmt/format.h"
#include "gtest-extra.h"
#include "util.h"
...
...
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