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
688de77b
Commit
688de77b
authored
Feb 05, 2013
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Parameterize Formatter on character type and rename it to BasicFormatter.
parent
03dccc3c
Changes
3
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
412 additions
and
410 deletions
+412
-410
format.cc
format.cc
+0
-322
format.h
format.h
+405
-74
format_test.cc
format_test.cc
+7
-14
No files found.
format.cc
View file @
688de77b
...
@@ -33,47 +33,18 @@
...
@@ -33,47 +33,18 @@
#include <math.h>
#include <math.h>
// Wrap signbit because when compiled in C++11 mode signbit is no longer a
// macro but a function defined in namespace std and the macro is undefined.
#ifndef _MSC_VER
inline
int
SignBit
(
double
value
)
{
return
signbit
(
value
);
}
#endif
#include "format.h"
#include "format.h"
#include <cassert>
#include <cctype>
#include <cctype>
#include <climits>
#include <cstring>
#include <cstring>
#include <algorithm>
#include <algorithm>
using
std
::
size_t
;
using
fmt
::
IntFormatter
;
using
fmt
::
Formatter
;
using
fmt
::
AlignSpec
;
using
fmt
::
FormatSpec
;
using
fmt
::
WidthSpec
;
using
fmt
::
StringRef
;
#if _MSC_VER
#if _MSC_VER
# undef snprintf
# undef snprintf
# define snprintf _snprintf
# define snprintf _snprintf
# define isinf(x) (!_finite(x))
# define isinf(x) (!_finite(x))
#endif
#endif
namespace
{
#ifdef _MSC_VER
int
SignBit
(
double
value
)
{
if
(
value
<
0
)
return
1
;
if
(
value
==
value
)
return
0
;
int
dec
=
0
,
sign
=
0
;
_ecvt
(
value
,
0
,
&
dec
,
&
sign
);
return
sign
;
}
#endif
}
const
char
fmt
::
internal
::
DIGITS
[]
=
const
char
fmt
::
internal
::
DIGITS
[]
=
"0001020304050607080910111213141516171819"
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"2021222324252627282930313233343536373839"
...
@@ -90,296 +61,3 @@ void fmt::internal::ReportUnknownType(char code, const char *type) {
...
@@ -90,296 +61,3 @@ void fmt::internal::ReportUnknownType(char code, const char *type) {
fmt
::
str
(
fmt
::
Format
(
"unknown format code '
\\
x{:02x}' for {}"
)
fmt
::
str
(
fmt
::
Format
(
"unknown format code '
\\
x{:02x}' for {}"
)
<<
static_cast
<
unsigned
>
(
code
)
<<
type
));
<<
static_cast
<
unsigned
>
(
code
)
<<
type
));
}
}
// Throws Exception(message) if format contains '}', otherwise throws
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
// should override other errors.
void
Formatter
::
ReportError
(
const
char
*
s
,
StringRef
message
)
const
{
for
(
int
num_open_braces
=
num_open_braces_
;
*
s
;
++
s
)
{
if
(
*
s
==
'{'
)
{
++
num_open_braces
;
}
else
if
(
*
s
==
'}'
)
{
if
(
--
num_open_braces
==
0
)
throw
fmt
::
FormatError
(
message
);
}
}
throw
fmt
::
FormatError
(
"unmatched '{' in format"
);
}
// Parses an unsigned integer advancing s to the end of the parsed input.
// This function assumes that the first character of s is a digit.
unsigned
Formatter
::
ParseUInt
(
const
char
*&
s
)
const
{
assert
(
'0'
<=
*
s
&&
*
s
<=
'9'
);
unsigned
value
=
0
;
do
{
unsigned
new_value
=
value
*
10
+
(
*
s
++
-
'0'
);
if
(
new_value
<
value
)
// Check if value wrapped around.
ReportError
(
s
,
"number is too big in format"
);
value
=
new_value
;
}
while
(
'0'
<=
*
s
&&
*
s
<=
'9'
);
return
value
;
}
inline
const
Formatter
::
Arg
&
Formatter
::
ParseArgIndex
(
const
char
*&
s
)
{
unsigned
arg_index
=
0
;
if
(
*
s
<
'0'
||
*
s
>
'9'
)
{
if
(
*
s
!=
'}'
&&
*
s
!=
':'
)
ReportError
(
s
,
"invalid argument index in format string"
);
if
(
next_arg_index_
<
0
)
{
ReportError
(
s
,
"cannot switch from manual to automatic argument indexing"
);
}
arg_index
=
next_arg_index_
++
;
}
else
{
if
(
next_arg_index_
>
0
)
{
ReportError
(
s
,
"cannot switch from automatic to manual argument indexing"
);
}
next_arg_index_
=
-
1
;
arg_index
=
ParseUInt
(
s
);
if
(
arg_index
>=
args_
.
size
())
ReportError
(
s
,
"argument index is out of range in format"
);
}
return
*
args_
[
arg_index
];
}
void
Formatter
::
CheckSign
(
const
char
*&
s
,
const
Arg
&
arg
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
{
ReportError
(
s
,
Format
(
"format specifier '{0}' requires numeric argument"
)
<<
*
s
);
}
if
(
arg
.
type
==
UINT
||
arg
.
type
==
ULONG
)
{
ReportError
(
s
,
Format
(
"format specifier '{0}' requires signed argument"
)
<<
*
s
);
}
++
s
;
}
void
Formatter
::
DoFormat
()
{
const
char
*
start
=
format_
;
format_
=
0
;
next_arg_index_
=
0
;
const
char
*
s
=
start
;
while
(
*
s
)
{
char
c
=
*
s
++
;
if
(
c
!=
'{'
&&
c
!=
'}'
)
continue
;
if
(
*
s
==
c
)
{
buffer_
.
append
(
start
,
s
);
start
=
++
s
;
continue
;
}
if
(
c
==
'}'
)
throw
FormatError
(
"unmatched '}' in format"
);
num_open_braces_
=
1
;
buffer_
.
append
(
start
,
s
-
1
);
const
Arg
&
arg
=
ParseArgIndex
(
s
);
FormatSpec
spec
;
int
precision
=
-
1
;
if
(
*
s
==
':'
)
{
++
s
;
// Parse fill and alignment.
if
(
char
c
=
*
s
)
{
const
char
*
p
=
s
+
1
;
spec
.
align_
=
ALIGN_DEFAULT
;
do
{
switch
(
*
p
)
{
case
'<'
:
spec
.
align_
=
ALIGN_LEFT
;
break
;
case
'>'
:
spec
.
align_
=
ALIGN_RIGHT
;
break
;
case
'='
:
spec
.
align_
=
ALIGN_NUMERIC
;
break
;
case
'^'
:
spec
.
align_
=
ALIGN_CENTER
;
break
;
}
if
(
spec
.
align_
!=
ALIGN_DEFAULT
)
{
if
(
p
!=
s
)
{
if
(
c
==
'}'
)
break
;
if
(
c
==
'{'
)
ReportError
(
s
,
"invalid fill character '{'"
);
s
+=
2
;
spec
.
fill_
=
c
;
}
else
++
s
;
if
(
spec
.
align_
==
ALIGN_NUMERIC
&&
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '=' requires numeric argument"
);
break
;
}
}
while
(
--
p
>=
s
);
}
// Parse sign.
switch
(
*
s
)
{
case
'+'
:
CheckSign
(
s
,
arg
);
spec
.
flags_
|=
SIGN_FLAG
|
PLUS_FLAG
;
break
;
case
'-'
:
CheckSign
(
s
,
arg
);
break
;
case
' '
:
CheckSign
(
s
,
arg
);
spec
.
flags_
|=
SIGN_FLAG
;
break
;
}
if
(
*
s
==
'#'
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '#' requires numeric argument"
);
spec
.
flags_
|=
HASH_FLAG
;
++
s
;
}
// Parse width and zero flag.
if
(
'0'
<=
*
s
&&
*
s
<=
'9'
)
{
if
(
*
s
==
'0'
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '0' requires numeric argument"
);
spec
.
align_
=
ALIGN_NUMERIC
;
spec
.
fill_
=
'0'
;
}
// Zero may be parsed again as a part of the width, but it is simpler
// and more efficient than checking if the next char is a digit.
unsigned
value
=
ParseUInt
(
s
);
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
spec
.
width_
=
value
;
}
// Parse precision.
if
(
*
s
==
'.'
)
{
++
s
;
precision
=
0
;
if
(
'0'
<=
*
s
&&
*
s
<=
'9'
)
{
unsigned
value
=
ParseUInt
(
s
);
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
precision
=
value
;
}
else
if
(
*
s
==
'{'
)
{
++
s
;
++
num_open_braces_
;
const
Arg
&
precision_arg
=
ParseArgIndex
(
s
);
unsigned
long
value
=
0
;
switch
(
precision_arg
.
type
)
{
case
INT
:
if
(
precision_arg
.
int_value
<
0
)
ReportError
(
s
,
"negative precision in format"
);
value
=
precision_arg
.
int_value
;
break
;
case
UINT
:
value
=
precision_arg
.
uint_value
;
break
;
case
LONG
:
if
(
precision_arg
.
long_value
<
0
)
ReportError
(
s
,
"negative precision in format"
);
value
=
precision_arg
.
long_value
;
break
;
case
ULONG
:
value
=
precision_arg
.
ulong_value
;
break
;
default:
ReportError
(
s
,
"precision is not integer"
);
}
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
precision
=
value
;
if
(
*
s
++
!=
'}'
)
throw
FormatError
(
"unmatched '{' in format"
);
--
num_open_braces_
;
}
else
{
ReportError
(
s
,
"missing precision in format"
);
}
if
(
arg
.
type
!=
DOUBLE
&&
arg
.
type
!=
LONG_DOUBLE
)
{
ReportError
(
s
,
"precision specifier requires floating-point argument"
);
}
}
// Parse type.
if
(
*
s
!=
'}'
&&
*
s
)
spec
.
type_
=
*
s
++
;
}
if
(
*
s
++
!=
'}'
)
throw
FormatError
(
"unmatched '{' in format"
);
start
=
s
;
// Format argument.
switch
(
arg
.
type
)
{
case
INT
:
FormatInt
(
arg
.
int_value
,
spec
);
break
;
case
UINT
:
FormatInt
(
arg
.
uint_value
,
spec
);
break
;
case
LONG
:
FormatInt
(
arg
.
long_value
,
spec
);
break
;
case
ULONG
:
FormatInt
(
arg
.
ulong_value
,
spec
);
break
;
case
DOUBLE
:
FormatDouble
(
arg
.
double_value
,
spec
,
precision
);
break
;
case
LONG_DOUBLE
:
FormatDouble
(
arg
.
long_double_value
,
spec
,
precision
);
break
;
case
CHAR
:
{
if
(
spec
.
type_
&&
spec
.
type_
!=
'c'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"char"
);
char
*
out
=
0
;
if
(
spec
.
width_
>
1
)
{
out
=
GrowBuffer
(
spec
.
width_
);
if
(
spec
.
align_
==
ALIGN_RIGHT
)
{
std
::
fill_n
(
out
,
spec
.
width_
-
1
,
spec
.
fill_
);
out
+=
spec
.
width_
-
1
;
}
else
if
(
spec
.
align_
==
ALIGN_CENTER
)
{
out
=
FillPadding
(
out
,
spec
.
width_
,
1
,
spec
.
fill_
);
}
else
{
std
::
fill_n
(
out
+
1
,
spec
.
width_
-
1
,
spec
.
fill_
);
}
}
else
{
out
=
GrowBuffer
(
1
);
}
*
out
=
arg
.
int_value
;
break
;
}
case
STRING
:
{
if
(
spec
.
type_
&&
spec
.
type_
!=
's'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"string"
);
const
char
*
str
=
arg
.
string
.
value
;
size_t
size
=
arg
.
string
.
size
;
if
(
size
==
0
)
{
if
(
!
str
)
throw
FormatError
(
"string pointer is null"
);
if
(
*
str
)
size
=
std
::
strlen
(
str
);
}
FormatString
(
str
,
size
,
spec
);
break
;
}
case
POINTER
:
if
(
spec
.
type_
&&
spec
.
type_
!=
'p'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"pointer"
);
spec
.
flags_
=
HASH_FLAG
;
spec
.
type_
=
'x'
;
FormatInt
(
reinterpret_cast
<
uintptr_t
>
(
arg
.
pointer_value
),
spec
);
break
;
case
CUSTOM
:
if
(
spec
.
type_
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"object"
);
(
this
->*
arg
.
custom
.
format
)(
arg
.
custom
.
value
,
spec
);
break
;
default:
assert
(
false
);
break
;
}
}
buffer_
.
append
(
start
,
s
);
}
format.h
View file @
688de77b
This diff is collapsed.
Click to expand it.
format_test.cc
View file @
688de77b
...
@@ -25,12 +25,6 @@
...
@@ -25,12 +25,6 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
*/
// Disable useless MSVC warnings.
#undef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#undef _SCL_SECURE_NO_WARNINGS
#define _SCL_SECURE_NO_WARNINGS
#include <cctype>
#include <cctype>
#include <cfloat>
#include <cfloat>
#include <climits>
#include <climits>
...
@@ -980,7 +974,7 @@ struct CountCalls {
...
@@ -980,7 +974,7 @@ struct CountCalls {
TEST
(
TempFormatterTest
,
Action
)
{
TEST
(
TempFormatterTest
,
Action
)
{
int
num_calls
=
0
;
int
num_calls
=
0
;
{
{
fmt
::
TempFormatter
<
CountCalls
>
af
(
"test"
,
CountCalls
(
num_calls
));
fmt
::
TempFormatter
<
char
,
CountCalls
>
af
(
"test"
,
CountCalls
(
num_calls
));
EXPECT_EQ
(
0
,
num_calls
);
EXPECT_EQ
(
0
,
num_calls
);
}
}
EXPECT_EQ
(
1
,
num_calls
);
EXPECT_EQ
(
1
,
num_calls
);
...
@@ -989,9 +983,8 @@ TEST(TempFormatterTest, Action) {
...
@@ -989,9 +983,8 @@ TEST(TempFormatterTest, Action) {
TEST
(
TempFormatterTest
,
ActionNotCalledOnError
)
{
TEST
(
TempFormatterTest
,
ActionNotCalledOnError
)
{
int
num_calls
=
0
;
int
num_calls
=
0
;
{
{
EXPECT_THROW
(
typedef
fmt
::
TempFormatter
<
char
,
CountCalls
>
TestFormatter
;
fmt
::
TempFormatter
<
CountCalls
>
af
(
"{0"
,
CountCalls
(
num_calls
)),
EXPECT_THROW
(
TestFormatter
af
(
"{0"
,
CountCalls
(
num_calls
)),
FormatError
);
FormatError
);
}
}
EXPECT_EQ
(
0
,
num_calls
);
EXPECT_EQ
(
0
,
num_calls
);
}
}
...
@@ -1003,8 +996,8 @@ TEST(TempFormatterTest, ActionNotCalledOnError) {
...
@@ -1003,8 +996,8 @@ TEST(TempFormatterTest, ActionNotCalledOnError) {
TEST
(
TempFormatterTest
,
ArgLifetime
)
{
TEST
(
TempFormatterTest
,
ArgLifetime
)
{
// The following code is for testing purposes only. It is a definite abuse
// The following code is for testing purposes only. It is a definite abuse
// of the API and shouldn't be used in real applications.
// of the API and shouldn't be used in real applications.
const
fmt
::
TempFormatter
<>
&
af
=
fmt
::
Format
(
"{0}"
);
const
fmt
::
TempFormatter
<
char
>
&
af
=
fmt
::
Format
(
"{0}"
);
const_cast
<
fmt
::
TempFormatter
<>&>
(
af
)
<<
std
::
string
(
"test"
);
const_cast
<
fmt
::
TempFormatter
<
char
>&>
(
af
)
<<
std
::
string
(
"test"
);
// String object passed as an argument to TempFormatter has
// String object passed as an argument to TempFormatter has
// been destroyed, but ArgInserter dtor hasn't been called yet.
// been destroyed, but ArgInserter dtor hasn't been called yet.
// But that's OK since the Arg's dtor takes care of this and
// But that's OK since the Arg's dtor takes care of this and
...
@@ -1023,8 +1016,8 @@ struct PrintError {
...
@@ -1023,8 +1016,8 @@ struct PrintError {
}
}
};
};
fmt
::
TempFormatter
<
PrintError
>
ReportError
(
const
char
*
format
)
{
fmt
::
TempFormatter
<
char
,
PrintError
>
ReportError
(
const
char
*
format
)
{
return
fmt
::
TempFormatter
<
PrintError
>
(
format
);
return
fmt
::
TempFormatter
<
char
,
PrintError
>
(
format
);
}
}
TEST
(
TempFormatterTest
,
Examples
)
{
TEST
(
TempFormatterTest
,
Examples
)
{
...
...
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