adds basic_print_iterator to {fmt}

`basic_print_iterator` is a {fmt}-friendly iterator that is a
replacement for `std::ostream_iterator` when writing to `stdout`,
`stderr`, or a file.
This commit is contained in:
Christopher Di Bella 2020-02-22 22:05:25 -08:00
parent b2d3a86ec0
commit ad460829da
3 changed files with 185 additions and 1 deletions

81
include/fmt/iterator.h Normal file
View File

@ -0,0 +1,81 @@
// Copyright (c) Google LLC.
// SPDX-License-Identifier: MIT Licence
//
#ifndef FMT_ITERATOR_H_
#define FMT_ITERATOR_H_
#include <cstdio>
#include "core.h"
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
#if defined(__cpp_concepts) and __cpp_concepts >= 201907
template <typename T> concept formattable = requires(T&& t) {
::fmt::format("{}", std::forward<T>(t));
};
#endif // __cpp_concepts
} // namespace internal
// Iterator that allows for writing to file via fmt::print.
//
template <typename CharT> class basic_print_iterator {
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using char_type = CharT;
basic_print_iterator() = default;
// Constructs a print-iterator that writes to stdout.
explicit basic_print_iterator(basic_string_view<CharT> const format_specifier)
: file_(stdout), format_specifier_(format_specifier) {}
// Constructs a print-iterator that writes to a designated file.
explicit basic_print_iterator(
std::FILE* file, basic_string_view<CharT> const format_specifier = "{}")
: file_(file), format_specifier_(format_specifier) {}
basic_print_iterator& operator++() noexcept { return *this; }
basic_print_iterator& operator++(int) noexcept { return *this; }
basic_print_iterator& operator*() noexcept { return *this; }
#if defined(__cpp_concepts) and __cpp_concepts >= 201907
template <internal::formattable T>
#else
template <typename T>
#endif // __cpp_concepts
void operator=(T&& t)
{
::fmt::print(file_, format_specifier_, std::forward<T>(t));
}
private:
std::FILE* file_ = nullptr;
basic_string_view<CharT> format_specifier_;
};
#if __cplusplus >= 201703
template <typename CharT>
basic_print_iterator(basic_string_view<CharT>) -> basic_print_iterator<CharT>;
template <typename CharT>
basic_print_iterator(std::FILE*, basic_string_view<CharT>)
-> basic_print_iterator<CharT>;
#endif // __cplusplus
using print_iterator = basic_print_iterator<char>;
using wprint_iterator = basic_print_iterator<wchar_t>;
using u8print_iterator = basic_print_iterator<char8_t>;
using u16print_iterator = basic_print_iterator<char16_t>;
using u32print_iterator = basic_print_iterator<char32_t>;
FMT_END_NAMESPACE
#endif // FMT_ITERATOR_H_

View File

@ -24,7 +24,7 @@ endif ()
if (MSVC)
# Workaround a bug in implementation of variadic templates in MSVC11.
target_compile_definitions(gmock PUBLIC _VARIADIC_MAX=10)
# Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions.
target_compile_definitions(gmock PRIVATE _CRT_SECURE_NO_WARNINGS)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
@ -103,6 +103,7 @@ endif ()
if (NOT (MSVC AND BUILD_SHARED_LIBS))
add_fmt_test(format-impl-test)
endif ()
add_fmt_test(iterator-test)
add_fmt_test(locale-test)
add_fmt_test(ostream-test)
add_fmt_test(compile-test)

102
test/iterator-test.cc Normal file
View File

@ -0,0 +1,102 @@
// Copyright (c) Google LLC.
// SPDX-License-Identifier: MIT Licence
//
#include "fmt/iterator.h"
#include <algorithm>
#include <deque>
#include <iterator>
#include <vector>
#include "fmt/ranges.h"
#include "gtest-extra.h"
#include "gtest/gtest.h"
namespace {
#if defined(__cpp_lib_ranges) and __cpp_lib_ranges >= 201911
template <typename I> void check_iterator_conformance() {
static_assert(not std::readable<I>);
static_assert(std::output_iterator<I, int>);
static_assert(std::output_iterator<I, double>);
static_assert(std::output_iterator<I, std::string>);
static_assert(not std::output_iterator<I, std::vector<int>>);
}
template [[maybe_unused]] void
check_iterator_conformance<fmt::print_iterator>();
template [[maybe_unused]] void
check_iterator_conformance<fmt::wprint_iterator>();
template [[maybe_unused]] void
check_iterator_conformance<fmt::u8print_iterator>();
template [[maybe_unused]] void
check_iterator_conformance<fmt::u16print_iterator>();
template [[maybe_unused]] void
check_iterator_conformance<fmt::u32print_iterator>();
#endif // __cpp_lib_ranges
template <typename I> void check_default_operations() {
auto i = I{};
{
auto& prefix = ++i;
EXPECT_EQ(std::addressof(i), std::addressof(prefix));
}
{
auto& postfix = i++;
EXPECT_EQ(std::addressof(i), std::addressof(postfix));
}
{
auto& deref = *i;
EXPECT_EQ(std::addressof(i), std::addressof(deref));
}
}
TEST(PrintIterator, DefaultOperators) {
check_default_operations<fmt::print_iterator>();
check_default_operations<fmt::wprint_iterator>();
check_default_operations<fmt::u16print_iterator>();
check_default_operations<fmt::u32print_iterator>();
#ifdef __cpp_char8_t
check_default_operations<fmt::u8print_iterator>();
#endif // __cpp_char8_t
}
enum class decorator { off, on };
template <decorator is_decorated>
void check_assignment(fmt::print_iterator simple_writer, std::FILE* out);
template <>
void check_assignment<decorator::off>(fmt::print_iterator simple_writer,
std::FILE* out) {
EXPECT_WRITE(out, simple_writer = 0, "0");
EXPECT_WRITE(out, simple_writer = 0.0, "0.0");
EXPECT_WRITE(out, simple_writer = "hello there", "hello there");
auto v = std::vector<std::vector<int>>{{0, 1, 2}, {3, 4, 5}, {6, 7, 8, 9}};
EXPECT_WRITE(out, simple_writer = v, "{{0, 1, 2}, {3, 4, 5}, {6, 7, 8, 9}}");
// GCC 4.8 silencing
(void)simple_writer, (void)out;
}
template <>
void check_assignment<decorator::on>(fmt::print_iterator decorated_writer,
std::FILE* out) {
auto d = std::deque<int>{16, 8, 4, 2, 1};
EXPECT_WRITE(out, std::copy(begin(d), end(d), decorated_writer),
"-16!-8!-4!-2!-1!");
EXPECT_WRITE(out, decorated_writer = 0.0, "-0.0!");
EXPECT_WRITE(out, decorated_writer = "Pusheen the Cat", "-Pusheen the Cat!");
// GCC 4.8 silencing
(void)decorated_writer, (void)out;
}
TEST(PrintIterator, DefaultDestinationAssignment) {
check_assignment<decorator::off>(fmt::print_iterator("{}"), stdout);
check_assignment<decorator::on>(fmt::print_iterator("-{}!"), stdout);
check_assignment<decorator::off>(fmt::print_iterator(stderr, "{}"), stderr);
check_assignment<decorator::on>(fmt::print_iterator(stderr, "-{}!"), stderr);
}
} // namespace