out(StringPiece(p, q));
p = q;
- CHECK(p != end && *p == '}') << "single '}' in format string";
+ if (p == end || *p != '}') {
+ throw std::invalid_argument(
+ "folly::format: single '}' in format string");
+ }
++p;
}
};
outputString(StringPiece(p, q));
p = q + 1;
- CHECK(p != end) << "'{' at end of format string";
+ if (p == end) {
+ throw std::invalid_argument(
+ "folly::format: '}' at end of format string");
+ }
// "{{" -> "{"
if (*p == '{') {
// Format string
q = static_cast<const char*>(memchr(p, '}', end - p));
- CHECK(q != end) << "missing ending '}'";
+ if (q == nullptr) {
+ throw std::invalid_argument("folly::format: missing ending '}'");
+ }
FormatArg arg(StringPiece(p, q));
p = q + 1;
try {
argIndex = to<int>(piece);
} catch (const std::out_of_range& e) {
- LOG(FATAL) << "argument index must be integer";
+ arg.error("argument index must be integer");
}
- CHECK(argIndex >= 0)
- << arg.errorStr("argument index must be non-negative");
+ arg.enforce(argIndex >= 0, "argument index must be non-negative");
hasExplicitArgIndex = true;
}
}
- CHECK(!hasDefaultArgIndex || !hasExplicitArgIndex)
- << "may not have both default and explicit arg indexes";
+ if (hasDefaultArgIndex && hasExplicitArgIndex) {
+ throw std::invalid_argument(
+ "folly::format: may not have both default and explicit arg indexes");
+ }
doFormat(argIndex, arg, out);
}
uval = val_;
sign = '\0';
- CHECK(arg.sign == FormatArg::Sign::DEFAULT)
- << arg.errorStr("sign specifications not allowed for unsigned values");
+ arg.enforce(arg.sign == FormatArg::Sign::DEFAULT,
+ "sign specifications not allowed for unsigned values");
}
// max of:
switch (presentation) {
case 'n': // TODO(tudorb): locale awareness?
case 'd':
- CHECK(!arg.basePrefix)
- << arg.errorStr("base prefix not allowed with '", presentation,
- "' specifier");
+ arg.enforce(!arg.basePrefix,
+ "base prefix not allowed with '", presentation,
+ "' specifier");
if (arg.thousandsSeparator) {
useSprintf("%'ju");
} else {
}
break;
case 'c':
- CHECK(!arg.basePrefix)
- << arg.errorStr("base prefix not allowed with '", presentation,
- "' specifier");
- CHECK(!arg.thousandsSeparator)
- << arg.errorStr("thousands separator (',') not allowed with '",
- presentation, "' specifier");
+ arg.enforce(!arg.basePrefix,
+ "base prefix not allowed with '", presentation,
+ "' specifier");
+ arg.enforce(!arg.thousandsSeparator,
+ "thousands separator (',') not allowed with '",
+ presentation, "' specifier");
valBufBegin = valBuf + 3;
*valBufBegin = static_cast<char>(uval);
valBufEnd = valBufBegin + 1;
break;
case 'o':
case 'O':
- CHECK(!arg.thousandsSeparator)
- << arg.errorStr("thousands separator (',') not allowed with '",
- presentation, "' specifier");
+ arg.enforce(!arg.thousandsSeparator,
+ "thousands separator (',') not allowed with '",
+ presentation, "' specifier");
valBufEnd = valBuf + valBufSize - 1;
valBufBegin = valBuf + detail::uintToOctal(valBuf, valBufSize - 1, uval);
if (arg.basePrefix) {
}
break;
case 'x':
- CHECK(!arg.thousandsSeparator)
- << arg.errorStr("thousands separator (',') not allowed with '",
- presentation, "' specifier");
+ arg.enforce(!arg.thousandsSeparator,
+ "thousands separator (',') not allowed with '",
+ presentation, "' specifier");
valBufEnd = valBuf + valBufSize - 1;
valBufBegin = valBuf + detail::uintToHexLower(valBuf, valBufSize - 1,
uval);
}
break;
case 'X':
- CHECK(!arg.thousandsSeparator)
- << arg.errorStr("thousands separator (',') not allowed with '",
- presentation, "' specifier");
+ arg.enforce(!arg.thousandsSeparator,
+ "thousands separator (',') not allowed with '",
+ presentation, "' specifier");
valBufEnd = valBuf + valBufSize - 1;
valBufBegin = valBuf + detail::uintToHexUpper(valBuf, valBufSize - 1,
uval);
break;
case 'b':
case 'B':
- CHECK(!arg.thousandsSeparator)
- << arg.errorStr("thousands separator (',') not allowed with '",
- presentation, "' specifier");
+ arg.enforce(!arg.thousandsSeparator,
+ "thousands separator (',') not allowed with '",
+ presentation, "' specifier");
valBufEnd = valBuf + valBufSize - 1;
valBufBegin = valBuf + detail::uintToBinary(valBuf, valBufSize - 1,
uval);
}
break;
default:
- LOG(FATAL) << arg.errorStr("invalid specifier '", presentation, "'");
+ arg.error("invalid specifier '", presentation, "'");
}
if (sign) {
exponentSymbol,
-4, arg.precision,
0, 0);
- CHECK(conv.ToExponential(val, arg.precision, &builder));
+ arg.enforce(conv.ToExponential(val, arg.precision, &builder));
}
break;
case 'n': // should be locale-aware, but isn't
exponentSymbol,
-4, arg.precision,
0, 0);
- CHECK(conv.ToShortest(val, &builder));
+ arg.enforce(conv.ToShortest(val, &builder));
}
break;
default:
- LOG(FATAL) << arg.errorStr("invalid specifier '", arg.presentation, "'");
+ arg.error("invalid specifier '", arg.presentation, "'");
}
int len = builder.position();
void format(FormatArg& arg, FormatCallback& cb) const {
if (arg.keyEmpty()) {
arg.validate(FormatArg::Type::OTHER);
- CHECK(arg.presentation == FormatArg::kDefaultPresentation ||
- arg.presentation == 's')
- << arg.errorStr("invalid specifier '", arg.presentation, "'");
+ arg.enforce(arg.presentation == FormatArg::kDefaultPresentation ||
+ arg.presentation == 's',
+ "invalid specifier '", arg.presentation, "'");
format_value::formatString(val_, arg, cb);
} else {
FormatValue<char>(val_.at(arg.splitIntKey())).format(arg, cb);
template <class FormatCallback>
void format(FormatArg& arg, FormatCallback& cb) const {
arg.validate(FormatArg::Type::OTHER);
- CHECK(arg.presentation == FormatArg::kDefaultPresentation)
- << arg.errorStr("invalid specifier '", arg.presentation, "'");
+ arg.enforce(arg.presentation == FormatArg::kDefaultPresentation,
+ "invalid specifier '", arg.presentation, "'");
format_value::formatString("(null)", arg, cb);
}
};
} else {
// Print as a pointer, in hex.
arg.validate(FormatArg::Type::OTHER);
- CHECK(arg.presentation == FormatArg::kDefaultPresentation)
- << arg.errorStr("invalid specifier '", arg.presentation, "'");
+ arg.enforce(arg.presentation == FormatArg::kDefaultPresentation,
+ "invalid specifier '", arg.presentation, "'");
arg.basePrefix = true;
arg.presentation = 'x';
if (arg.align == FormatArg::Align::DEFAULT) {
public:
template <class FormatCallback>
static void formatOrFail(T& value, FormatArg& arg, FormatCallback& cb) {
- LOG(FATAL) << arg.errorStr("No formatter available for this type");
+ arg.error("No formatter available for this type");
}
};
FormatValue<typename std::decay<B>::type>(val_.second).format(arg, cb);
break;
default:
- LOG(FATAL) << arg.errorStr("invalid index for pair");
+ arg.error("invalid index for pair");
}
}
template <class FormatCallback>
void format(FormatArg& arg, FormatCallback& cb) const {
int key = arg.splitIntKey();
- CHECK(key >= 0) << arg.errorStr("tuple index must be non-negative");
+ arg.enforce(key >= 0, "tuple index must be non-negative");
doFormat(key, arg, cb);
}
template <size_t K, class Callback>
typename std::enable_if<K == valueCount>::type
doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
- LOG(FATAL) << arg.errorStr("tuple index out of range, max=", i);
+ arg.enforce("tuple index out of range, max=", i);
}
template <size_t K, class Callback>
/*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
}
if (*p == '0') {
- CHECK(align == Align::DEFAULT) << errorStr("alignment specified twice");
+ enforce(align == Align::DEFAULT, "alignment specified twice");
fill = '0';
align = Align::PAD_AFTER_SIGN;
if (++p == end) return;
if (++p == end) return;
}
- LOG(FATAL) << "extra characters in format string";
+ error("extra characters in format string");
}
void FormatArg::validate(Type type) const {
- CHECK(keyEmpty()) << "index not allowed";
+ enforce(keyEmpty(), "index not allowed");
switch (type) {
case Type::INTEGER:
- CHECK(precision == kDefaultPrecision)
- << errorStr("precision not allowed on integers");
+ enforce(precision == kDefaultPrecision,
+ "precision not allowed on integers");
break;
case Type::FLOAT:
- CHECK(!basePrefix)
- << errorStr("base prefix ('#') specifier only allowed on integers");
- CHECK(!thousandsSeparator)
- << errorStr("thousands separator (',') only allowed on integers");
+ enforce(!basePrefix,
+ "base prefix ('#') specifier only allowed on integers");
+ enforce(!thousandsSeparator,
+ "thousands separator (',') only allowed on integers");
break;
case Type::OTHER:
- CHECK(align != Align::PAD_AFTER_SIGN)
- << errorStr("'='alignment only allowed on numbers");
- CHECK(sign == Sign::DEFAULT)
- << errorStr("sign specifier only allowed on numbers");
- CHECK(!basePrefix)
- << errorStr("base prefix ('#') specifier only allowed on integers");
- CHECK(!thousandsSeparator)
- << errorStr("thousands separator (',') only allowed on integers");
+ enforce(align != Align::PAD_AFTER_SIGN,
+ "'='alignment only allowed on numbers");
+ enforce(sign == Sign::DEFAULT,
+ "sign specifier only allowed on numbers");
+ enforce(!basePrefix,
+ "base prefix ('#') specifier only allowed on integers");
+ enforce(!thousandsSeparator,
+ "thousands separator (',') only allowed on integers");
break;
}
}
/*
- * Copyright 2013 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
EXPECT_NE("", fstr("{}", &kv));
}
+namespace {
+
+struct Opaque {
+ int k;
+};
+
+} // namespace
+
+TEST(Format, Unformatted) {
+ Opaque o;
+ EXPECT_NE("", fstr("{}", &o));
+ EXPECT_THROW(fstr("{0[0]}", &o), std::invalid_argument);
+}
+
TEST(Format, Nested) {
EXPECT_EQ("1 2 3 4", fstr("{} {} {}", 1, 2, format("{} {}", 3, 4)));
//
std::map<std::string, int> map{{"hello", 0}, {"world", 1}};
EXPECT_EQ("hello = 0", fstr("hello = {[hello]}", map));
EXPECT_THROW(fstr("{[nope]}", map), std::out_of_range);
+ EXPECT_THROW(vstr("{nope}", map), std::out_of_range);
+}
+
+TEST(Format, BogusFormatString) {
+ EXPECT_THROW(fstr("}"), std::invalid_argument);
+ EXPECT_THROW(fstr("foo}bar"), std::invalid_argument);
+ EXPECT_THROW(fstr("foo{bar"), std::invalid_argument);
+ EXPECT_THROW(fstr("{[test]"), std::invalid_argument);
+
+ // This one fails in detail::enforceWhitespace(), which throws
+ // std::range_error
+ EXPECT_THROW(fstr("{0[test}"), std::exception);
}
int main(int argc, char *argv[]) {