Export of internal Abseil changes

--
34c0d521b11ed4191ea3e071a864a84e5e5941b7 by Matthew Brown <matthewbr@google.com>:

Release absl::StrFormat custom type extensions
 - Allows StrFormat methods to be extended to accept types which implement
   AbslFormatConvert()
 - NOLINTNEXTLINE(readability-redundant-declaration) used, declarations are
   required in some compilers.

PiperOrigin-RevId: 316963065

--
4d475b5ad02d41057447d722ad35573fc4f48d1f by Evan Brown <ezb@google.com>:

Small fix to previous change: the first overload of insert_iterator_unique wasn't actually being selected. Fix that issue and add tests to verify that it actually works.

Note: couldn't use TestInstanceTracker here because that counts instances (and decrements in destructor) rather than counting all constructor calls.
PiperOrigin-RevId: 316927690
GitOrigin-RevId: 34c0d521b11ed4191ea3e071a864a84e5e5941b7
Change-Id: If8bbb8317b93af4084ac4cc55b752b99b1581b58
pull/705/merge
Abseil Team 5 years ago committed by Andy Getz
parent 4a851046a0
commit 4ccc0fce09
  1. 50
      absl/container/btree_test.cc
  2. 6
      absl/container/internal/btree.h
  3. 34
      absl/strings/internal/str_format/arg.h
  4. 10
      absl/strings/internal/str_format/arg_test.cc
  5. 23
      absl/strings/internal/str_format/extension.cc
  6. 6
      absl/strings/internal/str_format/extension.h
  7. 15
      absl/strings/internal/str_format/extension_test.cc
  8. 270
      absl/strings/str_format.h
  9. 160
      absl/strings/str_format_test.cc

@ -2416,6 +2416,41 @@ TEST(Btree, SetRangeConstructorAndInsertSupportExplicitConversionComparable) {
EXPECT_THAT(name_set2, ElementsAreArray(names));
}
// A type that is explicitly convertible from int and counts constructor calls.
struct ConstructorCounted {
explicit ConstructorCounted(int i) : i(i) { ++constructor_calls; }
bool operator==(int other) const { return i == other; }
int i;
static int constructor_calls;
};
int ConstructorCounted::constructor_calls = 0;
struct ConstructorCountedCompare {
bool operator()(int a, const ConstructorCounted &b) const { return a < b.i; }
bool operator()(const ConstructorCounted &a, int b) const { return a.i < b; }
bool operator()(const ConstructorCounted &a,
const ConstructorCounted &b) const {
return a.i < b.i;
}
using is_transparent = void;
};
TEST(Btree,
SetRangeConstructorAndInsertExplicitConvComparableLimitConstruction) {
const int i[] = {0, 1, 1};
ConstructorCounted::constructor_calls = 0;
absl::btree_set<ConstructorCounted, ConstructorCountedCompare> set{
std::begin(i), std::end(i)};
EXPECT_THAT(set, ElementsAre(0, 1));
EXPECT_EQ(ConstructorCounted::constructor_calls, 2);
set.insert(std::begin(i), std::end(i));
EXPECT_THAT(set, ElementsAre(0, 1));
EXPECT_EQ(ConstructorCounted::constructor_calls, 2);
}
TEST(Btree,
SetRangeConstructorAndInsertSupportExplicitConversionNonComparable) {
const int i[] = {0, 1};
@ -2443,6 +2478,21 @@ TEST(Btree, MapRangeConstructorAndInsertSupportExplicitConversionComparable) {
EXPECT_THAT(name_map2, ElementsAre(Pair("n1", 1), Pair("n2", 2)));
}
TEST(Btree,
MapRangeConstructorAndInsertExplicitConvComparableLimitConstruction) {
const std::pair<int, int> i[] = {{0, 1}, {1, 2}, {1, 3}};
ConstructorCounted::constructor_calls = 0;
absl::btree_map<ConstructorCounted, int, ConstructorCountedCompare> map{
std::begin(i), std::end(i)};
EXPECT_THAT(map, ElementsAre(Pair(0, 1), Pair(1, 2)));
EXPECT_EQ(ConstructorCounted::constructor_calls, 2);
map.insert(std::begin(i), std::end(i));
EXPECT_THAT(map, ElementsAre(Pair(0, 1), Pair(1, 2)));
EXPECT_EQ(ConstructorCounted::constructor_calls, 2);
}
TEST(Btree,
MapRangeConstructorAndInsertSupportExplicitConversionNonComparable) {
const std::pair<int, int> i[] = {{0, 1}, {1, 2}};

@ -1208,9 +1208,9 @@ class btree {
// Note: the first overload avoids constructing a value_type if the key
// already exists in the btree.
template <typename InputIterator,
typename = decltype(
compare_keys(params_type::key(*std::declval<InputIterator>()),
std::declval<const key_type &>()))>
typename = decltype(std::declval<const key_compare &>()(
params_type::key(*std::declval<InputIterator>()),
std::declval<const key_type &>()))>
void insert_iterator_unique(InputIterator b, InputIterator e, int);
// We need the second overload for cases in which we need to construct a
// value_type in order to compare it with the keys already in the btree.

@ -25,10 +25,12 @@ class Cord;
class FormatCountCapture;
class FormatSink;
namespace str_format_internal {
template <absl::FormatConversionCharSet C>
struct FormatConvertResult;
class FormatConversionSpec;
namespace str_format_internal {
template <typename T, typename = void>
struct HasUserDefinedConvert : std::false_type {};
@ -39,6 +41,22 @@ struct HasUserDefinedConvert<T, void_t<decltype(AbslFormatConvert(
std::declval<FormatSink*>()))>>
: std::true_type {};
void AbslFormatConvert(); // Stops the lexical name lookup
template <typename T>
auto FormatConvertImpl(const T& v, FormatConversionSpecImpl conv,
FormatSinkImpl* sink)
-> decltype(AbslFormatConvert(v,
std::declval<const FormatConversionSpec&>(),
std::declval<FormatSink*>())) {
using FormatConversionSpecT =
absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatConversionSpec>;
using FormatSinkT =
absl::enable_if_t<sizeof(const T& (*)()) != 0, FormatSink>;
auto fcs = conv.Wrap<FormatConversionSpecT>();
auto fs = sink->Wrap<FormatSinkT>();
return AbslFormatConvert(v, fcs, &fs);
}
template <typename T>
class StreamedWrapper;
@ -46,6 +64,13 @@ class StreamedWrapper;
// then convert it, appending to `sink` and return `true`.
// Otherwise fail and return `false`.
// AbslFormatConvert(v, conv, sink) is intended to be found by ADL on 'v'
// as an extension mechanism. These FormatConvertImpl functions are the default
// implementations.
// The ADL search is augmented via the 'Sink*' parameter, which also
// serves as a disambiguator to reject possible unintended 'AbslFormatConvert'
// functions in the namespaces associated with 'v'.
// Raw pointers.
struct VoidPtr {
VoidPtr() = default;
@ -61,6 +86,11 @@ struct ArgConvertResult {
bool value;
};
template <FormatConversionCharSet C>
constexpr FormatConversionCharSet ExtractCharSet(FormatConvertResult<C>) {
return C;
}
template <FormatConversionCharSet C>
constexpr FormatConversionCharSet ExtractCharSet(ArgConvertResult<C>) {
return C;

@ -23,8 +23,17 @@ class FormatArgImplTest : public ::testing::Test {
enum Color { kRed, kGreen, kBlue };
static const char *hi() { return "hi"; }
struct X {};
X x_;
};
inline FormatConvertResult<FormatConversionCharSet{}> AbslFormatConvert(
const FormatArgImplTest::X &, const FormatConversionSpec &, FormatSink *) {
return {false};
}
TEST_F(FormatArgImplTest, ToInt) {
int out = 0;
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(1), &out));
@ -59,6 +68,7 @@ TEST_F(FormatArgImplTest, ToInt) {
FormatArgImpl(static_cast<int *>(nullptr)), &out));
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(hi()), &out));
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl("hi"), &out));
EXPECT_FALSE(FormatArgImplFriend::ToInt(FormatArgImpl(x_), &out));
EXPECT_TRUE(FormatArgImplFriend::ToInt(FormatArgImpl(kBlue), &out));
EXPECT_EQ(2, out);
}

@ -33,6 +33,29 @@ std::string Flags::ToString() const {
return s;
}
#define ABSL_INTERNAL_X_VAL(id) \
constexpr absl::FormatConversionChar FormatConversionCharInternal::id;
ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_X_VAL, )
#undef ABSL_INTERNAL_X_VAL
// NOLINTNEXTLINE(readability-redundant-declaration)
constexpr absl::FormatConversionChar FormatConversionCharInternal::kNone;
#define ABSL_INTERNAL_CHAR_SET_CASE(c) \
constexpr FormatConversionCharSet FormatConversionCharSetInternal::c;
ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(ABSL_INTERNAL_CHAR_SET_CASE, )
#undef ABSL_INTERNAL_CHAR_SET_CASE
// NOLINTNEXTLINE(readability-redundant-declaration)
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kStar;
// NOLINTNEXTLINE(readability-redundant-declaration)
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kIntegral;
// NOLINTNEXTLINE(readability-redundant-declaration)
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kFloating;
// NOLINTNEXTLINE(readability-redundant-declaration)
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kNumeric;
// NOLINTNEXTLINE(readability-redundant-declaration)
constexpr FormatConversionCharSet FormatConversionCharSetInternal::kPointer;
bool FormatSinkImpl::PutPaddedString(string_view value, int width,
int precision, bool left) {
size_t space_remaining = 0;

@ -31,11 +31,11 @@
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
enum class FormatConversionChar : uint8_t;
enum class FormatConversionCharSet : uint64_t;
namespace str_format_internal {
class FormatRawSinkImpl {
public:
// Implicitly convert from any type that provides the hook function as
@ -361,14 +361,12 @@ struct FormatConversionCharSetInternal {
static constexpr FormatConversionCharSet kStar =
FormatConversionCharToConvValue('*');
// Some predefined values (TODO(matthewbr), delete any that are unused).
static constexpr FormatConversionCharSet kIntegral =
FormatConversionCharSetUnion(d, i, u, o, x, X);
static constexpr FormatConversionCharSet kFloating =
FormatConversionCharSetUnion(a, e, f, g, A, E, F, G);
static constexpr FormatConversionCharSet kNumeric =
FormatConversionCharSetUnion(kIntegral, kFloating);
static constexpr FormatConversionCharSet kString = s;
static constexpr FormatConversionCharSet kPointer = p;
};

@ -80,4 +80,19 @@ TEST(FormatExtensionTest, SinkAppendChars) {
EXPECT_EQ(actual, expected);
}
}
TEST(FormatExtensionTest, VerifyEnumEquality) {
#define X_VAL(id) \
EXPECT_EQ(absl::FormatConversionChar::id, \
absl::str_format_internal::FormatConversionCharInternal::id);
ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, );
#undef X_VAL
#define X_VAL(id) \
EXPECT_EQ(absl::FormatConversionCharSet::id, \
absl::str_format_internal::FormatConversionCharSetInternal::id);
ABSL_INTERNAL_CONVERSION_CHARS_EXPAND_(X_VAL, );
#undef X_VAL
}
} // namespace

@ -63,6 +63,9 @@
// loosely typed. `FormatUntyped()` is not a template and does not perform
// any compile-time checking of the format string; instead, it returns a
// boolean from a runtime check.
//
// In addition, the `str_format` library provides extension points for
// augmenting formatting to new types. See "StrFormat Extensions" below.
#ifndef ABSL_STRINGS_STR_FORMAT_H_
#define ABSL_STRINGS_STR_FORMAT_H_
@ -278,9 +281,36 @@ using FormatSpec = str_format_internal::FormatSpecTemplate<
// } else {
// ... error case ...
// }
#if defined(__cpp_nontype_template_parameter_auto)
// If C++17 is available, an 'extended' format is also allowed that can specify
// multiple conversion characters per format argument, using a combination of
// `absl::FormatConversionCharSet` enum values (logically a set union)
// via the `|` operator. (Single character-based arguments are still accepted,
// but cannot be combined). Some common conversions also have predefined enum
// values, such as `absl::FormatConversionCharSet::kIntegral`.
//
// Example:
// // Extended format supports multiple conversion characters per argument,
// // specified via a combination of `FormatConversionCharSet` enums.
// using MyFormat = absl::ParsedFormat<absl::FormatConversionCharSet::d |
// absl::FormatConversionCharSet::x>;
// MyFormat GetFormat(bool use_hex) {
// if (use_hex) return MyFormat("foo %x bar");
// return MyFormat("foo %d bar");
// }
// // `format` can be used with any value that supports 'd' and 'x',
// // like `int`.
// auto format = GetFormat(use_hex);
// value = StringF(format, i);
template <auto... Conv>
using ParsedFormat = absl::str_format_internal::ExtendedParsedFormat<
absl::str_format_internal::ToFormatConversionCharSet(Conv)...>;
#else
template <char... Conv>
using ParsedFormat = str_format_internal::ExtendedParsedFormat<
absl::str_format_internal::ToFormatConversionCharSet(Conv)...>;
#endif // defined(__cpp_nontype_template_parameter_auto)
// StrFormat()
//
@ -537,6 +567,246 @@ ABSL_MUST_USE_RESULT inline bool FormatUntyped(
str_format_internal::UntypedFormatSpecImpl::Extract(format), args);
}
//------------------------------------------------------------------------------
// StrFormat Extensions
//------------------------------------------------------------------------------
//
// AbslFormatConvert()
//
// The StrFormat library provides a customization API for formatting
// user-defined types using absl::StrFormat(). The API relies on detecting an
// overload in the user-defined type's namespace of a free (non-member)
// `AbslFormatConvert()` function, usually as a friend definition with the
// following signature:
//
// absl::FormatConvertResult<...> AbslFormatConvert(
// const X& value,
// const absl::FormatConversionSpec& spec,
// absl::FormatSink *sink);
//
// An `AbslFormatConvert()` overload for a type should only be declared in the
// same file and namespace as said type.
//
// The abstractions within this definition include:
//
// * An `absl::FormatConversionSpec` to specify the fields to pull from a
// user-defined type's format string
// * An `absl::FormatSink` to hold the converted string data during the
// conversion process.
// * An `absl::FormatConvertResult` to hold the status of the returned
// formatting operation
//
// The return type encodes all the conversion characters that your
// AbslFormatConvert() routine accepts. The return value should be {true}.
// A return value of {false} will result in `StrFormat()` returning
// an empty string. This result will be propagated to the result of
// `FormatUntyped`.
//
// Example:
//
// struct Point {
// // To add formatting support to `Point`, we simply need to add a free
// // (non-member) function `AbslFormatConvert()`. This method interprets
// // `spec` to print in the request format. The allowed conversion characters
// // can be restricted via the type of the result, in this example
// // string and integral formatting are allowed (but not, for instance
// // floating point characters like "%f"). You can add such a free function
// // using a friend declaration within the body of the class:
// friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString |
// absl::FormatConversionCharSet::kIntegral>
// AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec,
// absl::FormatSink* s) {
// if (spec.conversion_char() == absl::FormatConversionChar::s) {
// s->Append(absl::StrCat("x=", p.x, " y=", p.y));
// } else {
// s->Append(absl::StrCat(p.x, ",", p.y));
// }
// return {true};
// }
//
// int x;
// int y;
// };
// clang-format off
// FormatConversionChar
//
// Specifies the formatting character provided in the format string
// passed to `StrFormat()`.
enum class FormatConversionChar : uint8_t {
c, s, // text
d, i, o, u, x, X, // int
f, F, e, E, g, G, a, A, // float
n, p // misc
};
// clang-format on
// FormatConversionSpec
//
// Specifies modifications to the conversion of the format string, through use
// of one or more format flags in the source format string.
class FormatConversionSpec {
public:
// FormatConversionSpec::is_basic()
//
// Indicates that width and precision are not specified, and no additional
// flags are set for this conversion character in the format string.
bool is_basic() const { return impl_.is_basic(); }
// FormatConversionSpec::has_left_flag()
//
// Indicates whether the result should be left justified for this conversion
// character in the format string. This flag is set through use of a '-'
// character in the format string. E.g. "%-s"
bool has_left_flag() const { return impl_.has_left_flag(); }
// FormatConversionSpec::has_show_pos_flag()
//
// Indicates whether a sign column is prepended to the result for this
// conversion character in the format string, even if the result is positive.
// This flag is set through use of a '+' character in the format string.
// E.g. "%+d"
bool has_show_pos_flag() const { return impl_.has_show_pos_flag(); }
// FormatConversionSpec::has_sign_col_flag()
//
// Indicates whether a mandatory sign column is added to the result for this
// conversion character. This flag is set through use of a space character
// (' ') in the format string. E.g. "% i"
bool has_sign_col_flag() const { return impl_.has_sign_col_flag(); }
// FormatConversionSpec::has_alt_flag()
//
// Indicates whether an "alternate" format is applied to the result for this
// conversion character. Alternative forms depend on the type of conversion
// character, and unallowed alternatives are undefined. This flag is set
// through use of a '#' character in the format string. E.g. "%#h"
bool has_alt_flag() const { return impl_.has_alt_flag(); }
// FormatConversionSpec::has_zero_flag()
//
// Indicates whether zeroes should be prepended to the result for this
// conversion character instead of spaces. This flag is set through use of the
// '0' character in the format string. E.g. "%0f"
bool has_zero_flag() const { return impl_.has_zero_flag(); }
// FormatConversionSpec::conversion_char()
//
// Returns the underlying conversion character.
FormatConversionChar conversion_char() const {
return impl_.conversion_char();
}
// FormatConversionSpec::width()
//
// Returns the specified width (indicated through use of a non-zero integer
// value or '*' character) of the conversion character. If width is
// unspecified, it returns a negative value.
int width() const { return impl_.width(); }
// FormatConversionSpec::precision()
//
// Returns the specified precision (through use of the '.' character followed
// by a non-zero integer value or '*' character) of the conversion character.
// If precision is unspecified, it returns a negative value.
int precision() const { return impl_.precision(); }
private:
explicit FormatConversionSpec(
str_format_internal::FormatConversionSpecImpl impl)
: impl_(impl) {}
friend str_format_internal::FormatConversionSpecImpl;
absl::str_format_internal::FormatConversionSpecImpl impl_;
};
// Type safe OR operator for FormatConversionCharSet to allow accepting multiple
// conversion chars in custom format converters.
constexpr FormatConversionCharSet operator|(FormatConversionCharSet a,
FormatConversionCharSet b) {
return static_cast<FormatConversionCharSet>(static_cast<uint64_t>(a) |
static_cast<uint64_t>(b));
}
// FormatConversionCharSet
//
// Specifies the _accepted_ conversion types as a template parameter to
// FormatConvertResult for custom implementations of `AbslFormatConvert`.
// Note the helper predefined alias definitions (kIntegral, etc.) below.
enum class FormatConversionCharSet : uint64_t {
// text
c = str_format_internal::FormatConversionCharToConvInt('c'),
s = str_format_internal::FormatConversionCharToConvInt('s'),
// integer
d = str_format_internal::FormatConversionCharToConvInt('d'),
i = str_format_internal::FormatConversionCharToConvInt('i'),
o = str_format_internal::FormatConversionCharToConvInt('o'),
u = str_format_internal::FormatConversionCharToConvInt('u'),
x = str_format_internal::FormatConversionCharToConvInt('x'),
X = str_format_internal::FormatConversionCharToConvInt('X'),
// Float
f = str_format_internal::FormatConversionCharToConvInt('f'),
F = str_format_internal::FormatConversionCharToConvInt('F'),
e = str_format_internal::FormatConversionCharToConvInt('e'),
E = str_format_internal::FormatConversionCharToConvInt('E'),
g = str_format_internal::FormatConversionCharToConvInt('g'),
G = str_format_internal::FormatConversionCharToConvInt('G'),
a = str_format_internal::FormatConversionCharToConvInt('a'),
A = str_format_internal::FormatConversionCharToConvInt('A'),
// misc
n = str_format_internal::FormatConversionCharToConvInt('n'),
p = str_format_internal::FormatConversionCharToConvInt('p'),
// Used for width/precision '*' specification.
kStar = static_cast<uint64_t>(
absl::str_format_internal::FormatConversionCharSetInternal::kStar),
// Some predefined values:
kIntegral = d | i | u | o | x | X,
kFloating = a | e | f | g | A | E | F | G,
kNumeric = kIntegral | kFloating,
kString = s,
kPointer = p,
};
// FormatSink
//
// An abstraction to which conversions write their string data.
//
class FormatSink {
public:
// Appends `count` copies of `ch`.
void Append(size_t count, char ch) { sink_->Append(count, ch); }
void Append(string_view v) { sink_->Append(v); }
// Appends the first `precision` bytes of `v`. If this is less than
// `width`, spaces will be appended first (if `left` is false), or
// after (if `left` is true) to ensure the total amount appended is
// at least `width`.
bool PutPaddedString(string_view v, int width, int precision, bool left) {
return sink_->PutPaddedString(v, width, precision, left);
}
private:
friend str_format_internal::FormatSinkImpl;
explicit FormatSink(str_format_internal::FormatSinkImpl* s) : sink_(s) {}
str_format_internal::FormatSinkImpl* sink_;
};
// FormatConvertResult
//
// Indicates whether a call to AbslFormatConvert() was successful.
// This return type informs the StrFormat extension framework (through
// ADL but using the return type) of what conversion characters are supported.
// It is strongly discouraged to return {false}, as this will result in an
// empty string in StrFormat.
template <FormatConversionCharSet C>
struct FormatConvertResult {
bool value;
};
ABSL_NAMESPACE_END
} // namespace absl

@ -16,7 +16,6 @@ namespace absl {
ABSL_NAMESPACE_BEGIN
namespace {
using str_format_internal::FormatArgImpl;
using str_format_internal::FormatConversionCharSetInternal;
using FormatEntryPointTest = ::testing::Test;
@ -537,46 +536,90 @@ TEST_F(ParsedFormatTest, SimpleUncheckedIncorrect) {
EXPECT_FALSE((ParsedFormat<'s', 'd', 'g'>::New(format)));
}
using absl::str_format_internal::FormatConversionCharSet;
#if defined(__cpp_nontype_template_parameter_auto)
template <auto T>
std::true_type IsValidParsedFormatArgTest(ParsedFormat<T>*);
template <auto T>
std::false_type IsValidParsedFormatArgTest(...);
template <auto T>
using IsValidParsedFormatArg = decltype(IsValidParsedFormatArgTest<T>(nullptr));
TEST_F(ParsedFormatTest, OnlyValidTypesAllowed) {
ASSERT_TRUE(IsValidParsedFormatArg<'c'>::value);
ASSERT_TRUE(IsValidParsedFormatArg<FormatConversionCharSet::d>::value);
ASSERT_TRUE(IsValidParsedFormatArg<absl::FormatConversionCharSet::d |
absl::FormatConversionCharSet::x>::value);
ASSERT_TRUE(
IsValidParsedFormatArg<absl::FormatConversionCharSet::kIntegral>::value);
// This is an easy mistake to make, however, this will reduce to an integer
// which has no meaning, so we need to ensure it doesn't compile.
ASSERT_FALSE(IsValidParsedFormatArg<'x' | 'd'>::value);
// For now, we disallow construction based on ConversionChar (rather than
// CharSet)
ASSERT_FALSE(IsValidParsedFormatArg<absl::FormatConversionChar::d>::value);
}
TEST_F(ParsedFormatTest, ExtendedTyping) {
EXPECT_FALSE(ParsedFormat<FormatConversionCharSet::d>::New(""));
ASSERT_TRUE(ParsedFormat<absl::FormatConversionCharSet::d>::New("%d"));
auto v1 = ParsedFormat<'d', absl::FormatConversionCharSet::s>::New("%d%s");
ASSERT_TRUE(v1);
auto v2 = ParsedFormat<absl::FormatConversionCharSet::d, 's'>::New("%d%s");
ASSERT_TRUE(v2);
auto v3 = ParsedFormat<absl::FormatConversionCharSet::d |
absl::FormatConversionCharSet::s,
's'>::New("%d%s");
ASSERT_TRUE(v3);
auto v4 = ParsedFormat<absl::FormatConversionCharSet::d |
absl::FormatConversionCharSet::s,
's'>::New("%s%s");
ASSERT_TRUE(v4);
}
#endif
TEST_F(ParsedFormatTest, UncheckedCorrect) {
auto f =
ExtendedParsedFormat<FormatConversionCharSetInternal::d>::New("ABC%dDEF");
ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New("ABC%dDEF");
ASSERT_TRUE(f);
EXPECT_EQ("[ABC]{d:1$d}[DEF]", SummarizeParsedFormat(*f));
std::string format = "%sFFF%dZZZ%f";
auto f2 = ExtendedParsedFormat<
FormatConversionCharSetInternal::kString,
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::kFloating>::New(format);
absl::FormatConversionCharSet::kString, absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::kFloating>::New(format);
ASSERT_TRUE(f2);
EXPECT_EQ("{s:1$s}[FFF]{d:2$d}[ZZZ]{f:3$f}", SummarizeParsedFormat(*f2));
f2 = ExtendedParsedFormat<
FormatConversionCharSetInternal::kString,
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::kFloating>::New("%s %d %f");
absl::FormatConversionCharSet::kString, absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::kFloating>::New("%s %d %f");
ASSERT_TRUE(f2);
EXPECT_EQ("{s:1$s}[ ]{d:2$d}[ ]{f:3$f}", SummarizeParsedFormat(*f2));
auto star =
ExtendedParsedFormat<FormatConversionCharSetInternal::kStar,
FormatConversionCharSetInternal::d>::New("%*d");
ExtendedParsedFormat<absl::FormatConversionCharSet::kStar,
absl::FormatConversionCharSet::d>::New("%*d");
ASSERT_TRUE(star);
EXPECT_EQ("{*d:2$1$*d}", SummarizeParsedFormat(*star));
auto dollar = ExtendedParsedFormat<
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::s>::New("%2$s %1$d");
auto dollar =
ExtendedParsedFormat<absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::s>::New("%2$s %1$d");
ASSERT_TRUE(dollar);
EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}", SummarizeParsedFormat(*dollar));
// with reuse
dollar = ExtendedParsedFormat<
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::s>::New("%2$s %1$d %1$d");
absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::s>::New("%2$s %1$d %1$d");
ASSERT_TRUE(dollar);
EXPECT_EQ("{2$s:2$s}[ ]{1$d:1$d}[ ]{1$d:1$d}",
SummarizeParsedFormat(*dollar));
@ -584,62 +627,61 @@ TEST_F(ParsedFormatTest, UncheckedCorrect) {
TEST_F(ParsedFormatTest, UncheckedIgnoredArgs) {
EXPECT_FALSE(
(ExtendedParsedFormat<FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::s>::New("ABC")));
(ExtendedParsedFormat<absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::s>::New("ABC")));
EXPECT_FALSE(
(ExtendedParsedFormat<FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::s>::New("%dABC")));
EXPECT_FALSE((ExtendedParsedFormat<
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::s>::New("ABC%2$s")));
(ExtendedParsedFormat<absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::s>::New("%dABC")));
EXPECT_FALSE(
(ExtendedParsedFormat<absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::s>::New("ABC%2$s")));
auto f = ExtendedParsedFormat<
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::s>::NewAllowIgnored("ABC");
absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::s>::NewAllowIgnored("ABC");
ASSERT_TRUE(f);
EXPECT_EQ("[ABC]", SummarizeParsedFormat(*f));
f = ExtendedParsedFormat<
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::s>::NewAllowIgnored("%dABC");
absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::s>::NewAllowIgnored("%dABC");
ASSERT_TRUE(f);
EXPECT_EQ("{d:1$d}[ABC]", SummarizeParsedFormat(*f));
f = ExtendedParsedFormat<
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::s>::NewAllowIgnored("ABC%2$s");
absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::s>::NewAllowIgnored("ABC%2$s");
ASSERT_TRUE(f);
EXPECT_EQ("[ABC]{2$s:2$s}", SummarizeParsedFormat(*f));
}
TEST_F(ParsedFormatTest, UncheckedMultipleTypes) {
auto dx = ExtendedParsedFormat<
FormatConversionCharSetInternal::d |
FormatConversionCharSetInternal::x>::New("%1$d %1$x");
auto dx =
ExtendedParsedFormat<absl::FormatConversionCharSet::d |
absl::FormatConversionCharSet::x>::New("%1$d %1$x");
EXPECT_TRUE(dx);
EXPECT_EQ("{1$d:1$d}[ ]{1$x:1$x}", SummarizeParsedFormat(*dx));
dx = ExtendedParsedFormat<FormatConversionCharSetInternal::d |
FormatConversionCharSetInternal::x>::New("%1$d");
dx = ExtendedParsedFormat<absl::FormatConversionCharSet::d |
absl::FormatConversionCharSet::x>::New("%1$d");
EXPECT_TRUE(dx);
EXPECT_EQ("{1$d:1$d}", SummarizeParsedFormat(*dx));
}
TEST_F(ParsedFormatTest, UncheckedIncorrect) {
EXPECT_FALSE(
ExtendedParsedFormat<FormatConversionCharSetInternal::d>::New(""));
EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New(""));
EXPECT_FALSE(ExtendedParsedFormat<FormatConversionCharSetInternal::d>::New(
EXPECT_FALSE(ExtendedParsedFormat<absl::FormatConversionCharSet::d>::New(
"ABC%dDEF%d"));
std::string format = "%sFFF%dZZZ%f";
EXPECT_FALSE(
(ExtendedParsedFormat<FormatConversionCharSetInternal::s,
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::g>::New(format)));
(ExtendedParsedFormat<absl::FormatConversionCharSet::s,
absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::g>::New(format)));
}
TEST_F(ParsedFormatTest, RegressionMixPositional) {
EXPECT_FALSE((ExtendedParsedFormat<
FormatConversionCharSetInternal::d,
FormatConversionCharSetInternal::o>::New("%1$d %o")));
EXPECT_FALSE(
(ExtendedParsedFormat<absl::FormatConversionCharSet::d,
absl::FormatConversionCharSet::o>::New("%1$d %o")));
}
using FormatWrapperTest = ::testing::Test;
@ -664,6 +706,38 @@ TEST_F(FormatWrapperTest, ParsedFormat) {
ABSL_NAMESPACE_END
} // namespace absl
using FormatExtensionTest = ::testing::Test;
struct Point {
friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString |
absl::FormatConversionCharSet::kIntegral>
AbslFormatConvert(const Point& p, const absl::FormatConversionSpec& spec,
absl::FormatSink* s) {
if (spec.conversion_char() == absl::FormatConversionChar::s) {
s->Append(absl::StrCat("x=", p.x, " y=", p.y));
} else {
s->Append(absl::StrCat(p.x, ",", p.y));
}
return {true};
}
int x = 10;
int y = 20;
};
TEST_F(FormatExtensionTest, AbslFormatConvertExample) {
Point p;
EXPECT_EQ(absl::StrFormat("a %s z", p), "a x=10 y=20 z");
EXPECT_EQ(absl::StrFormat("a %d z", p), "a 10,20 z");
// Typed formatting will fail to compile an invalid format.
// StrFormat("%f", p); // Does not compile.
std::string actual;
absl::UntypedFormatSpec f1("%f");
// FormatUntyped will return false for bad character.
EXPECT_FALSE(absl::FormatUntyped(&actual, f1, {absl::FormatArg(p)}));
}
// Some codegen thunks that we can use to easily dump the generated assembly for
// different StrFormat calls.

Loading…
Cancel
Save