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:
parent
b2d3a86ec0
commit
ad460829da
81
include/fmt/iterator.h
Normal file
81
include/fmt/iterator.h
Normal 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_
|
||||
@ -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
102
test/iterator-test.cc
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user