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
#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>
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
//===----------------------------------------------------------------------===//
--- /dev/null
+//===- 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
\ No newline at end of file
--- /dev/null
+//===- 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
\ No newline at end of file
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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
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);
#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"
// 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;
#include <system_error>
namespace llvm {
+class formatv_object_base;
class format_object_base;
class FormattedString;
class FormattedNumber;
// 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 &);
FileOutputBuffer.cpp
FoldingSet.cpp
FormattedStream.cpp
+ FormatVariadic.cpp
GraphWriter.cpp
Hashing.cpp
IntEqClasses.cpp
--- /dev/null
+//===- 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;
+}
}
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];
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 {
}
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, unsigned int N, size_t MinDigits,
+ IntegerStyle Style) {
+ write_unsigned(S, N, MinDigits, Style);
}
-void llvm::write_integer(raw_ostream &S, int 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, IntegerStyle Style) {
- write_unsigned(S, N, 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, IntegerStyle Style) {
- write_signed(S, N, 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,
+void llvm::write_integer(raw_ostream &S, unsigned long long 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, 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,
#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;
}
// 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;
}
S << '%';
}
+bool llvm::isPrefixedHexStyle(HexPrintStyle S) {
+ return (S == HexPrintStyle::PrefixLower || S == HexPrintStyle::PrefixUpper);
+}
+
size_t llvm::getDefaultPrecision(FloatStyle Style) {
switch (Style) {
case FloatStyle::Exponent:
#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"
}
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;
}
}
}
+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;
} 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;
ErrorTest.cpp
ErrorOrTest.cpp
FileOutputBufferTest.cpp
+ FormatVariadicTest.cpp
Host.cpp
LEB128Test.cpp
LineIteratorTest.cpp
--- /dev/null
+//===- 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);
+}
\ No newline at end of file
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;
}