Skip to content

Commit 7fde1c3

Browse files
author
Chris Foster
committed
Refactor to combine code for reading width and precision
The positional mode code makes this complex enough that it seems worth combining these in one place. Also add an extra test to check that negative variable position is treated as though no precision was set.
1 parent b2b6ce3 commit 7fde1c3

File tree

2 files changed

+61
-66
lines changed

2 files changed

+61
-66
lines changed

tinyformat.h

Lines changed: 56 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,49 @@ inline int parseIntAndAdvance(const char*& c)
555555
return i;
556556
}
557557

558+
// Parse width or precision `n` from format string pointer `c`, and advance it
559+
// to the next character. If an indirection is requested with `*`, the argument
560+
// is read from `formatters[argIndex]` and `argIndex` is incremented (or read
561+
// from `formatters[n]` in positional mode). Returns true if one or more
562+
// characters were read.
563+
inline bool parseWidthOrPrecision(int& n, const char*& c, bool positionalMode,
564+
const detail::FormatArg* formatters,
565+
int& argIndex, int numFormatters)
566+
{
567+
if(*c >= '0' && *c <= '9')
568+
{
569+
n = parseIntAndAdvance(c);
570+
}
571+
else if(*c == '*')
572+
{
573+
++c;
574+
n = 0;
575+
if(positionalMode)
576+
{
577+
int pos = parseIntAndAdvance(c) - 1;
578+
if(*c != '$')
579+
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
580+
if(pos >= 0 && pos < numFormatters)
581+
n = formatters[pos].toInt();
582+
else
583+
TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
584+
++c;
585+
}
586+
else
587+
{
588+
if(argIndex < numFormatters)
589+
n = formatters[argIndex++].toInt();
590+
else
591+
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width or precision");
592+
}
593+
}
594+
else
595+
{
596+
return false;
597+
}
598+
return true;
599+
}
600+
558601
// Print literal part of format string and return next format spec
559602
// position.
560603
//
@@ -649,15 +692,12 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode
649692
const char tmpc = *c;
650693
int value = parseIntAndAdvance(c);
651694
if(*c == '$')
652-
{ // value is an argument index
695+
{
696+
// value is an argument index
653697
if(value > 0 && value <= numFormatters)
654-
{
655698
argIndex = value - 1;
656-
}
657699
else
658-
{
659700
TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
660-
}
661701
++c;
662702
positionalMode = true;
663703
}
@@ -727,33 +767,11 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode
727767
break;
728768
}
729769
// Parse width
730-
if(*c >= '0' && *c <= '9')
731-
{
732-
widthSet = true;
733-
out.width(parseIntAndAdvance(c));
734-
}
735-
else if(*c == '*')
770+
int width = 0;
771+
widthSet = parseWidthOrPrecision(width, c, positionalMode,
772+
formatters, argIndex, numFormatters);
773+
if(widthSet)
736774
{
737-
widthSet = true;
738-
int width = 0;
739-
if(positionalMode)
740-
{
741-
++c;
742-
int pos = parseIntAndAdvance(c) - 1;
743-
if(*c != '$')
744-
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
745-
if(pos >= 0 && pos < numFormatters)
746-
width = formatters[pos].toInt();
747-
else
748-
TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
749-
}
750-
else
751-
{
752-
if(argIndex < numFormatters)
753-
width = formatters[argIndex++].toInt();
754-
else
755-
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width");
756-
}
757775
if(width < 0)
758776
{
759777
// negative widths correspond to '-' flag set
@@ -762,45 +780,20 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode
762780
width = -width;
763781
}
764782
out.width(width);
765-
++c;
766783
}
767784
}
768785
// 3) Parse precision
769786
if(*c == '.')
770787
{
771788
++c;
772789
int precision = 0;
773-
if(*c == '*')
774-
{
775-
++c;
776-
if(positionalMode)
777-
{
778-
int pos = parseIntAndAdvance(c) - 1;
779-
if(*c != '$')
780-
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
781-
if(pos >= 0 && pos < numFormatters)
782-
precision = formatters[pos].toInt();
783-
else
784-
TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
785-
++c;
786-
}
787-
else
788-
{
789-
if(argIndex < numFormatters)
790-
precision = formatters[argIndex++].toInt();
791-
else
792-
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
793-
}
794-
}
795-
else
796-
{
797-
if(*c >= '0' && *c <= '9')
798-
precision = parseIntAndAdvance(c);
799-
else if(*c == '-') // negative precisions ignored, treated as zero.
800-
parseIntAndAdvance(++c);
801-
}
802-
out.precision(precision);
803-
precisionSet = true;
790+
parseWidthOrPrecision(precision, c, positionalMode,
791+
formatters, argIndex, numFormatters);
792+
// Presence of `.` indicates precision set, unless the inferred value
793+
// was negative in which case the default is used.
794+
precisionSet = precision >= 0;
795+
if(precisionSet)
796+
out.precision(precision);
804797
}
805798
// 4) Ignore any C99 length modifier
806799
while(*c == 'l' || *c == 'h' || *c == 'L' ||

tinyformat_test.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ int unitTests()
128128
CHECK_EQUAL(tfm::format("%hc", (short)65), "A");
129129
CHECK_EQUAL(tfm::format("%lc", (long)65), "A");
130130
CHECK_EQUAL(tfm::format("%s", "asdf_123098"), "asdf_123098");
131-
// Note: All tests printing pointers are different on windows, since
132-
// there's no standard numerical representation.
133-
// Representation also differs between 32-bit and 64-bit windows.
131+
132+
// Test printing of pointers. Note that there's no standard numerical
133+
// representation so this is platform and OS dependent.
134134
# ifdef _MSC_VER
135135
# ifdef _WIN64
136136
CHECK_EQUAL(tfm::format("%p", (void*)0x12345), "0000000000012345");
@@ -170,6 +170,7 @@ int unitTests()
170170
CHECK_EQUAL(tfm::format("%10.*f", 4, 1234.1234567890), " 1234.1235");
171171
CHECK_EQUAL(tfm::format("%*.*f", 10, 4, 1234.1234567890), " 1234.1235");
172172
CHECK_EQUAL(tfm::format("%*.*f", -10, 4, 1234.1234567890), "1234.1235 ");
173+
CHECK_EQUAL(tfm::format("%.*f", -4, 1234.1234567890), "1234.123457"); // negative precision ignored
173174
// Test variable precision & width with positional arguments
174175
CHECK_EQUAL(tfm::format("%1$*2$.4f", 1234.1234567890, 10), " 1234.1235");
175176
CHECK_EQUAL(tfm::format("%1$10.*2$f", 1234.1234567890, 4), " 1234.1235");
@@ -238,6 +239,7 @@ int unitTests()
238239
EXPECT_ERROR( tfm::format("%0$d", 1) )
239240
EXPECT_ERROR( tfm::format("%1$.*3$d", 1, 2) )
240241
EXPECT_ERROR( tfm::format("%1$.*0$d", 1, 2) )
242+
EXPECT_ERROR( tfm::format("%1$.*$d", 1, 2) )
241243
EXPECT_ERROR( tfm::format("%3$*4$.*2$d", 1, 2, 3) )
242244
EXPECT_ERROR( tfm::format("%3$*0$.*2$d", 1, 2, 3) )
243245

0 commit comments

Comments
 (0)