1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[Support] Introduce llvm::formatv() function.

This introduces a new type-safe general purpose formatting
library.  It provides compile-time type safety, does not require
a format specifier (since the type is deduced), and provides
mechanisms for extending the format capability to user defined
types, and overriding the formatting behavior for existing types.

This patch additionally adds documentation for the API to the
LLVM programmer's manual.

Mailing List Thread:
http://lists.llvm.org/pipermail/llvm-dev/2016-October/105836.html

Differential Revision: https://reviews.llvm.org/D25587

llvm-svn: 286682
This commit is contained in:
Zachary Turner 2016-11-11 23:57:40 +00:00
parent 4782867f82
commit 1cad744181
17 changed files with 1893 additions and 47 deletions

View File

@ -263,6 +263,136 @@ almost never be stored or mentioned directly. They are intended solely for use
when defining a function which should be able to efficiently accept concatenated
strings.
.. _formatting_strings:
Formatting strings (the ``formatv`` function)
---------------------------------------------
While LLVM doesn't necessarily do a lot of string manipulation and parsing, it
does do a lot of string formatting. From diagnostic messages, to llvm tool
outputs such as ``llvm-readobj`` to printing verbose disassembly listings and
LLDB runtime logging, the need for string formatting is pervasive.
The ``formatv`` is similar in spirit to ``printf``, but uses a different syntax
which borrows heavily from Python and C#. Unlike ``printf`` it deduces the type
to be formatted at compile time, so it does not need a format specifier such as
``%d``. This reduces the mental overhead of trying to construct portable format
strings, especially for platform-specific types like ``size_t`` or pointer types.
Unlike both ``printf`` and Python, it additionally fails to compile if LLVM does
not know how to format the type. These two properties ensure that the function
is both safer and simpler to use than traditional formatting methods such as
the ``printf`` family of functions.
Simple formatting
^^^^^^^^^^^^^^^^^
A call to ``formatv`` involves a single **format string** consisting of 0 or more
**replacement sequences**, followed by a variable length list of **replacement values**.
A replacement sequence is a string of the form ``{N[[,align]:style]}``.
``N`` refers to the 0-based index of the argument from the list of replacement
values. Note that this means it is possible to reference the same parameter
multiple times, possibly with different style and/or alignment options, in any order.
``align`` is an optional string specifying the width of the field to format
the value into, and the alignment of the value within the field. It is specified as
an optional **alignment style** followed by a positive integral **field width**. The
alignment style can be one of the characters ``-`` (left align), ``=`` (center align),
or ``+`` (right align). The default is right aligned.
``style`` is an optional string consisting of a type specific that controls the
formatting of the value. For example, to format a floating point value as a percentage,
you can use the style option ``P``.
Custom formatting
^^^^^^^^^^^^^^^^^
There are two ways to customize the formatting behavior for a type.
1. Provide a template specialization of ``llvm::format_provider<T>`` for your
type ``T`` with the appropriate static format method.
.. code-block:: c++
namespace llvm {
template<>
struct format_provider<MyFooBar> {
static void format(const MyFooBar &V, raw_ostream &Stream, StringRef Style) {
// Do whatever is necessary to format `V` into `Stream`
}
};
void foo() {
MyFooBar X;
std::string S = formatv("{0}", X);
}
}
This is a useful extensibility mechanism for adding support for formatting your own
custom types with your own custom Style options. But it does not help when you want
to extend the mechanism for formatting a type that the library already knows how to
format. For that, we need something else.
2. Provide a **format adapter** with a non-static format method.
.. code-block:: c++
namespace anything {
struct format_int_custom {
int N;
explicit format_int_custom(int N) : N(N) {}
void format(llvm::raw_ostream &Stream, StringRef Style) {
// Do whatever is necessary to format ``N`` into ``Stream``
}
};
}
namespace llvm {
void foo() {
std::string S = formatv("{0}", anything::format_int_custom(42));
}
}
If the search for a specialization of ``format_provider<T>`` for the given type
fails, ``formatv`` will subsequently check the argument for an instance method
named ``format`` with the signature described above. If so, it will call the
``format`` method on the argument passing in the specified style. This allows
one to provide custom formatting of any type, including one which already has
a builtin format provider.
``formatv`` Examples
^^^^^^^^^^^^^^^^^^^^
Below is intended to provide an incomplete set of examples demonstrating
the usage of ``formatv``. More information can be found by reading the
doxygen documentation or by looking at the unit test suite.
.. code-block:: c++
std::string S;
// Simple formatting of basic types and implicit string conversion.
S = formatv("{0} ({1:P})", 7, 0.35); // S == "7 (35.00%)"
// Out-of-order referencing and multi-referencing
outs() << formatv("{0} {2} {1} {0}", 1, "test", 3); // prints "1 3 test 1"
// Left, right, and center alignment
S = formatv("{0,7}", 'a'); // S == " a";
S = formatv("{0,-7}", 'a'); // S == "a ";
S = formatv("{0,=7}", 'a'); // S == " a ";
S = formatv("{0,+7}", 'a'); // S == " a";
// Custom styles
S = formatv("{0:N} - {0:x} - {1:E}", 12345, 123908342); // S == "12,345 - 0x3039 - 1.24E8"
// Adapters
S = formatv("{0}", fmt_align(42, AlignStyle::Center, 7)); // S == " 42 "
S = formatv("{0}", fmt_repeat("hi", 3)); // S == "hihihi"
S = formatv("{0}", fmt_pad("hi", 2, 6)); // S == " hi "
// Ranges
std::vector<int> V = {8, 9, 10};
S = formatv("{0}", make_range(V.begin(), V.end())); // S == "8, 9, 10"
S = formatv("{0:$[+]}", make_range(V.begin(), V.end())); // S == "8+9+10"
S = formatv("{0:$[ + ]@[x]}", make_range(V.begin(), V.end())); // S == "0x8 + 0x9 + 0xA"
.. _error_apis:
Error handling

View File

@ -33,6 +33,11 @@
#include "llvm/Support/Compiler.h"
namespace llvm {
// Only used by compiler if both template types are the same. Useful when
// using SFINAE to test for the existence of member functions.
template <typename T, T> struct SameType;
namespace detail {
template <typename RangeT>
@ -477,6 +482,18 @@ struct index_sequence_for : build_index_impl<sizeof...(Ts)> {};
template <int N> struct rank : rank<N - 1> {};
template <> struct rank<0> {};
/// \brief traits class for checking whether type T is one of any of the given
/// types in the variadic list.
template <typename T, typename... Ts> struct is_one_of {
static const bool value = false;
};
template <typename T, typename U, typename... Ts>
struct is_one_of<T, U, Ts...> {
static const bool value =
std::is_same<T, U>::value || is_one_of<T, Ts...>::value;
};
//===----------------------------------------------------------------------===//
// Extra additions for arrays
//===----------------------------------------------------------------------===//

View File

@ -0,0 +1,92 @@
//===- FormatAdapters.h - Formatters for common LLVM types -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_FORMATADAPTERS_H
#define LLVM_SUPPORT_FORMATADAPTERS_H
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FormatCommon.h"
#include "llvm/Support/FormatVariadicDetails.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
template <typename T> class AdapterBase {
protected:
explicit AdapterBase(T &&Item) : Item(Item) {}
T Item;
static_assert(!detail::uses_missing_provider<T>::value,
"Item does not have a format provider!");
};
namespace detail {
template <typename T> class AlignAdapter : public AdapterBase<T> {
AlignStyle Where;
size_t Amount;
public:
AlignAdapter(T &&Item, AlignStyle Where, size_t Amount)
: AdapterBase(std::forward<T>(Item)), Where(Where), Amount(Amount) {}
void format(llvm::raw_ostream &Stream, StringRef Style) {
auto Wrapper = detail::build_format_wrapper(std::forward<T>(Item));
FmtAlign(Wrapper, Where, Amount).format(Stream, Style);
}
};
template <typename T> class PadAdapter : public AdapterBase<T> {
size_t Left;
size_t Right;
public:
PadAdapter(T &&Item, size_t Left, size_t Right)
: AdapterBase(std::forward<T>(Item)), Left(Left), Right(Right) {}
void format(llvm::raw_ostream &Stream, StringRef Style) {
auto Wrapper = detail::build_format_wrapper(std::forward<T>(Item));
Stream.indent(Left);
Wrapper.format(Stream, Style);
Stream.indent(Right);
}
};
template <typename T> class RepeatAdapter : public AdapterBase<T> {
size_t Count;
public:
RepeatAdapter(T &&Item, size_t Count)
: AdapterBase(std::forward<T>(Item)), Count(Count) {}
void format(llvm::raw_ostream &Stream, StringRef Style) {
auto Wrapper = detail::build_format_wrapper(std::forward<T>(Item));
for (size_t I = 0; I < Count; ++I) {
Wrapper.format(Stream, Style);
}
}
};
}
template <typename T>
detail::AlignAdapter<T> fmt_align(T &&Item, AlignStyle Where, size_t Amount) {
return detail::AlignAdapter<T>(std::forward<T>(Item), Where, Amount);
}
template <typename T>
detail::PadAdapter<T> fmt_pad(T &&Item, size_t Left, size_t Right) {
return detail::PadAdapter<T>(std::forward<T>(Item), Left, Right);
}
template <typename T>
detail::RepeatAdapter<T> fmt_repeat(T &&Item, size_t Count) {
return detail::RepeatAdapter<T>(std::forward<T>(Item), Count);
}
}
#endif

View File

@ -0,0 +1,69 @@
//===- FormatAdapters.h - Formatters for common LLVM types -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_FORMATCOMMON_H
#define LLVM_SUPPORT_FORMATCOMMON_H
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FormatVariadicDetails.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
enum class AlignStyle { Left, Center, Right };
struct FmtAlign {
detail::format_wrapper &Wrapper;
AlignStyle Where;
size_t Amount;
FmtAlign(detail::format_wrapper &Wrapper, AlignStyle Where, size_t Amount)
: Wrapper(Wrapper), Where(Where), Amount(Amount) {}
void format(raw_ostream &S, StringRef Options) {
// If we don't need to align, we can format straight into the underlying
// stream. Otherwise we have to go through an intermediate stream first
// in order to calculate how long the output is so we can align it.
// TODO: Make the format method return the number of bytes written, that
// way we can also skip the intermediate stream for left-aligned output.
if (Amount == 0) {
Wrapper.format(S, Options);
return;
}
SmallString<64> Item;
raw_svector_ostream Stream(Item);
Wrapper.format(Stream, Options);
if (Amount <= Item.size()) {
S << Item;
return;
}
size_t PadAmount = Amount - Item.size();
switch (Where) {
case AlignStyle::Left:
S << Item;
S.indent(PadAmount);
break;
case AlignStyle::Center: {
size_t X = PadAmount / 2;
S.indent(X);
S << Item;
S.indent(PadAmount - X);
break;
}
default:
S.indent(PadAmount);
S << Item;
break;
}
}
};
}
#endif

View File

@ -0,0 +1,411 @@
//===- FormatProviders.h - Formatters for common LLVM types -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements format providers for many common LLVM types, for example
// allowing precision and width specifiers for scalar and string types.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_FORMATPROVIDERS_H
#define LLVM_SUPPORT_FORMATPROVIDERS_H
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/FormatVariadicDetails.h"
#include "llvm/Support/NativeFormatting.h"
#include <type_traits>
namespace llvm {
namespace detail {
template <typename T>
struct use_integral_formatter
: public std::integral_constant<
bool, is_one_of<T, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
int64_t, uint64_t>::value> {};
template <typename T>
struct use_char_formatter
: public std::integral_constant<bool, std::is_same<T, char>::value> {};
template <typename T>
struct is_cstring
: public std::integral_constant<bool,
is_one_of<T, char *, const char *>::value> {
};
template <typename T>
struct use_string_formatter
: public std::integral_constant<
bool, is_one_of<T, llvm::StringRef, std::string>::value ||
is_cstring<T>::value> {};
template <typename T>
struct use_pointer_formatter
: public std::integral_constant<bool, std::is_pointer<T>::value &&
!is_cstring<T>::value> {};
template <typename T>
struct use_double_formatter
: public std::integral_constant<bool, std::is_floating_point<T>::value> {};
class HelperFunctions {
protected:
static Optional<size_t> parseNumericPrecision(StringRef Str) {
size_t Prec;
Optional<size_t> Result;
if (Str.empty())
Result = None;
else if (Str.getAsInteger(10, Prec)) {
assert(false && "Invalid precision specifier");
Result = None;
} else {
assert(Prec < 100 && "Precision out of range");
Result = std::min(99u, Prec);
}
return Result;
}
static bool consumeHexStyle(StringRef &Str, HexPrintStyle &Style) {
if (!Str.startswith_lower("x"))
return false;
if (Str.consume_front("x-"))
Style = HexPrintStyle::Lower;
else if (Str.consume_front("X-"))
Style = HexPrintStyle::Upper;
else if (Str.consume_front("x+") || Str.consume_front("x"))
Style = HexPrintStyle::PrefixLower;
else if (Str.consume_front("X+") || Str.consume_front("X"))
Style = HexPrintStyle::PrefixUpper;
return true;
}
static size_t consumeNumHexDigits(StringRef &Str, HexPrintStyle Style,
size_t Default) {
Str.consumeInteger(10, Default);
if (isPrefixedHexStyle(Style))
Default += 2;
return Default;
}
};
}
/// Implementation of format_provider<T> for integral arithmetic types.
///
/// The options string of an integral type has the grammar:
///
/// integer_options :: [style][digits]
/// style :: <see table below>
/// digits :: <non-negative integer> 0-99
///
/// ==========================================================================
/// | style | Meaning | Example | Digits Meaning |
/// --------------------------------------------------------------------------
/// | | | Input | Output | |
/// ==========================================================================
/// | x- | Hex no prefix, lower | 42 | 2a | Minimum # digits |
/// | X- | Hex no prefix, upper | 42 | 2A | Minimum # digits |
/// | x+ / x | Hex + prefix, lower | 42 | 0x2a | Minimum # digits |
/// | X+ / X | Hex + prefix, upper | 42 | 0x2A | Minimum # digits |
/// | N / n | Digit grouped number | 123456 | 123,456 | Ignored |
/// | D / d | Integer | 100000 | 100000 | Ignored |
/// | (empty) | Same as D / d | | | |
/// ==========================================================================
///
template <typename T>
struct format_provider<
T, typename std::enable_if<detail::use_integral_formatter<T>::value>::type>
: public detail::HelperFunctions {
private:
public:
static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) {
HexPrintStyle HS;
size_t Digits = 0;
if (consumeHexStyle(Style, HS)) {
Digits = consumeNumHexDigits(Style, HS, 0);
write_hex(Stream, V, HS, Digits);
return;
}
IntegerStyle IS = IntegerStyle::Integer;
if (Style.consume_front("N") || Style.consume_front("n"))
IS = IntegerStyle::Number;
else if (Style.consume_front("D") || Style.consume_front("d"))
IS = IntegerStyle::Integer;
Style.consumeInteger(10, Digits);
assert(Style.empty() && "Invalid integral format style!");
write_integer(Stream, V, Digits, IS);
}
};
/// Implementation of format_provider<T> for integral pointer types.
///
/// The options string of a pointer type has the grammar:
///
/// pointer_options :: [style][precision]
/// style :: <see table below>
/// digits :: <non-negative integer> 0-sizeof(void*)
///
/// ==========================================================================
/// | S | Meaning | Example |
/// --------------------------------------------------------------------------
/// | | | Input | Output |
/// ==========================================================================
/// | x- | Hex no prefix, lower | 0xDEADBEEF | deadbeef |
/// | X- | Hex no prefix, upper | 0xDEADBEEF | DEADBEEF |
/// | x+ / x | Hex + prefix, lower | 0xDEADBEEF | 0xdeadbeef |
/// | X+ / X | Hex + prefix, upper | 0xDEADBEEF | 0xDEADBEEF |
/// | (empty) | Same as X+ / X | | |
/// ==========================================================================
///
/// The default precision is the number of nibbles in a machine word, and in all
/// cases indicates the minimum number of nibbles to print.
template <typename T>
struct format_provider<
T, typename std::enable_if<detail::use_pointer_formatter<T>::value>::type>
: public detail::HelperFunctions {
private:
public:
static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) {
HexPrintStyle HS = HexPrintStyle::PrefixUpper;
consumeHexStyle(Style, HS);
size_t Digits = consumeNumHexDigits(Style, HS, sizeof(void *) * 2);
write_hex(Stream, reinterpret_cast<std::uintptr_t>(V), HS, Digits);
}
};
/// Implementation of format_provider<T> for c-style strings and string
/// objects such as std::string and llvm::StringRef.
///
/// The options string of a string type has the grammar:
///
/// string_options :: [length]
///
/// where `length` is an optional integer specifying the maximum number of
/// characters in the string to print. If `length` is omitted, the string is
/// printed up to the null terminator.
template <typename T>
struct format_provider<
T, typename std::enable_if<detail::use_string_formatter<T>::value>::type> {
static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) {
size_t N = StringRef::npos;
if (!Style.empty() && Style.getAsInteger(10, N)) {
assert(false && "Style is not a valid integer");
}
llvm::StringRef S(V);
Stream << S.substr(0, N);
}
};
/// Implementation of format_provider<T> for characters.
///
/// The options string of a character type has the grammar:
///
/// char_options :: (empty) | [integer_options]
///
/// If `char_options` is empty, the character is displayed as an ASCII
/// character. Otherwise, it is treated as an integer options string.
///
template <typename T>
struct format_provider<
T, typename std::enable_if<detail::use_char_formatter<T>::value>::type> {
static void format(const char &V, llvm::raw_ostream &Stream,
StringRef Style) {
if (Style.empty())
Stream << V;
else {
int X = static_cast<int>(V);
format_provider<int>::format(X, Stream, Style);
}
}
};
/// Implementation of format_provider<T> for type `bool`
///
/// The options string of a boolean type has the grammar:
///
/// bool_options :: "" | "Y" | "y" | "D" | "d" | "T" | "t"
///
/// ==================================
/// | C | Meaning |
/// ==================================
/// | Y | YES / NO |
/// | y | yes / no |
/// | D / d | Integer 0 or 1 |
/// | T | TRUE / FALSE |
/// | t | true / false |
/// | (empty) | Equivalent to 't' |
/// ==================================
template <> struct format_provider<bool> {
static void format(const bool &B, llvm::raw_ostream &Stream,
StringRef Style) {
Stream << StringSwitch<const char *>(Style)
.Case("Y", B ? "YES" : "NO")
.Case("y", B ? "yes" : "no")
.CaseLower("D", B ? "1" : "0")
.Case("T", B ? "TRUE" : "FALSE")
.Cases("t", "", B ? "true" : "false")
.Default(B ? "1" : "0");
}
};
/// Implementation of format_provider<T> for floating point types.
///
/// The options string of a floating point type has the format:
///
/// float_options :: [style][precision]
/// style :: <see table below>
/// precision :: <non-negative integer> 0-99
///
/// =====================================================
/// | style | Meaning | Example |
/// -----------------------------------------------------
/// | | | Input | Output |
/// =====================================================
/// | P / p | Percentage | 0.05 | 5.00% |
/// | F / f | Fixed point | 1.0 | 1.00 |
/// | E | Exponential with E | 100000 | 1.0E+05 |
/// | e | Exponential with e | 100000 | 1.0e+05 |
/// | (empty) | Same as F / f | | |
/// =====================================================
///
/// The default precision is 6 for exponential (E / e) and 2 for everything
/// else.
template <typename T>
struct format_provider<
T, typename std::enable_if<detail::use_double_formatter<T>::value>::type>
: public detail::HelperFunctions {
static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) {
FloatStyle S;
if (Style.consume_front("P") || Style.consume_front("p"))
S = FloatStyle::Percent;
else if (Style.consume_front("F") || Style.consume_front("f"))
S = FloatStyle::Fixed;
else if (Style.consume_front("E"))
S = FloatStyle::ExponentUpper;
else if (Style.consume_front("e"))
S = FloatStyle::Exponent;
else
S = FloatStyle::Fixed;
Optional<size_t> Precision = parseNumericPrecision(Style);
if (!Precision.hasValue())
Precision = getDefaultPrecision(S);
write_double(Stream, static_cast<double>(V), S, Precision);
}
};
namespace detail {
template <typename IterT>
using IterValue = typename std::iterator_traits<IterT>::value_type;
template <typename IterT>
struct range_item_has_provider
: public std::integral_constant<
bool, !uses_missing_provider<IterValue<IterT>>::value> {};
}
/// Implementation of format_provider<T> for ranges.
///
/// This will print an arbitrary range as a delimited sequence of items.
///
/// The options string of a range type has the grammar:
///
/// range_style ::= [separator] [element_style]
/// separator ::= "$" delimeted_expr
/// element_style ::= "@" delimeted_expr
/// delimeted_expr ::= "[" expr "]" | "(" expr ")" | "<" expr ">"
/// expr ::= <any string not containing delimeter>
///
/// where the separator expression is the string to insert between consecutive
/// items in the range and the argument expression is the Style specification to
/// be used when formatting the underlying type. The default separator if
/// unspecified is ' ' (space). The syntax of the argument expression follows
/// whatever grammar is dictated by the format provider or format adapter used
/// to format the value type.
///
/// Note that attempting to format an `iterator_range<T>` where no format
/// provider can be found for T will result in a compile error.
///
template <typename IterT> class format_provider<llvm::iterator_range<IterT>> {
using value = typename std::iterator_traits<IterT>::value_type;
using reference = typename std::iterator_traits<IterT>::reference;
static StringRef consumeOneOption(StringRef &Style, char Indicator,
StringRef Default) {
if (Style.empty())
return Default;
if (Style.front() != Indicator)
return Default;
Style = Style.drop_front();
if (Style.empty()) {
assert(false && "Invalid range style");
return Default;
}
std::vector<const char *> Delims = {"[]", "<>", "()"};
for (const char *D : Delims) {
if (Style.front() != D[0])
continue;
size_t End = Style.find_first_of(D[1]);
if (End == StringRef::npos) {
assert(false && "Missing range option end delimeter!");
return Default;
}
StringRef Result = Style.slice(1, End);
Style = Style.drop_front(End + 1);
return Result;
}
assert(false && "Invalid range style!");
return Default;
}
static std::pair<StringRef, StringRef> parseOptions(StringRef Style) {
StringRef Sep = consumeOneOption(Style, '$', ", ");
StringRef Args = consumeOneOption(Style, '@', "");
assert(Style.empty() && "Unexpected text in range option string!");
return std::make_pair(Sep, Args);
}
public:
static_assert(detail::range_item_has_provider<IterT>::value,
"Range value_type does not have a format provider!");
static void format(const llvm::iterator_range<IterT> &V,
llvm::raw_ostream &Stream, StringRef Style) {
StringRef Sep;
StringRef ArgStyle;
std::tie(Sep, ArgStyle) = parseOptions(Style);
auto Begin = V.begin();
auto End = V.end();
if (Begin != End) {
auto Wrapper =
detail::build_format_wrapper(std::forward<reference>(*Begin));
Wrapper.format(Stream, ArgStyle);
++Begin;
}
while (Begin != End) {
Stream << Sep;
auto Wrapper =
detail::build_format_wrapper(std::forward<reference>(*Begin));
Wrapper.format(Stream, ArgStyle);
++Begin;
}
}
};
}
#endif

View File

@ -0,0 +1,246 @@
//===- FormatVariadic.h - Efficient type-safe string formatting --*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the formatv() function which can be used with other LLVM
// subsystems to provide printf-like formatting, but with improved safety and
// flexibility. The result of `formatv` is an object which can be streamed to
// a raw_ostream or converted to a std::string or llvm::SmallString.
//
// // Convert to std::string.
// std::string S = formatv("{0} {1}", 1234.412, "test").str();
//
// // Convert to llvm::SmallString
// SmallString<8> S = formatv("{0} {1}", 1234.412, "test").sstr<8>();
//
// // Stream to an existing raw_ostream.
// OS << formatv("{0} {1}", 1234.412, "test");
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_FORMATVARIADIC_H
#define LLVM_SUPPORT_FORMATVARIADIC_H
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/FormatCommon.h"
#include "llvm/Support/FormatProviders.h"
#include "llvm/Support/FormatVariadicDetails.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
#include <tuple>
#include <vector>
namespace llvm {
enum class ReplacementType { Empty, Format, Literal };
struct ReplacementItem {
ReplacementItem() {}
explicit ReplacementItem(StringRef Literal)
: Type(ReplacementType::Literal), Spec(Literal) {}
ReplacementItem(StringRef Spec, size_t Index, size_t Align, AlignStyle Where,
char Pad, StringRef Options)
: Type(ReplacementType::Format), Spec(Spec), Index(Index), Align(Align),
Where(Where), Pad(Pad), Options(Options) {}
ReplacementType Type = ReplacementType::Empty;
StringRef Spec;
size_t Index = 0;
size_t Align = 0;
AlignStyle Where = AlignStyle::Right;
char Pad;
StringRef Options;
};
class formatv_object_base {
protected:
// The parameters are stored in a std::tuple, which does not provide runtime
// indexing capabilities. In order to enable runtime indexing, we use this
// structure to put the parameters into a std::vector. Since the parameters
// are not all the same type, we use some type-erasure by wrapping the
// parameters in a template class that derives from a non-template superclass.
// Essentially, we are converting a std::tuple<Derived<Ts...>> to a
// std::vector<Base*>.
struct create_wrappers {
template <typename... Ts>
std::vector<detail::format_wrapper *> operator()(Ts &... Items) {
return std::vector<detail::format_wrapper *>{&Items...};
}
};
StringRef Fmt;
std::vector<detail::format_wrapper *> Wrappers;
std::vector<ReplacementItem> Replacements;
static bool consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
size_t &Align, char &Pad);
static std::pair<ReplacementItem, StringRef>
splitLiteralAndReplacement(StringRef Fmt);
public:
formatv_object_base(StringRef Fmt, std::size_t ParamCount)
: Fmt(Fmt), Replacements(parseFormatString(Fmt)) {
Wrappers.reserve(ParamCount);
return;
}
void format(raw_ostream &S) const {
for (auto &R : Replacements) {
if (R.Type == ReplacementType::Empty)
continue;
if (R.Type == ReplacementType::Literal) {
S << R.Spec;
continue;
}
if (R.Index >= Wrappers.size()) {
S << R.Spec;
continue;
}
auto W = Wrappers[R.Index];
FmtAlign Align(*W, R.Where, R.Align);
Align.format(S, R.Options);
}
}
static std::vector<ReplacementItem> parseFormatString(StringRef Fmt);
static Optional<ReplacementItem> parseReplacementItem(StringRef Spec);
std::string str() const {
std::string Result;
raw_string_ostream Stream(Result);
Stream << *this;
Stream.flush();
return Result;
}
template <size_t N> llvm::SmallString<N> sstr() const {
SmallString<N> Result;
raw_svector_ostream Stream(Result);
Stream << *this;
return Result;
}
template <size_t N> operator SmallString<N>() const { return sstr<N>(); }
operator std::string() const { return str(); }
};
template <typename Tuple> class formatv_object : public formatv_object_base {
// Storage for the parameter wrappers. Since the base class erases the type
// of the parameters, we have to own the storage for the parameters here, and
// have the base class store type-erased pointers into this tuple.
Tuple Parameters;
public:
formatv_object(StringRef Fmt, Tuple &&Params)
: formatv_object_base(Fmt, std::tuple_size<Tuple>::value),
Parameters(std::move(Params)) {
Wrappers = apply_tuple(create_wrappers(), Parameters);
}
};
// \brief Format text given a format string and replacement parameters.
//
// ===General Description===
//
// Formats textual output. `Fmt` is a string consisting of one or more
// replacement sequences with the following grammar:
//
// rep_field ::= "{" [index] ["," layout] [":" format] "}"
// index ::= <non-negative integer>
// layout ::= [[[char]loc]width]
// format ::= <any string not containing "{" or "}">
// char ::= <any character except "{" or "}">
// loc ::= "-" | "=" | "+"
// width ::= <positive integer>
//
// index - A non-negative integer specifying the index of the item in the
// parameter pack to print. Any other value is invalid.
// layout - A string controlling how the field is laid out within the available
// space.
// format - A type-dependent string used to provide additional options to
// the formatting operation. Refer to the documentation of the
// various individual format providers for per-type options.
// char - The padding character. Defaults to ' ' (space). Only valid if
// `loc` is also specified.
// loc - Where to print the formatted text within the field. Only valid if
// `width` is also specified.
// '-' : The field is left aligned within the available space.
// '=' : The field is centered within the available space.
// '+' : The field is right aligned within the available space (this
// is the default).
// width - The width of the field within which to print the formatted text.
// If this is less than the required length then the `char` and `loc`
// fields are ignored, and the field is printed with no leading or
// trailing padding. If this is greater than the required length,
// then the text is output according to the value of `loc`, and padded
// as appropriate on the left and/or right by `char`.
//
// ===Special Characters===
//
// The characters '{' and '}' are reserved and cannot appear anywhere within a
// replacement sequence. Outside of a replacement sequence, in order to print
// a literal '{' or '}' it must be doubled -- "{{" to print a literal '{' and
// "}}" to print a literal '}'.
//
// ===Parameter Indexing===
// `index` specifies the index of the paramter in the parameter pack to format
// into the output. Note that it is possible to refer to the same parameter
// index multiple times in a given format string. This makes it possible to
// output the same value multiple times without passing it multiple times to the
// function. For example:
//
// formatv("{0} {1} {0}", "a", "bb")
//
// would yield the string "abba". This can be convenient when it is expensive
// to compute the value of the parameter, and you would otherwise have had to
// save it to a temporary.
//
// ===Formatter Search===
//
// For a given parameter of type T, the following steps are executed in order
// until a match is found:
//
// 1. If the parameter is of class type, and contains a method
// void format(raw_ostream &Stream, StringRef Options)
// Then this method is invoked to produce the formatted output. The
// implementation should write the formatted text into `Stream`.
// 2. If there is a suitable template specialization of format_provider<>
// for type T containing a method whose signature is:
// void format(const T &Obj, raw_ostream &Stream, StringRef Options)
// Then this method is invoked as described in Step 1.
//
// If a match cannot be found through either of the above methods, a compiler
// error is generated.
//
// ===Invalid Format String Handling===
//
// In the case of a format string which does not match the grammar described
// above, the output is undefined. With asserts enabled, LLVM will trigger an
// assertion. Otherwise, it will try to do something reasonable, but in general
// the details of what that is are undefined.
//
template <typename... Ts>
inline auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object<decltype(
std::make_tuple(detail::build_format_wrapper(std::forward<Ts>(Vals))...))> {
using ParamTuple = decltype(
std::make_tuple(detail::build_format_wrapper(std::forward<Ts>(Vals))...));
return formatv_object<ParamTuple>(
Fmt,
std::make_tuple(detail::build_format_wrapper(std::forward<Ts>(Vals))...));
}
} // end namespace llvm
#endif

View File

@ -0,0 +1,149 @@
//===- FormatVariadicDetails.h - Helpers for FormatVariadic.h ----*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_FORMATVARIADIC_DETAILS_H
#define LLVM_SUPPORT_FORMATVARIADIC_DETAILS_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include <type_traits>
namespace llvm {
template <typename T, typename Enable = void> struct format_provider {};
namespace detail {
class format_wrapper {
protected:
~format_wrapper() {}
public:
virtual void format(llvm::raw_ostream &S, StringRef Options) = 0;
};
template <typename T> class member_format_wrapper : public format_wrapper {
T Item;
public:
explicit member_format_wrapper(T &&Item) : Item(Item) {}
void format(llvm::raw_ostream &S, StringRef Options) override {
Item.format(S, Options);
}
};
template <typename T> class provider_format_wrapper : public format_wrapper {
T Item;
public:
explicit provider_format_wrapper(T &&Item) : Item(Item) {}
void format(llvm::raw_ostream &S, StringRef Options) override {
format_provider<typename std::decay<T>::type>::format(Item, S, Options);
}
};
template <typename T> class missing_format_wrapper : public format_wrapper {
public:
missing_format_wrapper() {
static_assert(false, "T does not have a format_provider");
}
void format(llvm::raw_ostream &S, StringRef Options) override {}
};
// Test if T is a class that contains a member function with the signature:
//
// void format(raw_ostream &, StringRef);
//
template <class T, class Enable = void> class has_FormatMember {
public:
static bool const value = false;
};
template <class T>
class has_FormatMember<T,
typename std::enable_if<std::is_class<T>::value>::type> {
using Signature_format = void (T::*)(llvm::raw_ostream &S, StringRef Options);
template <typename U>
static char test2(SameType<Signature_format, &U::format> *);
template <typename U> static double test2(...);
public:
static bool const value = (sizeof(test2<T>(nullptr)) == 1);
};
// Test if format_provider<T> is defined on T and contains a member function
// with the signature:
// static void format(const T&, raw_stream &, StringRef);
//
template <class T> class has_FormatProvider {
public:
using Decayed = typename std::decay<T>::type;
typedef void (*Signature_format)(const Decayed &, llvm::raw_ostream &,
StringRef);
template <typename U>
static char test(SameType<Signature_format, &U::format> *);
template <typename U> static double test(...);
static bool const value =
(sizeof(test<llvm::format_provider<Decayed>>(nullptr)) == 1);
};
// Simple template that decides whether a type T should use the member-function
// based format() invocation.
template <typename T>
struct uses_format_member
: public std::integral_constant<bool, has_FormatMember<T>::value> {};
// Simple template that decides whether a type T should use the format_provider
// based format() invocation. The member function takes priority, so this test
// will only be true if there is not ALSO a format member.
template <typename T>
struct uses_format_provider
: public std::integral_constant<bool, !has_FormatMember<T>::value &&
has_FormatProvider<T>::value> {};
// Simple template that decides whether a type T has neither a member-function
// nor format_provider based implementation that it can use. Mostly used so
// that the compiler spits out a nice diagnostic when a type with no format
// implementation can be located.
template <typename T>
struct uses_missing_provider
: public std::integral_constant<bool, !has_FormatMember<T>::value &&
!has_FormatProvider<T>::value> {};
template <typename T>
typename std::enable_if<uses_format_member<T>::value,
member_format_wrapper<T>>::type
build_format_wrapper(T &&Item) {
return member_format_wrapper<T>(std::forward<T>(Item));
}
template <typename T>
typename std::enable_if<uses_format_provider<T>::value,
provider_format_wrapper<T>>::type
build_format_wrapper(T &&Item) {
return provider_format_wrapper<T>(std::forward<T>(Item));
}
template <typename T>
typename std::enable_if<uses_missing_provider<T>::value,
missing_format_wrapper<T>>::type
build_format_wrapper(T &&Item) {
return missing_format_wrapper<T>();
}
}
}
#endif

View File

@ -25,12 +25,19 @@ enum class HexPrintStyle { Upper, Lower, PrefixUpper, PrefixLower };
size_t getDefaultPrecision(FloatStyle Style);
void write_integer(raw_ostream &S, unsigned int N, IntegerStyle Style);
void write_integer(raw_ostream &S, int N, IntegerStyle Style);
void write_integer(raw_ostream &S, unsigned long N, IntegerStyle Style);
void write_integer(raw_ostream &S, long N, IntegerStyle Style);
void write_integer(raw_ostream &S, unsigned long long N, IntegerStyle Style);
void write_integer(raw_ostream &S, long long N, IntegerStyle Style);
bool isPrefixedHexStyle(HexPrintStyle S);
void write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
IntegerStyle Style);
void write_integer(raw_ostream &S, int N, size_t MinDigits, IntegerStyle Style);
void write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
IntegerStyle Style);
void write_integer(raw_ostream &S, long N, size_t MinDigits,
IntegerStyle Style);
void write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits,
IntegerStyle Style);
void write_integer(raw_ostream &S, long long N, size_t MinDigits,
IntegerStyle Style);
void write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
Optional<size_t> Width = None);

View File

@ -11,6 +11,7 @@
#define LLVM_SUPPORT_YAMLTRAITS_H
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
@ -201,10 +202,6 @@ struct DocumentListTraits {
// static T::value_type& element(IO &io, T &seq, size_t index);
};
// Only used by compiler if both template types are the same
template <typename T, T>
struct SameType;
// Only used for better diagnostics of missing traits
template <typename T>
struct MissingTrait;

View File

@ -20,6 +20,7 @@
#include <system_error>
namespace llvm {
class formatv_object_base;
class format_object_base;
class FormattedString;
class FormattedNumber;
@ -223,6 +224,9 @@ public:
// Formatted output, see the formatHex() function in Support/Format.h.
raw_ostream &operator<<(const FormattedNumber &);
// Formatted output, see the formatv() function in Support/FormatVariadic.h.
raw_ostream &operator<<(const formatv_object_base &);
// Formatted output, see the format_bytes() function in Support/Format.h.
raw_ostream &operator<<(const FormattedBytes &);

View File

@ -55,6 +55,7 @@ add_llvm_library(LLVMSupport
FileOutputBuffer.cpp
FoldingSet.cpp
FormattedStream.cpp
FormatVariadic.cpp
GraphWriter.cpp
Hashing.cpp
IntEqClasses.cpp

View File

@ -0,0 +1,156 @@
//===- FormatVariadic.cpp - Format string parsing and analysis ----*-C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
#include "llvm/Support/FormatVariadic.h"
using namespace llvm;
static Optional<AlignStyle> translateLocChar(char C) {
switch (C) {
case '-':
return AlignStyle::Left;
case '=':
return AlignStyle::Center;
case '+':
return AlignStyle::Right;
default:
return None;
}
LLVM_BUILTIN_UNREACHABLE;
}
bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
size_t &Align, char &Pad) {
Where = AlignStyle::Right;
Align = 0;
Pad = ' ';
if (Spec.empty())
return true;
if (Spec.size() > 1) {
// A maximum of 2 characters at the beginning can be used for something
// other
// than the width.
// If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...]
// contains the width.
// Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width.
// Otherwise, Spec[0:...] contains the width.
if (auto Loc = translateLocChar(Spec[1])) {
Pad = Spec[0];
Where = *Loc;
Spec = Spec.drop_front(2);
} else if (auto Loc = translateLocChar(Spec[0])) {
Where = *Loc;
Spec = Spec.drop_front(1);
}
}
bool Failed = Spec.consumeInteger(0, Align);
return !Failed;
}
Optional<ReplacementItem>
formatv_object_base::parseReplacementItem(StringRef Spec) {
StringRef RepString = Spec.trim("{}");
// If the replacement sequence does not start with a non-negative integer,
// this is an error.
char Pad = ' ';
std::size_t Align = 0;
AlignStyle Where = AlignStyle::Right;
StringRef Options;
size_t Index = 0;
RepString = RepString.trim();
if (RepString.consumeInteger(0, Index)) {
assert(false && "Invalid replacement sequence index!");
return ReplacementItem{};
}
RepString = RepString.trim();
if (!RepString.empty() && RepString.front() == ',') {
RepString = RepString.drop_front();
if (!consumeFieldLayout(RepString, Where, Align, Pad))
assert(false && "Invalid replacement field layout specification!");
}
RepString = RepString.trim();
if (!RepString.empty() && RepString.front() == ':') {
Options = RepString.drop_front().trim();
RepString = StringRef();
}
RepString = RepString.trim();
if (!RepString.empty()) {
assert(false && "Unexpected characters found in replacement string!");
}
return ReplacementItem{Spec, Index, Align, Where, Pad, Options};
}
std::pair<ReplacementItem, StringRef>
formatv_object_base::splitLiteralAndReplacement(StringRef Fmt) {
StringRef Rep;
StringRef Remainder;
std::size_t From = 0;
while (From < Fmt.size() && From != StringRef::npos) {
std::size_t BO = Fmt.find_first_of('{', From);
// Everything up until the first brace is a literal.
if (BO != 0)
return std::make_pair(ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO));
StringRef Braces =
Fmt.drop_front(BO).take_while([](char C) { return C == '{'; });
// If there is more than one brace, then some of them are escaped. Treat
// these as replacements.
if (Braces.size() > 1) {
size_t NumEscapedBraces = Braces.size() / 2;
StringRef Middle = Fmt.substr(BO, NumEscapedBraces);
StringRef Right = Fmt.drop_front(BO + NumEscapedBraces * 2);
return std::make_pair(ReplacementItem{Middle}, Right);
}
// An unterminated open brace is undefined. We treat the rest of the string
// as a literal replacement, but we assert to indicate that this is
// undefined and that we consider it an error.
std::size_t BC = Fmt.find_first_of('}', BO);
if (BC == StringRef::npos) {
assert(
false &&
"Unterminated brace sequence. Escape with {{ for a literal brace.");
return std::make_pair(ReplacementItem{Fmt}, StringRef());
}
// Even if there is a closing brace, if there is another open brace before
// this closing brace, treat this portion as literal, and try again with the
// next one.
std::size_t BO2 = Fmt.find_first_of('{', BO + 1);
if (BO2 < BC)
return std::make_pair(ReplacementItem{Fmt.substr(0, BO2)},
Fmt.substr(BO2));
StringRef Spec = Fmt.slice(BO + 1, BC);
StringRef Right = Fmt.substr(BC + 1);
auto RI = parseReplacementItem(Spec);
if (RI.hasValue())
return std::make_pair(*RI, Right);
// If there was an error parsing the replacement item, treat it as an
// invalid replacement spec, and just continue.
From = BC + 1;
}
return std::make_pair(ReplacementItem{Fmt}, StringRef());
}
std::vector<ReplacementItem>
formatv_object_base::parseFormatString(StringRef Fmt) {
std::vector<ReplacementItem> Replacements;
ReplacementItem I;
while (!Fmt.empty()) {
std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt);
if (I.Type != ReplacementType::Empty)
Replacements.push_back(I);
}
return Replacements;
}

View File

@ -47,8 +47,8 @@ static void writeWithCommas(raw_ostream &S, ArrayRef<char> Buffer) {
}
template <typename T>
static void write_unsigned_impl(raw_ostream &S, T N, IntegerStyle Style,
bool IsNegative) {
static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits,
IntegerStyle Style, bool IsNegative) {
static_assert(std::is_unsigned<T>::value, "Value is not unsigned!");
char NumberBuffer[128];
@ -59,6 +59,12 @@ static void write_unsigned_impl(raw_ostream &S, T N, IntegerStyle Style,
if (IsNegative)
S << '-';
if (Len < MinDigits && Style != IntegerStyle::Number) {
for (size_t I = Len; I < MinDigits; ++I)
S << '0';
}
if (Style == IntegerStyle::Number) {
writeWithCommas(S, ArrayRef<char>(std::end(NumberBuffer) - Len, Len));
} else {
@ -67,53 +73,60 @@ static void write_unsigned_impl(raw_ostream &S, T N, IntegerStyle Style,
}
template <typename T>
static void write_unsigned(raw_ostream &S, T N, IntegerStyle Style,
bool IsNegative = false) {
static void write_unsigned(raw_ostream &S, T N, size_t MinDigits,
IntegerStyle Style, bool IsNegative = false) {
// Output using 32-bit div/mod if possible.
if (N == static_cast<uint32_t>(N))
write_unsigned_impl(S, static_cast<uint32_t>(N), Style, IsNegative);
write_unsigned_impl(S, static_cast<uint32_t>(N), MinDigits, Style,
IsNegative);
else
write_unsigned_impl(S, N, Style, IsNegative);
write_unsigned_impl(S, N, MinDigits, Style, IsNegative);
}
template <typename T>
static void write_signed(raw_ostream &S, T N, IntegerStyle Style) {
static void write_signed(raw_ostream &S, T N, size_t MinDigits,
IntegerStyle Style) {
static_assert(std::is_signed<T>::value, "Value is not signed!");
using UnsignedT = typename std::make_unsigned<T>::type;
if (N >= 0) {
write_unsigned(S, static_cast<UnsignedT>(N), Style);
write_unsigned(S, static_cast<UnsignedT>(N), MinDigits, Style);
return;
}
UnsignedT UN = -(UnsignedT)N;
write_unsigned(S, UN, Style, true);
write_unsigned(S, UN, MinDigits, Style, true);
}
void llvm::write_integer(raw_ostream &S, unsigned int N, IntegerStyle Style) {
write_unsigned(S, N, Style);
}
void llvm::write_integer(raw_ostream &S, int N, IntegerStyle Style) {
write_signed(S, N, Style);
}
void llvm::write_integer(raw_ostream &S, unsigned long N, IntegerStyle Style) {
write_unsigned(S, N, Style);
}
void llvm::write_integer(raw_ostream &S, long N, IntegerStyle Style) {
write_signed(S, N, Style);
}
void llvm::write_integer(raw_ostream &S, unsigned long long N,
void llvm::write_integer(raw_ostream &S, unsigned int N, size_t MinDigits,
IntegerStyle Style) {
write_unsigned(S, N, Style);
write_unsigned(S, N, MinDigits, Style);
}
void llvm::write_integer(raw_ostream &S, long long N, IntegerStyle Style) {
write_signed(S, N, Style);
void llvm::write_integer(raw_ostream &S, int N, size_t MinDigits,
IntegerStyle Style) {
write_signed(S, N, MinDigits, Style);
}
void llvm::write_integer(raw_ostream &S, unsigned long N, size_t MinDigits,
IntegerStyle Style) {
write_unsigned(S, N, MinDigits, Style);
}
void llvm::write_integer(raw_ostream &S, long N, size_t MinDigits,
IntegerStyle Style) {
write_signed(S, N, MinDigits, Style);
}
void llvm::write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits,
IntegerStyle Style) {
write_unsigned(S, N, MinDigits, Style);
}
void llvm::write_integer(raw_ostream &S, long long N, size_t MinDigits,
IntegerStyle Style) {
write_signed(S, N, MinDigits, Style);
}
void llvm::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style,
@ -178,7 +191,9 @@ void llvm::write_double(raw_ostream &S, double N, FloatStyle Style,
#if defined(__MINGW32__)
// FIXME: It should be generic to C++11.
if (N == 0.0 && std::signbit(N)) {
const char *NegativeZero = "-0.000000e+00";
char NegativeZero[] = "-0.000000e+00";
if (Style == FloatStyle::ExponentUpper)
NegativeZero[strlen(NegativeZero) - 4] = 'E';
S << NegativeZero;
return;
}
@ -187,7 +202,9 @@ void llvm::write_double(raw_ostream &S, double N, FloatStyle Style,
// negative zero
if (fpcl == _FPCLASS_NZ) {
const char *NegativeZero = "-0.000000e+00";
char NegativeZero[] = "-0.000000e+00";
if (Style == FloatStyle::ExponentUpper)
NegativeZero[strlen(NegativeZero) - 4] = 'E';
S << NegativeZero;
return;
}
@ -231,6 +248,10 @@ void llvm::write_double(raw_ostream &S, double N, FloatStyle Style,
S << '%';
}
bool llvm::isPrefixedHexStyle(HexPrintStyle S) {
return (S == HexPrintStyle::PrefixLower || S == HexPrintStyle::PrefixUpper);
}
size_t llvm::getDefaultPrecision(FloatStyle Style) {
switch (Style) {
case FloatStyle::Exponent:

View File

@ -20,6 +20,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/NativeFormatting.h"
#include "llvm/Support/Process.h"
@ -114,22 +115,22 @@ void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
}
raw_ostream &raw_ostream::operator<<(unsigned long N) {
write_integer(*this, static_cast<uint64_t>(N), IntegerStyle::Integer);
write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
return *this;
}
raw_ostream &raw_ostream::operator<<(long N) {
write_integer(*this, static_cast<int64_t>(N), IntegerStyle::Integer);
write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
return *this;
}
raw_ostream &raw_ostream::operator<<(unsigned long long N) {
write_integer(*this, static_cast<uint64_t>(N), IntegerStyle::Integer);
write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
return *this;
}
raw_ostream &raw_ostream::operator<<(long long N) {
write_integer(*this, static_cast<int64_t>(N), IntegerStyle::Integer);
write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
return *this;
}
@ -318,6 +319,12 @@ raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
}
}
raw_ostream &raw_ostream::operator<<(const formatv_object_base &Obj) {
SmallString<128> S;
Obj.format(*this);
return *this;
}
raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
unsigned Len = FS.Str.size();
int PadAmount = FS.Width - Len;
@ -344,7 +351,7 @@ raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) {
} else {
llvm::SmallString<16> Buffer;
llvm::raw_svector_ostream Stream(Buffer);
llvm::write_integer(Stream, FN.DecValue, IntegerStyle::Integer);
llvm::write_integer(Stream, FN.DecValue, 0, IntegerStyle::Integer);
if (Buffer.size() < FN.Width)
indent(FN.Width - Buffer.size());
(*this) << Buffer;

View File

@ -20,6 +20,7 @@ add_llvm_unittest(SupportTests
ErrorTest.cpp
ErrorOrTest.cpp
FileOutputBufferTest.cpp
FormatVariadicTest.cpp
Host.cpp
LEB128Test.cpp
LineIteratorTest.cpp

View File

@ -0,0 +1,538 @@
//===- FormatVariadicTest.cpp - Unit tests for string formatting ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/FormatVariadic.h"
#include "gtest/gtest.h"
using namespace llvm;
TEST(FormatVariadicTest, EmptyFormatString) {
auto Replacements = formatv_object_base::parseFormatString("");
EXPECT_EQ(0U, Replacements.size());
}
TEST(FormatVariadicTest, NoReplacements) {
const StringRef kFormatString = "This is a test";
auto Replacements = formatv_object_base::parseFormatString(kFormatString);
ASSERT_EQ(1U, Replacements.size());
EXPECT_EQ(kFormatString, Replacements[0].Spec);
EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type);
}
TEST(FormatVariadicTest, EscapedBrace) {
// {{ should be replaced with {
auto Replacements = formatv_object_base::parseFormatString("{{");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ("{", Replacements[0].Spec);
EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type);
// An even number N of braces should be replaced with N/2 braces.
Replacements = formatv_object_base::parseFormatString("{{{{{{");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ("{{{", Replacements[0].Spec);
EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type);
}
TEST(FormatVariadicTest, ValidReplacementSequence) {
// 1. Simple replacement - parameter index only
auto Replacements = formatv_object_base::parseFormatString("{0}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(0u, Replacements[0].Align);
EXPECT_EQ("", Replacements[0].Options);
Replacements = formatv_object_base::parseFormatString("{1}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(1u, Replacements[0].Index);
EXPECT_EQ(0u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
EXPECT_EQ("", Replacements[0].Options);
// 2. Parameter index with right alignment
Replacements = formatv_object_base::parseFormatString("{0,3}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(3u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
EXPECT_EQ("", Replacements[0].Options);
// 3. And left alignment
Replacements = formatv_object_base::parseFormatString("{0,-3}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(3u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Left, Replacements[0].Where);
EXPECT_EQ("", Replacements[0].Options);
// 4. And center alignment
Replacements = formatv_object_base::parseFormatString("{0,=3}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(3u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Center, Replacements[0].Where);
EXPECT_EQ("", Replacements[0].Options);
// 4. Parameter index with option string
Replacements = formatv_object_base::parseFormatString("{0:foo}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(0u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
EXPECT_EQ("foo", Replacements[0].Options);
// 5. Parameter index with alignment before option string
Replacements = formatv_object_base::parseFormatString("{0,-3:foo}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(3u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Left, Replacements[0].Where);
EXPECT_EQ("foo", Replacements[0].Options);
// 7. Parameter indices, options, and alignment can all have whitespace.
Replacements = formatv_object_base::parseFormatString("{ 0, -3 : foo }");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(3u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Left, Replacements[0].Where);
EXPECT_EQ("foo", Replacements[0].Options);
// 8. Everything after the first option specifier is part of the style, even
// if it contains another option specifier.
Replacements = formatv_object_base::parseFormatString("{0:0:1}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ("0:0:1", Replacements[0].Spec);
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(0u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
EXPECT_EQ("0:1", Replacements[0].Options);
}
TEST(FormatVariadicTest, DefaultReplacementValues) {
// 2. If options string is missing, it defaults to empty.
auto Replacements = formatv_object_base::parseFormatString("{0,3}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(3u, Replacements[0].Align);
EXPECT_EQ("", Replacements[0].Options);
// Including if the colon is present but contains no text.
Replacements = formatv_object_base::parseFormatString("{0,3:}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(3u, Replacements[0].Align);
EXPECT_EQ("", Replacements[0].Options);
// 3. If alignment is missing, it defaults to 0, right, space
Replacements = formatv_object_base::parseFormatString("{0:foo}");
ASSERT_EQ(1u, Replacements.size());
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
EXPECT_EQ(' ', Replacements[0].Pad);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(0u, Replacements[0].Align);
EXPECT_EQ("foo", Replacements[0].Options);
}
TEST(FormatVariadicTest, MultipleReplacements) {
auto Replacements =
formatv_object_base::parseFormatString("{0} {1:foo}-{2,-3:bar}");
ASSERT_EQ(5u, Replacements.size());
// {0}
EXPECT_EQ(ReplacementType::Format, Replacements[0].Type);
EXPECT_EQ(0u, Replacements[0].Index);
EXPECT_EQ(0u, Replacements[0].Align);
EXPECT_EQ(AlignStyle::Right, Replacements[0].Where);
EXPECT_EQ("", Replacements[0].Options);
// " "
EXPECT_EQ(ReplacementType::Literal, Replacements[1].Type);
EXPECT_EQ(" ", Replacements[1].Spec);
// {1:foo} - Options=foo
EXPECT_EQ(ReplacementType::Format, Replacements[2].Type);
EXPECT_EQ(1u, Replacements[2].Index);
EXPECT_EQ(0u, Replacements[2].Align);
EXPECT_EQ(AlignStyle::Right, Replacements[2].Where);
EXPECT_EQ("foo", Replacements[2].Options);
// "-"
EXPECT_EQ(ReplacementType::Literal, Replacements[3].Type);
EXPECT_EQ("-", Replacements[3].Spec);
// {2:bar,-3} - Options=bar, Align=-3
EXPECT_EQ(ReplacementType::Format, Replacements[4].Type);
EXPECT_EQ(2u, Replacements[4].Index);
EXPECT_EQ(3u, Replacements[4].Align);
EXPECT_EQ(AlignStyle::Left, Replacements[4].Where);
EXPECT_EQ("bar", Replacements[4].Options);
}
TEST(FormatVariadicTest, FormatNoReplacements) {
EXPECT_EQ("", formatv("").str());
EXPECT_EQ("Test", formatv("Test").str());
}
TEST(FormatVariadicTest, FormatBasicTypesOneReplacement) {
EXPECT_EQ("1", formatv("{0}", 1).str());
EXPECT_EQ("c", formatv("{0}", 'c').str());
EXPECT_EQ("-3", formatv("{0}", -3).str());
EXPECT_EQ("Test", formatv("{0}", "Test").str());
EXPECT_EQ("Test2", formatv("{0}", StringRef("Test2")).str());
EXPECT_EQ("Test3", formatv("{0}", std::string("Test3")).str());
}
TEST(FormatVariadicTest, IntegralHexFormatting) {
// 1. Trivial cases. Make sure hex is not the default.
EXPECT_EQ("0", formatv("{0}", 0).str());
EXPECT_EQ("2748", formatv("{0}", 0xABC).str());
EXPECT_EQ("-2748", formatv("{0}", -0xABC).str());
// 3. various hex prefixes.
EXPECT_EQ("0xFF", formatv("{0:X}", 255).str());
EXPECT_EQ("0xFF", formatv("{0:X+}", 255).str());
EXPECT_EQ("0xff", formatv("{0:x}", 255).str());
EXPECT_EQ("0xff", formatv("{0:x+}", 255).str());
EXPECT_EQ("FF", formatv("{0:X-}", 255).str());
EXPECT_EQ("ff", formatv("{0:x-}", 255).str());
// 5. Precision pads left of the most significant digit but right of the
// prefix (if one exists).
EXPECT_EQ("0xFF", formatv("{0:X2}", 255).str());
EXPECT_EQ("0xFF", formatv("{0:X+2}", 255).str());
EXPECT_EQ("0x0ff", formatv("{0:x3}", 255).str());
EXPECT_EQ("0x0ff", formatv("{0:x+3}", 255).str());
EXPECT_EQ("00FF", formatv("{0:X-4}", 255).str());
EXPECT_EQ("00ff", formatv("{0:x-4}", 255).str());
// 6. Try some larger types.
EXPECT_EQ("0xDEADBEEFDEADBEEF",
formatv("{0:X16}", -2401053088876216593LL).str());
EXPECT_EQ("0xFEEBDAEDFEEBDAED",
formatv("{0:X16}", 0xFEEBDAEDFEEBDAEDULL).str());
EXPECT_EQ("0x00000000DEADBEEF", formatv("{0:X16}", 0xDEADBEEF).str());
// 7. Padding should take into account the prefix
EXPECT_EQ("0xff", formatv("{0,4:x}", 255).str());
EXPECT_EQ(" 0xff", formatv("{0,5:x+}", 255).str());
EXPECT_EQ(" FF", formatv("{0,4:X-}", 255).str());
EXPECT_EQ(" ff", formatv("{0,5:x-}", 255).str());
// 8. Including when it's been zero-padded
EXPECT_EQ(" 0x0ff", formatv("{0,7:x3}", 255).str());
EXPECT_EQ(" 0x00ff", formatv("{0,7:x+4}", 255).str());
EXPECT_EQ(" 000FF", formatv("{0,7:X-5}", 255).str());
EXPECT_EQ(" 0000ff", formatv("{0,7:x-6}", 255).str());
// 9. Precision with default format specifier should work too
EXPECT_EQ(" 255", formatv("{0,7:3}", 255).str());
EXPECT_EQ(" 0255", formatv("{0,7:4}", 255).str());
EXPECT_EQ(" 00255", formatv("{0,7:5}", 255).str());
EXPECT_EQ(" 000255", formatv("{0,7:6}", 255).str());
}
TEST(FormatVariadicTest, PointerFormatting) {
// 1. Trivial cases. Hex is default. Default Precision is pointer width.
if (sizeof(void *) == 4) {
EXPECT_EQ("0x00000000", formatv("{0}", (void *)0).str());
EXPECT_EQ("0x00000ABC", formatv("{0}", (void *)0xABC).str());
} else {
EXPECT_EQ("0x0000000000000000", formatv("{0}", (void *)0).str());
EXPECT_EQ("0x0000000000000ABC", formatv("{0}", (void *)0xABC).str());
}
// 2. But we can reduce the precision explicitly.
EXPECT_EQ("0x0", formatv("{0:0}", (void *)0).str());
EXPECT_EQ("0xABC", formatv("{0:0}", (void *)0xABC).str());
EXPECT_EQ("0x0000", formatv("{0:4}", (void *)0).str());
EXPECT_EQ("0x0ABC", formatv("{0:4}", (void *)0xABC).str());
// 3. various hex prefixes.
EXPECT_EQ("0x0ABC", formatv("{0:X4}", (void *)0xABC).str());
EXPECT_EQ("0x0abc", formatv("{0:x4}", (void *)0xABC).str());
EXPECT_EQ("0ABC", formatv("{0:X-4}", (void *)0xABC).str());
EXPECT_EQ("0abc", formatv("{0:x-4}", (void *)0xABC).str());
}
TEST(FormatVariadicTest, IntegralNumberFormatting) {
// 1. Test comma grouping with default widths and precisions.
EXPECT_EQ("0", formatv("{0:N}", 0).str());
EXPECT_EQ("10", formatv("{0:N}", 10).str());
EXPECT_EQ("100", formatv("{0:N}", 100).str());
EXPECT_EQ("1,000", formatv("{0:N}", 1000).str());
EXPECT_EQ("1,234,567,890", formatv("{0:N}", 1234567890).str());
EXPECT_EQ("-10", formatv("{0:N}", -10).str());
EXPECT_EQ("-100", formatv("{0:N}", -100).str());
EXPECT_EQ("-1,000", formatv("{0:N}", -1000).str());
EXPECT_EQ("-1,234,567,890", formatv("{0:N}", -1234567890).str());
// 2. If there is no comma, width and precision pad to the same absolute
// size.
EXPECT_EQ(" 1", formatv("{0,2:N}", 1).str());
// 3. But if there is a comma or negative sign, width factors them in but
// precision doesn't.
EXPECT_EQ(" 1,000", formatv("{0,6:N}", 1000).str());
EXPECT_EQ(" -1,000", formatv("{0,7:N}", -1000).str());
// 4. Large widths all line up.
EXPECT_EQ(" 1,000", formatv("{0,11:N}", 1000).str());
EXPECT_EQ(" -1,000", formatv("{0,11:N}", -1000).str());
EXPECT_EQ(" -100,000", formatv("{0,11:N}", -100000).str());
}
TEST(FormatVariadicTest, StringFormatting) {
const char FooArray[] = "FooArray";
const char *FooPtr = "FooPtr";
llvm::StringRef FooRef("FooRef");
std::string FooString("FooString");
// 1. Test that we can print various types of strings.
EXPECT_EQ(FooArray, formatv("{0}", FooArray).str());
EXPECT_EQ(FooPtr, formatv("{0}", FooPtr).str());
EXPECT_EQ(FooRef, formatv("{0}", FooRef).str());
EXPECT_EQ(FooString, formatv("{0}", FooString).str());
// 2. Test that the precision specifier prints the correct number of
// characters.
EXPECT_EQ("FooA", formatv("{0:4}", FooArray).str());
EXPECT_EQ("FooP", formatv("{0:4}", FooPtr).str());
EXPECT_EQ("FooR", formatv("{0:4}", FooRef).str());
EXPECT_EQ("FooS", formatv("{0:4}", FooString).str());
// 3. And that padding works.
EXPECT_EQ(" FooA", formatv("{0,6:4}", FooArray).str());
EXPECT_EQ(" FooP", formatv("{0,6:4}", FooPtr).str());
EXPECT_EQ(" FooR", formatv("{0,6:4}", FooRef).str());
EXPECT_EQ(" FooS", formatv("{0,6:4}", FooString).str());
}
TEST(FormatVariadicTest, CharFormatting) {
// 1. Not much to see here. Just print a char with and without padding.
EXPECT_EQ("C", formatv("{0}", 'C').str());
EXPECT_EQ(" C", formatv("{0,3}", 'C').str());
// 2. char is really an integral type though, where the only difference is
// that the "default" is to print the ASCII. So if a non-default presentation
// specifier exists, it should print as an integer.
EXPECT_EQ("37", formatv("{0:D}", (char)37).str());
EXPECT_EQ(" 037", formatv("{0,5:D3}", (char)37).str());
}
TEST(FormatVariadicTest, BoolTest) {
// 1. Default style is lowercase text (same as 't')
EXPECT_EQ("true", formatv("{0}", true).str());
EXPECT_EQ("false", formatv("{0}", false).str());
EXPECT_EQ("true", formatv("{0:t}", true).str());
EXPECT_EQ("false", formatv("{0:t}", false).str());
// 2. T - uppercase text
EXPECT_EQ("TRUE", formatv("{0:T}", true).str());
EXPECT_EQ("FALSE", formatv("{0:T}", false).str());
// 3. D / d - integral
EXPECT_EQ("1", formatv("{0:D}", true).str());
EXPECT_EQ("0", formatv("{0:D}", false).str());
EXPECT_EQ("1", formatv("{0:d}", true).str());
EXPECT_EQ("0", formatv("{0:d}", false).str());
// 4. Y - uppercase yes/no
EXPECT_EQ("YES", formatv("{0:Y}", true).str());
EXPECT_EQ("NO", formatv("{0:Y}", false).str());
// 5. y - lowercase yes/no
EXPECT_EQ("yes", formatv("{0:y}", true).str());
EXPECT_EQ("no", formatv("{0:y}", false).str());
}
TEST(FormatVariadicTest, DoubleFormatting) {
// Test exponents, fixed point, and percent formatting.
// 1. Signed, unsigned, and zero exponent format.
EXPECT_EQ("0.000000E+00", formatv("{0:E}", 0.0).str());
EXPECT_EQ("-0.000000E+00", formatv("{0:E}", -0.0).str());
EXPECT_EQ("1.100000E+00", formatv("{0:E}", 1.1).str());
EXPECT_EQ("-1.100000E+00", formatv("{0:E}", -1.1).str());
EXPECT_EQ("1.234568E+03", formatv("{0:E}", 1234.5678).str());
EXPECT_EQ("-1.234568E+03", formatv("{0:E}", -1234.5678).str());
EXPECT_EQ("1.234568E-03", formatv("{0:E}", .0012345678).str());
EXPECT_EQ("-1.234568E-03", formatv("{0:E}", -.0012345678).str());
// 2. With padding and precision.
EXPECT_EQ(" 0.000E+00", formatv("{0,11:E3}", 0.0).str());
EXPECT_EQ(" -1.100E+00", formatv("{0,11:E3}", -1.1).str());
EXPECT_EQ(" 1.235E+03", formatv("{0,11:E3}", 1234.5678).str());
EXPECT_EQ(" -1.235E-03", formatv("{0,11:E3}", -.0012345678).str());
// 3. Signed, unsigned, and zero fixed point format.
EXPECT_EQ("0.00", formatv("{0:F}", 0.0).str());
EXPECT_EQ("-0.00", formatv("{0:F}", -0.0).str());
EXPECT_EQ("1.10", formatv("{0:F}", 1.1).str());
EXPECT_EQ("-1.10", formatv("{0:F}", -1.1).str());
EXPECT_EQ("1234.57", formatv("{0:F}", 1234.5678).str());
EXPECT_EQ("-1234.57", formatv("{0:F}", -1234.5678).str());
EXPECT_EQ("0.00", formatv("{0:F}", .0012345678).str());
EXPECT_EQ("-0.00", formatv("{0:F}", -.0012345678).str());
// 2. With padding and precision.
EXPECT_EQ(" 0.000", formatv("{0,8:F3}", 0.0).str());
EXPECT_EQ(" -1.100", formatv("{0,8:F3}", -1.1).str());
EXPECT_EQ("1234.568", formatv("{0,8:F3}", 1234.5678).str());
EXPECT_EQ(" -0.001", formatv("{0,8:F3}", -.0012345678).str());
}
struct format_tuple {
const char *Fmt;
explicit format_tuple(const char *Fmt) : Fmt(Fmt) {}
template <typename... Ts>
auto operator()(Ts &&... Values) const
-> decltype(formatv(Fmt, std::forward<Ts>(Values)...)) {
return formatv(Fmt, std::forward<Ts>(Values)...);
}
};
TEST(FormatVariadicTest, BigTest) {
using Tuple =
std::tuple<char, int, const char *, StringRef, std::string, double, float,
void *, int, double, int64_t, uint64_t, double, uint8_t>;
Tuple Ts[] = {
Tuple('a', 1, "Str", StringRef(), std::string(), 3.14159, -.17532f,
(void *)nullptr, 123456, 6.02E23, -908234908423, 908234908422234,
std::numeric_limits<double>::quiet_NaN(), 0xAB),
Tuple('x', 0xDDB5B, "LongerStr", "StringRef", "std::string", -2.7,
.08215f, (void *)nullptr, 0, 6.62E-34, -908234908423,
908234908422234, std::numeric_limits<double>::infinity(), 0x0)};
// Test long string formatting with many edge cases combined.
const char *Intro =
"There are {{{0}} items in the tuple, and {{{1}} tuple(s) in the array.";
const char *Header =
"{0,6}|{1,8}|{2,=10}|{3,=10}|{4,=13}|{5,7}|{6,7}|{7,10}|{8,"
"-7}|{9,10}|{10,16}|{11,17}|{12,6}|{13,4}";
const char *Line =
"{0,6}|{1,8:X}|{2,=10}|{3,=10:5}|{4,=13}|{5,7:3}|{6,7:P2}|{7,"
"10:X8}|{8,-7:N}|{9,10:E4}|{10,16:N}|{11,17:D}|{12,6}|{13,"
"4:X}";
std::string S;
llvm::raw_string_ostream Stream(S);
Stream << formatv(Intro, std::tuple_size<Tuple>::value,
llvm::array_lengthof(Ts))
<< "\n";
Stream << formatv(Header, "Char", "HexInt", "Str", "Ref", "std::str",
"double", "float", "pointer", "comma", "exp", "bigint",
"bigint2", "limit", "byte")
<< "\n";
for (auto &Item : Ts) {
Stream << llvm::apply_tuple(format_tuple(Line), Item) << "\n";
}
Stream.flush();
const char *Expected =
R"foo(There are {14} items in the tuple, and {2} tuple(s) in the array.
Char| HexInt| Str | Ref | std::str | double| float| pointer|comma | exp| bigint| bigint2| limit|byte
a| 0x1| Str | | | 3.142|-17.53%|0x00000000|123,456|6.0200E+23|-908,234,908,423| 908234908422234| nan|0xAB
x| 0xDDB5B|LongerStr | Strin | std::string | -2.700| 8.21%|0x00000000|0 |6.6200E-34|-908,234,908,423| 908234908422234| INF| 0x0
)foo";
EXPECT_EQ(Expected, S);
}
TEST(FormatVariadicTest, Range) {
std::vector<int> IntRange = {1, 1, 2, 3, 5, 8, 13};
// 1. Simple range with default separator and element style.
EXPECT_EQ("1, 1, 2, 3, 5, 8, 13",
formatv("{0}", make_range(IntRange.begin(), IntRange.end())).str());
EXPECT_EQ("1, 2, 3, 5, 8",
formatv("{0}", make_range(IntRange.begin() + 1, IntRange.end() - 1))
.str());
// 2. Non-default separator
EXPECT_EQ(
"1/1/2/3/5/8/13",
formatv("{0:$[/]}", make_range(IntRange.begin(), IntRange.end())).str());
// 3. Default separator, non-default element style.
EXPECT_EQ(
"0x1, 0x1, 0x2, 0x3, 0x5, 0x8, 0xd",
formatv("{0:@[x]}", make_range(IntRange.begin(), IntRange.end())).str());
// 4. Non-default separator and element style.
EXPECT_EQ(
"0x1 + 0x1 + 0x2 + 0x3 + 0x5 + 0x8 + 0xd",
formatv("{0:$[ + ]@[x]}", make_range(IntRange.begin(), IntRange.end()))
.str());
// 5. Element style and/or separator using alternate delimeters to allow using
// delimeter characters as part of the separator.
EXPECT_EQ(
"<0x1><0x1><0x2><0x3><0x5><0x8><0xd>",
formatv("<{0:$[><]@(x)}>", make_range(IntRange.begin(), IntRange.end()))
.str());
EXPECT_EQ(
"[0x1][0x1][0x2][0x3][0x5][0x8][0xd]",
formatv("[{0:$(][)@[x]}]", make_range(IntRange.begin(), IntRange.end()))
.str());
EXPECT_EQ(
"(0x1)(0x1)(0x2)(0x3)(0x5)(0x8)(0xd)",
formatv("({0:$<)(>@<x>})", make_range(IntRange.begin(), IntRange.end()))
.str());
// 5. Empty range.
EXPECT_EQ("", formatv("{0:$[+]@[x]}",
make_range(IntRange.begin(), IntRange.begin()))
.str());
// 6. Empty separator and style.
EXPECT_EQ("11235813",
formatv("{0:$[]@<>}", make_range(IntRange.begin(), IntRange.end()))
.str());
}
TEST(FormatVariadicTest, Adapter) {
class Negative {
int N;
public:
explicit Negative(int N) : N(N) {}
void format(raw_ostream &S, StringRef Options) { S << -N; }
};
EXPECT_EQ("-7", formatv("{0}", Negative(7)).str());
int N = 171;
EXPECT_EQ(" 171 ",
formatv("{0}", fmt_align(N, AlignStyle::Center, 7)).str());
EXPECT_EQ(" 171 ", formatv("{0}", fmt_pad(N, 1, 3)).str());
EXPECT_EQ("171171171171171", formatv("{0}", fmt_repeat(N, 5)).str());
EXPECT_EQ(" ABABABABAB ",
formatv("{0:X-}", fmt_pad(fmt_repeat(N, 5), 1, 3)).str());
EXPECT_EQ(" AB AB AB AB AB ",
formatv("{0,=34:X-}", fmt_repeat(fmt_pad(N, 1, 3), 5)).str());
}
TEST(FormatVariadicTest, ImplicitConversions) {
std::string S = formatv("{0} {1}", 1, 2);
EXPECT_EQ("1 2", S);
SmallString<4> S2 = formatv("{0} {1}", 1, 2);
EXPECT_EQ("1 2", S2);
}

View File

@ -21,7 +21,7 @@ namespace {
template <typename T> std::string format_number(T N, IntegerStyle Style) {
std::string S;
llvm::raw_string_ostream Str(S);
write_integer(Str, N, Style);
write_integer(Str, N, 0, Style);
Str.flush();
return S;
}