Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Attila Mark 2020-03-18 13:04:02 -07:00
commit 5453e8c84d
21 changed files with 555 additions and 231 deletions

View File

@ -24,13 +24,24 @@ function(join result_var)
set(${result_var} "${result}" PARENT_SCOPE)
endfunction()
# Sets a cache variable with a docstring joined from multiple arguments:
# set(<variable> <value>... CACHE <type> <docstring>...)
# This allows splitting a long docstring for readability.
function(set_verbose)
cmake_parse_arguments(SET_VERBOSE "" "" "CACHE" ${ARGN})
list(GET SET_VERBOSE_CACHE 0 type)
list(REMOVE_AT SET_VERBOSE_CACHE 0)
join(doc ${SET_VERBOSE_CACHE})
set(${SET_VERBOSE_UNPARSED_ARGUMENTS} CACHE ${type} ${doc})
endfunction()
# Set the default CMAKE_BUILD_TYPE to Release.
# This should be done before the project command since the latter can set
# CMAKE_BUILD_TYPE itself (it does so for nmake).
if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
endif ()
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
@ -61,7 +72,9 @@ message(STATUS "Version: ${FMT_VERSION}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif ()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
@ -69,7 +82,13 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
include(cxx14)
include(CheckCXXCompilerFlag)
set(FMT_REQUIRED_FEATURES cxx_auto_type cxx_variadic_templates)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
if (${index} GREATER -1)
# Use cxx_variadic_templates instead of more appropriate cxx_std_11 for
# compatibility with older CMake versions.
set(FMT_REQUIRED_FEATURES cxx_variadic_templates)
endif ()
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
@ -123,7 +142,9 @@ if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"")
endif ()
# Set FrameworkPathOverride to get rid of MSB3644 warnings.
set(netfxpath "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.0")
join(netfxpath
"C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\"
".NETFramework\\v4.0")
file(WRITE run-msbuild.bat "
${MSBUILD_SETUP}
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
@ -173,13 +194,12 @@ target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
set(FMT_DEBUG_POSTFIX d)
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES
OUTPUT_NAME "fmt"
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX ${FMT_DEBUG_POSTFIX})
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc.
get_target_property(FMT_LIB_NAME fmt OUTPUT_NAME)
@ -203,7 +223,6 @@ add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt-header-only INTERFACE
@ -214,8 +233,9 @@ target_include_directories(fmt-header-only INTERFACE
if (FMT_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
@ -226,14 +246,17 @@ if (FMT_INSTALL)
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
endif ()
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, relative to ${CMAKE_INSTALL_PREFIX}.")
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(
@ -290,7 +313,7 @@ set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
if (MASTER_PROJECT AND EXISTS ${gitignore})
# Get the list of ignored files from .gitignore.
file (STRINGS ${gitignore} lines)
LIST(REMOVE_ITEM lines /doc/html)
list(REMOVE_ITEM lines /doc/html)
foreach (line ${lines})
string(REPLACE "." "[.]" line "${line}")
string(REPLACE "*" ".*" line "${line}")

View File

@ -412,7 +412,7 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::strlen(buffer);
return buffer + std::char_traits<Char>::length(buffer);
}
private:

View File

@ -351,6 +351,8 @@ template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
@ -362,6 +364,9 @@ template <typename Char> struct text {
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
@ -407,6 +412,9 @@ template <typename Char, typename T, int N> struct field {
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
@ -419,6 +427,9 @@ template <typename L, typename R> struct concat {
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
@ -509,8 +520,7 @@ constexpr auto compile(S format_str) {
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
@ -518,8 +528,7 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);

View File

@ -10,9 +10,12 @@
#include <cstdio> // std::FILE
#include <cstring>
#include <functional>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60103
@ -36,6 +39,18 @@
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
#endif
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
(__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
(__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
# define FMT_CLANG_VERSION 0
#endif
#if defined(__GNUC__) && !defined(__clang__)
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
@ -126,9 +141,16 @@
# define FMT_NORETURN
#endif
#ifndef FMT_MAYBE_UNUSED
# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
# define FMT_MAYBE_UNUSED [[maybe_unused]]
# else
# define FMT_MAYBE_UNUSED
# endif
#endif
#ifndef FMT_DEPRECATED
# if (FMT_HAS_CPP_ATTRIBUTE(deprecated) && __cplusplus >= 201402L) || \
FMT_MSC_VER >= 1900
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
# define FMT_DEPRECATED [[deprecated]]
# else
# if defined(__GNUC__) || defined(__clang__)
@ -141,8 +163,8 @@
# endif
#endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || FMT_NVCC
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
@ -250,18 +272,23 @@ struct monostate {};
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
FMT_API void assert_fail(const char* file, int line, const char* message);
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
# define FMT_ASSERT(condition, message)
# else
# define FMT_ASSERT(condition, message) \
((condition) \
? void() \
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
@ -295,6 +322,12 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
#ifdef __cpp_char8_t
using char8_type = char8_t;
#else
enum char8_type : unsigned char {};
#endif
} // namespace internal
template <typename... Ts>
@ -395,15 +428,15 @@ using string_view = basic_string_view<char>;
using wstring_view = basic_string_view<wchar_t>;
#ifndef __cpp_char8_t
// A UTF-8 code unit type.
enum char8_t : unsigned char {};
// char8_t is deprecated; use char instead.
using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
#endif
/** Specifies if ``T`` is a character type. Can be specialized by users. */
template <typename T> struct is_char : std::false_type {};
template <> struct is_char<char> : std::true_type {};
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<char8_t> : std::true_type {};
template <> struct is_char<internal::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
@ -633,6 +666,9 @@ template <typename T> class buffer {
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
const T* begin() const FMT_NOEXCEPT { return ptr_; }
const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
/** Returns the size of this buffer. */
std::size_t size() const FMT_NOEXCEPT { return size_; }
@ -900,7 +936,8 @@ template <typename Context> struct arg_mapper {
template <typename T,
FMT_ENABLE_IF(
std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value)>
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val);
}
@ -909,7 +946,8 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value && !has_formatter<T, Context>::value)>
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
@ -938,18 +976,15 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR auto map(const T& val) -> decltype(
map(static_cast<typename std::underlying_type<T>::type>(val))) {
FMT_CONSTEXPR auto map(const T& val)
-> decltype(std::declval<arg_mapper>().map(
static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <
typename T,
FMT_ENABLE_IF(
!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
(has_formatter<T, Context>::value ||
(has_fallback_formatter<T, Context>::value &&
!std::is_constructible<std_string_view<char_type>, T>::value)))>
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
(has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))>
FMT_CONSTEXPR const T& map(const T& val) {
return val;
}
@ -1170,6 +1205,43 @@ template <bool IS_PACKED, typename Context, typename T,
inline basic_format_arg<Context> make_arg(const T& value) {
return make_arg<Context>(value);
}
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
auto next = std::move(head_);
auto node = new typed_node<T>(arg);
head_.reset(node);
head_->next = std::move(next);
return node->value;
}
};
} // namespace internal
// Formatting context.
@ -1279,6 +1351,102 @@ inline format_arg_store<Context, Args...> make_format_args(
return {args...};
}
/**
\rst
A dynamic version of `fmt::format_arg_store<>`.
It's equipped with a storage to potentially temporary objects which lifetime
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr internal::type mapped_type =
internal::mapped_type_constant<T, Context>::value;
enum {
value = !(internal::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, internal::std_string_view<char_type>>::value ||
(mapped_type != internal::type::cstring_type &&
mapped_type != internal::type::string_type &&
mapped_type != internal::type::custom_type &&
mapped_type != internal::type::named_arg_type))
};
};
template <typename T>
using stored_type = conditional_t<internal::is_string<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
internal::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
unsigned long long get_types() const {
return internal::is_unpacked_bit | data_.size();
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(internal::make_arg<Context>(arg));
}
public:
/**
\rst
Adds an argument into the dynamic store for later passing to a formating
function.
Note that custom types and string types (but not string views!) are copied
into the store with dynamic memory (in addition to resizing vector).
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
static_assert(
!std::is_base_of<internal::named_arg_base<char_type>, T>::value,
"named arguments are not supported yet");
if (internal::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
emplace_arg(arg);
}
/**
Adds a reference to the argument into the dynamic store for later passing to
a formating function.
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
};
/**
\rst
A view of a collection of formatting arguments. To avoid lifetime issues it
@ -1350,6 +1518,17 @@ template <typename Context> class basic_format_args {
set_data(store.data_);
}
/**
\rst
Constructs a `basic_format_args` object from
`~fmt::dynamic_format_arg_store`.
\endrst
*/
basic_format_args(const dynamic_format_arg_store<Context>& store)
: types_(store.get_types()) {
set_data(store.data_.data());
}
/**
\rst
Constructs a `basic_format_args` object from a dynamic set of arguments.
@ -1453,7 +1632,7 @@ make_args_checked(const S& format_str,
all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
!std::is_reference<Args>::value)...>::value,
"passing views as lvalues is disallowed");
check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str);
check_format_string<Args...>(format_str);
return {args...};
}

View File

@ -86,6 +86,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
}
// Handle the result of GNU-specific version of strerror_r.
FMT_MAYBE_UNUSED
int handle(char* message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
@ -95,11 +96,13 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
}
// Handle the case when strerror_r is not available.
FMT_MAYBE_UNUSED
int handle(internal::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
FMT_MAYBE_UNUSED
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
@ -336,6 +339,10 @@ class fp {
private:
using significand_type = uint64_t;
public:
significand_type f;
int e;
// All sizes are in bits.
// Subtract 1 to account for an implicit most significant bit in the
// normalized form.
@ -343,11 +350,6 @@ class fp {
std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
1ULL << double_significand_size;
public:
significand_type f;
int e;
static FMT_CONSTEXPR_DECL const int significand_size =
bits<significand_type>::value;
@ -358,22 +360,6 @@ class fp {
// errors on platforms where double is not IEEE754.
template <typename Double> explicit fp(Double d) { assign(d); }
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT> friend fp normalize(fp value) {
// Handle subnormals.
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
const auto offset =
fp::significand_size - fp::double_significand_size - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
// Assigns d to this and return true iff predecessor is closer than successor.
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
bool assign(Double d) {
@ -434,6 +420,22 @@ class fp {
}
};
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT> fp normalize(fp value) {
// Handle subnormals.
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
const auto offset =
fp::significand_size - fp::double_significand_size - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
@ -527,8 +529,7 @@ class bigint {
FMT_ASSERT(compare(*this, other) >= 0, "");
bigit borrow = 0;
int i = other.exp_ - exp_;
for (int j = 0, n = static_cast<int>(other.bigits_.size()); j != n;
++i, ++j) {
for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) {
subtract_bigits(i, other.bigits_[j], borrow);
}
while (borrow > 0) subtract_bigits(i, 0, borrow);
@ -579,7 +580,7 @@ class bigint {
}
void assign(uint64_t n) {
int num_bigits = 0;
size_t num_bigits = 0;
do {
bigits_[num_bigits++] = n & ~bigit(0);
n >>= bigit_bits;
@ -757,7 +758,7 @@ enum result {
}
// A version of count_digits optimized for grisu_gen_digits.
inline unsigned grisu_count_digits(uint32_t n) {
inline int grisu_count_digits(uint32_t n) {
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;

View File

@ -43,12 +43,6 @@
#include "core.h"
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
# define FMT_CLANG_VERSION 0
#endif
#ifdef __INTEL_COMPILER
# define FMT_ICC_VERSION __INTEL_COMPILER
#elif defined(__ICL)
@ -72,12 +66,12 @@
#if __cplusplus == 201103L || __cplusplus == 201402L
# if defined(__clang__)
# define FMT_FALLTHROUGH [[clang::fallthrough]]
# elif FMT_GCC_VERSION >= 700
# elif FMT_GCC_VERSION >= 700 && !defined(__PGI)
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
# else
# define FMT_FALLTHROUGH
# endif
#elif (FMT_HAS_CPP_ATTRIBUTE(fallthrough) && (__cplusplus >= 201703)) || \
#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \
(defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
# define FMT_FALLTHROUGH [[fallthrough]]
#else
@ -131,11 +125,11 @@ FMT_END_NAMESPACE
#endif
#ifndef FMT_USE_UDL_TEMPLATE
// EDG front end based compilers (icc, nvcc) do not support UDL templates yet
// and GCC 9 warns about them.
// EDG front end based compilers (icc, nvcc) and GCC < 6.4 do not propertly
// support UDL templates and GCC >= 9 warns about them.
# if FMT_USE_USER_DEFINED_LITERALS && FMT_ICC_VERSION == 0 && \
FMT_CUDA_VERSION == 0 && \
((FMT_GCC_VERSION >= 600 && FMT_GCC_VERSION <= 900 && \
((FMT_GCC_VERSION >= 604 && FMT_GCC_VERSION <= 900 && \
__cplusplus >= 201402L) || \
FMT_CLANG_VERSION >= 304)
# define FMT_USE_UDL_TEMPLATE 1
@ -219,10 +213,6 @@ FMT_END_NAMESPACE
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
// undefined behavior (e.g. due to type aliasing).
// Example: uint64_t d = bit_cast<uint64_t>(2.718);
@ -312,7 +302,7 @@ template <typename It> class is_output_iterator {
using type = decltype(test<It>(typename iterator_category<It>::type{}));
public:
static const bool value = !std::is_const<remove_reference_t<type>>::value;
enum { value = !std::is_const<remove_reference_t<type>>::value };
};
// A workaround for std::string not having mutable data() until C++17.
@ -479,8 +469,8 @@ inline size_t count_code_points(basic_string_view<Char> s) {
}
// Counts the number of code points in a UTF-8 string.
inline size_t count_code_points(basic_string_view<char8_t> s) {
const char8_t* data = s.data();
inline size_t count_code_points(basic_string_view<char> s) {
const char* data = s.data();
size_t num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i] & 0xc0) != 0x80) ++num_code_points;
@ -488,6 +478,11 @@ inline size_t count_code_points(basic_string_view<char8_t> s) {
return num_code_points;
}
inline size_t count_code_points(basic_string_view<char8_type> s) {
return count_code_points(basic_string_view<char>(
reinterpret_cast<const char*>(s.data()), s.size()));
}
template <typename Char>
inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
size_t size = s.size();
@ -495,8 +490,8 @@ inline size_t code_point_index(basic_string_view<Char> s, size_t n) {
}
// Calculates the index of the nth code point in a UTF-8 string.
inline size_t code_point_index(basic_string_view<char8_t> s, size_t n) {
const char8_t* data = s.data();
inline size_t code_point_index(basic_string_view<char8_type> s, size_t n) {
const char8_type* data = s.data();
size_t num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) {
@ -506,13 +501,13 @@ inline size_t code_point_index(basic_string_view<char8_t> s, size_t n) {
return s.size();
}
inline char8_t to_char8_t(char c) { return static_cast<char8_t>(c); }
inline char8_type to_char8_t(char c) { return static_cast<char8_type>(c); }
template <typename InputIt, typename OutChar>
using needs_conversion = bool_constant<
std::is_same<typename std::iterator_traits<InputIt>::value_type,
char>::value &&
std::is_same<OutChar, char8_t>::value>;
std::is_same<OutChar, char8_type>::value>;
template <typename OutChar, typename InputIt, typename OutputIt,
FMT_ENABLE_IF(!needs_conversion<InputIt, OutChar>::value)>
@ -556,19 +551,22 @@ class buffer_range : public internal::output_range<
: internal::output_range<iterator, T>(std::back_inserter(buf)) {}
};
class FMT_DEPRECATED u8string_view : public basic_string_view<char8_t> {
class FMT_DEPRECATED u8string_view
: public basic_string_view<internal::char8_type> {
public:
u8string_view(const char* s)
: basic_string_view<char8_t>(reinterpret_cast<const char8_t*>(s)) {}
: basic_string_view<internal::char8_type>(
reinterpret_cast<const internal::char8_type*>(s)) {}
u8string_view(const char* s, size_t count) FMT_NOEXCEPT
: basic_string_view<char8_t>(reinterpret_cast<const char8_t*>(s), count) {
}
: basic_string_view<internal::char8_type>(
reinterpret_cast<const internal::char8_type*>(s), count) {}
};
#if FMT_USE_USER_DEFINED_LITERALS
inline namespace literals {
inline basic_string_view<char8_t> operator"" _u(const char* s, std::size_t n) {
return {reinterpret_cast<const char8_t*>(s), n};
FMT_DEPRECATED inline basic_string_view<internal::char8_type> operator"" _u(
const char* s, std::size_t n) {
return {reinterpret_cast<const internal::char8_type*>(s), n};
}
} // namespace literals
#endif
@ -1609,6 +1607,18 @@ template <typename Range> class basic_writer {
}
};
struct bytes_writer {
string_view bytes;
size_t size() const { return bytes.size(); }
size_t width() const { return bytes.size(); }
template <typename It> void operator()(It&& it) const {
const char* data = bytes.data();
it = copy_str<char>(data, data + size(), it);
}
};
template <typename UIntPtr> struct pointer_writer {
UIntPtr value;
int num_digits;
@ -1767,6 +1777,10 @@ template <typename Range> class basic_writer {
write(data, size, specs);
}
void write_bytes(string_view bytes, const format_specs& specs) {
write_padded(specs, bytes_writer{bytes});
}
template <typename UIntPtr>
void write_pointer(UIntPtr value, const format_specs* specs) {
int num_digits = count_digits<4>(value);
@ -1935,10 +1949,6 @@ template <typename Char, typename ErrorHandler>
FMT_CONSTEXPR int parse_nonnegative_int(const Char*& begin, const Char* end,
ErrorHandler&& eh) {
FMT_ASSERT(begin != end && '0' <= *begin && *begin <= '9', "");
if (*begin == '0') {
++begin;
return 0;
}
unsigned value = 0;
// Convert to unsigned to prevent a warning.
constexpr unsigned max_int = max_value<int>();
@ -2293,7 +2303,11 @@ FMT_CONSTEXPR const Char* parse_arg_id(const Char* begin, const Char* end,
return begin;
}
if (c >= '0' && c <= '9') {
int index = parse_nonnegative_int(begin, end, handler);
int index = 0;
if (c != '0')
index = parse_nonnegative_int(begin, end, handler);
else
++begin;
if (begin == end || (*begin != '}' && *begin != ':'))
handler.on_error("invalid format string");
else
@ -2543,7 +2557,7 @@ FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
// Doing two passes with memchr (one for '{' and another for '}') is up to
// 2.5x faster than the naive one-pass implementation on big format strings.
const Char* p = begin;
if (*begin != '{' && !find<IS_CONSTEXPR>(begin, end, '{', p))
if (*begin != '{' && !find<IS_CONSTEXPR>(begin + 1, end, '{', p))
return write(begin, end);
write(begin, p);
++p;
@ -2647,10 +2661,9 @@ FMT_CONSTEXPR bool do_check_format_string(basic_string_view<Char> s,
template <typename... Args, typename S,
enable_if_t<(is_compile_string<S>::value), int>>
void check_format_string(S format_str) {
FMT_CONSTEXPR_DECL bool invalid_format =
internal::do_check_format_string<typename S::char_type,
internal::error_handler, Args...>(
to_string_view(format_str));
FMT_CONSTEXPR_DECL bool invalid_format = internal::do_check_format_string<
typename S::char_type, internal::error_handler,
remove_const_t<remove_reference_t<Args>>...>(to_string_view(format_str));
(void)invalid_format;
}
@ -2953,7 +2966,7 @@ struct formatter<T, Char,
template <typename Char> \
struct formatter<Type, Char> : formatter<Base, Char> { \
template <typename FormatContext> \
auto format(const Type& val, FormatContext& ctx) -> decltype(ctx.out()) { \
auto format(Type const& val, FormatContext& ctx) -> decltype(ctx.out()) { \
return formatter<Base, Char>::format(val, ctx); \
} \
}
@ -3157,11 +3170,32 @@ class bytes {
explicit bytes(string_view data) : data_(data) {}
};
template <> struct formatter<bytes> : formatter<string_view> {
template <> struct formatter<bytes> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
using handler_type = internal::dynamic_specs_handler<ParseContext>;
internal::specs_checker<handler_type> handler(handler_type(specs_, ctx),
internal::type::string_type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
internal::check_string_type_spec(specs_.type, ctx.error_handler());
return it;
}
template <typename FormatContext>
auto format(bytes b, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<string_view>::format(b.data_, ctx);
internal::handle_dynamic_spec<internal::width_checker>(
specs_.width, specs_.width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
using range_type =
internal::output_range<typename FormatContext::iterator, char>;
internal::basic_writer<range_type> writer(range_type(ctx.out()));
writer.write_bytes(b.data_, specs_);
return writer.out();
}
private:
internal::dynamic_format_specs<char> specs_;
};
template <typename It, typename Char> struct arg_join : internal::view {
@ -3238,7 +3272,6 @@ arg_join<internal::iterator_t<const Range>, wchar_t> join(const Range& range,
/**
\rst
Converts *value* to ``std::string`` using the default format for type *T*.
It doesn't support user-defined types with custom formatters.
**Example**::
@ -3512,15 +3545,15 @@ FMT_END_NAMESPACE
#define FMT_STRING_IMPL(s, ...) \
[] { \
/* Use a macro-like name to avoid shadowing warnings. */ \
struct FMT_STRING : fmt::compile_string { \
struct FMT_COMPILE_STRING : fmt::compile_string { \
using char_type = fmt::remove_cvref_t<decltype(*s)>; \
__VA_ARGS__ FMT_CONSTEXPR \
FMT_MAYBE_UNUSED __VA_ARGS__ FMT_CONSTEXPR \
operator fmt::basic_string_view<char_type>() const { \
/* FMT_STRING only accepts string literals. */ \
return fmt::internal::literal_to_view(s); \
} \
}; \
return FMT_STRING(); \
return FMT_COMPILE_STRING(); \
}()
/**

View File

@ -132,15 +132,8 @@ class error_code {
int get() const FMT_NOEXCEPT { return value_; }
};
// Define FMT_USE_WINDOWS_H to 0 to disable use of windows.h.
// All the functionality that relies on it will be disabled too.
#ifndef _WIN32
# define FMT_USE_WINDOWS_H 0
#elif !defined(FMT_USE_WINDOWS_H)
# define FMT_USE_WINDOWS_H 1
#endif
#if FMT_USE_WINDOWS_H
#ifdef _WIN32
namespace internal {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
@ -210,7 +203,7 @@ class windows_error : public system_error {
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
#endif
#endif // _WIN32
// A buffered file.
class buffered_file {

View File

@ -303,6 +303,8 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
};
template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
@ -320,6 +322,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
template <typename T> using formatter_type = printf_formatter<T>;
@ -355,6 +358,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
internal::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }

View File

@ -12,7 +12,9 @@
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
@ -104,10 +106,7 @@ struct is_range_<
/// tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U* p)
-> decltype(std::tuple_size<U>::value,
(void)std::declval<typename std::tuple_element<0, U>::type>(),
int());
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
@ -360,6 +359,29 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
std::initializer_list<T> list, string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
std::initializer_list<T> list, wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@ -44,7 +44,7 @@
# endif // _WIN32
#endif // FMT_USE_FCNTL
#if FMT_USE_WINDOWS_H
#ifdef _WIN32
# include <windows.h>
#endif
@ -72,7 +72,7 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE
#if FMT_USE_WINDOWS_H
#ifdef _WIN32
internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
@ -145,7 +145,7 @@ void report_windows_error(int error_code,
fmt::string_view message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message);
}
#endif // FMT_USE_WINDOWS_H
#endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)

View File

@ -48,17 +48,6 @@ endif ()
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
# Check if variadic templates are working and not affected by GCC bug 39653:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
# Can be removed once gcc 4.4 support is dropped.
check_cxx_source_compiles("
template <class T, class ...Types>
struct S { typedef typename S<Types...>::type type; };
int main() {}" SUPPORTS_VARIADIC_TEMPLATES)
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
set (SUPPORTS_VARIADIC_TEMPLATES OFF)
endif ()
# Check if user-defined literals are available
check_cxx_source_compiles("
void operator\"\" _udl(long double);

View File

@ -17,9 +17,7 @@ else ()
target_compile_definitions(gmock PUBLIC GTEST_HAS_PTHREAD=0)
endif ()
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
endif ()
target_compile_definitions(gmock PUBLIC GTEST_LANG_CXX11=0)
if (MSVC)
# Workaround a bug in implementation of variadic templates in MSVC11.
@ -171,18 +169,6 @@ if (FMT_PEDANTIC)
target_compile_definitions(
nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1)
# Test that the library compiles without windows.h.
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_library(no-windows-h-test ../src/format.cc)
target_include_directories(
no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
if (FMT_PEDANTIC)
target_compile_options(no-windows-h-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
target_include_directories(no-windows-h-test SYSTEM PUBLIC gtest gmock)
endif ()
add_test(compile-error-test ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"

View File

@ -8,17 +8,12 @@
#include "fmt/core.h"
#include "gtest.h"
#if GTEST_HAS_DEATH_TEST
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEBUG_DEATH(statement, regex)
#else
# define EXPECT_DEBUG_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
#endif
TEST(AssertTest, Fail) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(FMT_ASSERT(false, "don't panic!"),
"don't panic!");
#if GTEST_HAS_DEATH_TEST
EXPECT_DEBUG_DEATH(FMT_ASSERT(false, "don't panic!"), "don't panic!");
#else
fmt::print("warning: death tests are not supported\n");
#endif
}
bool test_condition = false;

View File

@ -50,6 +50,8 @@ TEST(ColorsTest, ColorsPrint) {
TEST(ColorsTest, Format) {
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"),
L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m");
EXPECT_EQ(

View File

@ -15,9 +15,8 @@
#include <string>
#include <type_traits>
#include "test-assert.h"
#include "gmock.h"
#include "test-assert.h"
// Check if fmt/core.h compiles with windows.h included before it.
#ifdef _WIN32
@ -402,6 +401,83 @@ TEST(ArgTest, VisitInvalidArg) {
fmt::visit_format_arg(visitor, arg);
}
TEST(FormatDynArgsTest, Basic) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc1");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
EXPECT_EQ("42 and abc1 and 1.5", result);
}
TEST(FormatDynArgsTest, StringsAndRefs) {
// Unfortunately the tests are compiled with old ABI so strings use COW.
fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[] = "1234567890";
store.push_back(str);
store.push_back(std::cref(str));
store.push_back(fmt::string_view{str});
str[0] = 'X';
std::string result = fmt::vformat("{} and {} and {}", store);
EXPECT_EQ("1234567890 and X234567890 and X234567890", result);
}
struct custom_type {
int i = 0;
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<custom_type> {
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const custom_type& p, FormatContext& ctx) -> decltype(format_to(
ctx.out(), std::declval<typename FormatContext::char_type const*>())) {
return format_to(ctx.out(), "cust={}", p.i);
}
};
FMT_END_NAMESPACE
TEST(FormatDynArgsTest, CustomFormat) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
custom_type c{};
store.push_back(c);
++c.i;
store.push_back(c);
++c.i;
store.push_back(std::cref(c));
++c.i;
std::string result = fmt::vformat("{} and {} and {}", store);
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
}
TEST(FormatDynArgsTest, NamedArgByRef) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
// Note: fmt::arg() constructs an object which holds a reference
// to its value. It's not an aggregate, so it doesn't extend the
// reference lifetime. As a result, it's a very bad idea passing temporary
// as a named argument value. Only GCC with optimization level >0
// complains about this.
//
// A real life usecase is when you have both name and value alive
// guarantee their lifetime and thus don't want them to be copied into
// storages.
int a1_val{42};
auto a1 = fmt::arg("a1_", a1_val);
store.push_back(std::cref(a1));
std::string result = fmt::vformat("{a1_}", // and {} and {}",
store);
EXPECT_EQ("42", result);
}
TEST(StringViewTest, ValueType) {
static_assert(std::is_same<string_view::value_type, char>::value, "");
}
@ -469,6 +545,10 @@ struct convertible_to_int {
operator int() const { return 42; }
};
struct convertible_to_c_string {
operator const char*() const { return "foo"; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<convertible_to_int> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
@ -478,10 +558,21 @@ template <> struct formatter<convertible_to_int> {
return std::copy_n("foo", 3, ctx.out());
}
};
template <> struct formatter<convertible_to_c_string> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_c_string, format_context& ctx)
-> decltype(ctx.out()) {
return std::copy_n("bar", 3, ctx.out());
}
};
FMT_END_NAMESPACE
TEST(CoreTest, FormatterOverridesImplicitConversion) {
EXPECT_EQ(fmt::format("{}", convertible_to_int()), "foo");
EXPECT_EQ(fmt::format("{}", convertible_to_c_string()), "bar");
}
namespace my_ns {

View File

@ -0,0 +1,6 @@
// Copyright (c) 2020 Vladimir Solontsov
// SPDX-License-Identifier: MIT Licence
#include <fmt/core.h>
#include "gtest-extra.h"

View File

@ -10,12 +10,11 @@
#include "test-assert.h"
// Include format.cc instead of format.h to test implementation.
#include "../src/format.cc"
#include "fmt/printf.h"
#include <algorithm>
#include <cstring>
#include "../src/format.cc"
#include "fmt/printf.h"
#include "gmock.h"
#include "gtest-extra.h"
#include "util.h"
@ -430,8 +429,10 @@ TEST(FormatTest, CountCodePoints) {
#ifndef __cpp_char8_t
using fmt::char8_t;
#endif
EXPECT_EQ(4, fmt::internal::count_code_points(
fmt::basic_string_view<char8_t>(reinterpret_cast<const char8_t*>("ёжик"))));
EXPECT_EQ(
4, fmt::internal::count_code_points(
fmt::basic_string_view<fmt::internal::char8_type>(
reinterpret_cast<const fmt::internal::char8_type*>("ёжик"))));
}
// Tests fmt::internal::count_digits for integer type Int.

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include <stdint.h>
#include <cctype>
#include <cfloat>
#include <climits>
@ -987,7 +988,9 @@ TEST(FormatterTest, Width) {
EXPECT_EQ(" 0xcafe", format("{0:10}", reinterpret_cast<void*>(0xcafe)));
EXPECT_EQ("x ", format("{0:11}", 'x'));
EXPECT_EQ("str ", format("{0:12}", "str"));
EXPECT_EQ(fmt::format("{:*^5}", "🤡"), "**🤡**");
}
template <typename T> inline T const_check(T value) { return value; }
TEST(FormatterTest, RuntimeWidth) {
@ -1121,6 +1124,7 @@ TEST(FormatterTest, Precision) {
"000000000000000000000000000000000000000000000000000P+127",
format("{:.838A}", -2.14001164E+38));
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
EXPECT_EQ("1.23", format("{:.02f}", 1.234));
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
format_error,
@ -1859,6 +1863,8 @@ TEST(FormatTest, CustomFormatCompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer()));
Answer answer;
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer));
char buf[10] = {};
fmt::format_to(buf, FMT_STRING("{}"), answer);
const Answer const_answer = Answer();
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer));
}
@ -2499,37 +2505,6 @@ TEST(FormatTest, FmtStringInTemplate) {
#endif // FMT_USE_CONSTEXPR
// C++20 feature test, since r346892 Clang considers char8_t a fundamental
// type in this mode. If this is the case __cpp_char8_t will be defined.
#ifndef __cpp_char8_t
// Locally provide type char8_t defined in format.h
using fmt::char8_t;
#endif
// Convert 'char8_t' character sequences to 'char' sequences
// Otherwise GTest will insist on inserting 'char8_t' NTBS into a 'char' stream,
// but basic_ostream<char>::operator<< overloads taking 'char8_t' arguments
// are defined as deleted by P1423.
// Handling individual 'char8_t's is done inline.
std::string from_u8str(const std::basic_string<char8_t>& str) {
return std::string(str.begin(), str.end());
}
std::string from_u8str(const fmt::basic_string_view<char8_t>& str) {
return std::string(str.begin(), str.end());
}
#if FMT_USE_USER_DEFINED_LITERALS
TEST(FormatTest, U8StringViewLiteral) {
using namespace fmt::literals;
fmt::basic_string_view<char8_t> s = "ab"_u;
EXPECT_EQ(s.size(), 2u);
const char8_t* data = s.data();
EXPECT_EQ(char(data[0]), 'a');
EXPECT_EQ(char(data[1]), 'b');
EXPECT_EQ(from_u8str(format("{:*^5}"_u, "🤡"_u)), from_u8str("**🤡**"_u));
}
#endif
TEST(FormatTest, EmphasisNonHeaderOnly) {
// Ensure this compiles even if FMT_HEADER_ONLY is not defined.
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
@ -2568,10 +2543,18 @@ TEST(FormatTest, FormatCustomChar) {
EXPECT_EQ(result[0], mychar('x'));
}
// Convert a char8_t string to std::string. Otherwise GTest will insist on
// inserting `char8_t` NTBS into a `char` stream which is disabled by P1423.
template <typename S> std::string from_u8str(const S& str) {
return std::string(str.begin(), str.end());
}
TEST(FormatTest, FormatUTF8Precision) {
using str_type = std::basic_string<char8_t>;
str_type format(reinterpret_cast<const char8_t*>(u8"{:.4}"));
str_type str(reinterpret_cast<const char8_t*>(u8"caf\u00e9s")); // cafés
using str_type = std::basic_string<fmt::internal::char8_type>;
str_type format(
reinterpret_cast<const fmt::internal::char8_type*>(u8"{:.4}"));
str_type str(reinterpret_cast<const fmt::internal::char8_type*>(
u8"caf\u00e9s")); // cafés
auto result = fmt::format(format, str);
EXPECT_EQ(fmt::internal::count_code_points(result), 4);
EXPECT_EQ(result.size(), 5);

View File

@ -264,17 +264,13 @@ struct explicitly_convertible_to_string_like {
}
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLikeIgnoreInserter) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#ifdef FMT_USE_STRING_VIEW
@ -284,17 +280,13 @@ struct explicitly_convertible_to_std_string_view {
}
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_std_string_view) {
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringViewIgnoreInserter) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif // FMT_USE_STRING_VIEW

View File

@ -474,9 +474,13 @@ TEST(PrintfTest, Location) {
// TODO: test %n
}
enum E { A = 42 };
enum test_enum { answer = 42 };
TEST(PrintfTest, Enum) { EXPECT_PRINTF("42", "%d", A); }
TEST(PrintfTest, Enum) {
EXPECT_PRINTF("42", "%d", answer);
volatile test_enum volatile_enum = answer;
EXPECT_PRINTF("42", "%d", volatile_enum);
}
#if FMT_USE_FCNTL
TEST(PrintfTest, Examples) {
@ -498,7 +502,9 @@ TEST(PrintfTest, PrintfError) {
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
TEST(PrintfTest, PrintfCustom) {
EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
// The test is disabled for now because it requires decoupling
// fallback_formatter::format from format_context.
//EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
}
TEST(PrintfTest, OStream) {

View File

@ -10,6 +10,7 @@
// {fmt} support for ranges, containers and types tuple interface.
#include "fmt/ranges.h"
#include "gtest.h"
// Check if 'if constexpr' is supported.
@ -44,9 +45,10 @@ TEST(RangesTest, FormatPair) {
}
TEST(RangesTest, FormatTuple) {
std::tuple<int64_t, float, std::string, char> tu1{42, 1.5f, "this is tuple",
'i'};
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", tu1));
std::tuple<int64_t, float, std::string, char> t{42, 1.5f, "this is tuple",
'i'};
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", t));
EXPECT_EQ("()", fmt::format("{}", std::tuple<>()));
}
TEST(RangesTest, JoinTuple) {
@ -68,6 +70,12 @@ TEST(RangesTest, JoinTuple) {
EXPECT_EQ("4.0", fmt::format("{}", fmt::join(t4, "/")));
}
TEST(RangesTest, JoinInitializerList) {
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", ")));
EXPECT_EQ("fmt rocks !",
fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")));
}
struct my_struct {
int32_t i;
std::string str; // can throw