add custom back_insert_iterator with constexpr member functions

This commit is contained in:
Alexey Ochapov 2020-11-08 00:42:57 +03:00
parent 701ed6c874
commit 8a2de0f85d
No known key found for this signature in database
GPG Key ID: 9DC52E8F031B8DA8
5 changed files with 104 additions and 19 deletions

View File

@ -645,10 +645,17 @@ struct is_contiguous<std::basic_string<Char>> : std::true_type {};
namespace detail {
#ifdef FMT_USE_CUSTOM_BACK_INSERT_ITERATOR
template <typename Container> class back_insert_iterator;
#else
template <typename Container>
using back_insert_iterator = std::back_insert_iterator<Container>;
#endif
// Extracts a reference to the container from back_insert_iterator.
template <typename Container>
inline Container& get_container(std::back_insert_iterator<Container> it) {
using bi_iterator = std::back_insert_iterator<Container>;
inline Container& get_container(back_insert_iterator<Container> it) {
using bi_iterator = back_insert_iterator<Container>;
struct accessor : bi_iterator {
accessor(bi_iterator iter) : bi_iterator(iter) {}
using bi_iterator::container;
@ -805,8 +812,83 @@ template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
T* out() { return &*this->end(); }
};
#ifdef FMT_USE_CUSTOM_BACK_INSERT_ITERATOR
template <class Container> class back_insert_iterator {
protected:
Container* container;
public:
using iterator_category = std::output_iterator_tag;
using value_type = void;
using difference_type = void;
using pointer = void;
using reference = void;
using container_type = Container;
using container_value_type = typename Container::value_type;
constexpr explicit back_insert_iterator(Container& container)
: container(std::addressof(container)) {}
constexpr explicit back_insert_iterator(
const std::back_insert_iterator<Container>& bi_iterator) {
using iterator = std::back_insert_iterator<Container>;
struct accessor : iterator {
explicit accessor(iterator iter) : iterator(iter) {}
using iterator::container;
};
container = accessor(bi_iterator).container;
}
constexpr back_insert_iterator& operator=(const container_value_type& value) {
container->push_back(value);
return *this;
}
constexpr back_insert_iterator& operator=(container_value_type&& value) {
container->push_back(std::move(value));
return *this;
}
constexpr back_insert_iterator& operator*() { return *this; }
constexpr back_insert_iterator& operator++() { return *this; }
constexpr back_insert_iterator operator++(int) { return *this; }
};
template <class Container>
constexpr back_insert_iterator<Container> back_inserter(Container& container) {
return back_insert_iterator<Container>(container);
}
#endif
// A buffer that writes to a container with the contiguous storage.
template <typename Container>
class iterator_buffer<back_insert_iterator<Container>,
enable_if_t<is_contiguous<Container>::value,
typename Container::value_type>>
final : public buffer<typename Container::value_type> {
private:
Container& container_;
protected:
void grow(size_t capacity) final FMT_OVERRIDE {
container_.resize(capacity);
this->set(&container_[0], capacity);
}
public:
explicit iterator_buffer(Container& c)
: buffer<typename Container::value_type>(c.size()), container_(c) {}
explicit iterator_buffer(back_insert_iterator<Container> out, size_t = 0)
: iterator_buffer(get_container(out)) {}
back_insert_iterator<Container> out() {
return back_inserter(container_);
}
};
#if FMT_USE_CUSTOM_BACK_INSERT_ITERATOR
// Explicit instantiation for std::back_insert_iterator
template <typename Container>
class iterator_buffer<std::back_insert_iterator<Container>,
enable_if_t<is_contiguous<Container>::value,
typename Container::value_type>>
@ -824,11 +906,12 @@ class iterator_buffer<std::back_insert_iterator<Container>,
explicit iterator_buffer(Container& c)
: buffer<typename Container::value_type>(c.size()), container_(c) {}
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
: iterator_buffer(get_container(out)) {}
: iterator_buffer(get_container(back_insert_iterator<Container>{out})) {}
std::back_insert_iterator<Container> out() {
return std::back_inserter(container_);
}
};
#endif
// A buffer that counts the number of code units written discarding the output.
template <typename T = char> class counting_buffer final : public buffer<T> {
@ -853,8 +936,8 @@ template <typename T = char> class counting_buffer final : public buffer<T> {
// An output iterator that appends to the buffer.
// It is used to reduce symbol sizes for the common case.
template <typename T>
class buffer_appender : public std::back_insert_iterator<buffer<T>> {
using base = std::back_insert_iterator<buffer<T>>;
class buffer_appender : public back_insert_iterator<buffer<T>> {
using base = back_insert_iterator<buffer<T>>;
public:
explicit buffer_appender(buffer<T>& buf) : base(buf) {}
@ -1385,13 +1468,13 @@ struct is_output_iterator<
template <typename OutputIt>
struct is_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_back_insert_iterator<std::back_insert_iterator<Container>>
struct is_back_insert_iterator<back_insert_iterator<Container>>
: std::true_type {};
template <typename OutputIt>
struct is_contiguous_back_insert_iterator : std::false_type {};
template <typename Container>
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
struct is_contiguous_back_insert_iterator<back_insert_iterator<Container>>
: is_contiguous<Container> {};
template <typename Char>
struct is_contiguous_back_insert_iterator<buffer_appender<Char>>

View File

@ -376,7 +376,7 @@ template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
__attribute__((no_sanitize("undefined")))
#endif
inline checked_ptr<typename Container::value_type>
reserve(std::back_insert_iterator<Container> it, size_t n) {
reserve(back_insert_iterator<Container> it, size_t n) {
Container& c = get_container(it);
size_t size = c.size();
c.resize(size + n);
@ -407,8 +407,8 @@ template <typename T> T* to_pointer(buffer_appender<T> it, size_t n) {
}
template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
inline std::back_insert_iterator<Container> base_iterator(
std::back_insert_iterator<Container>& it,
inline back_insert_iterator<Container> base_iterator(
back_insert_iterator<Container>& it,
checked_ptr<typename Container::value_type>) {
return it;
}

View File

@ -31,27 +31,27 @@ template dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
// DEPRECATED! This function exists for ABI compatibility.
template <typename Char>
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
typename basic_format_context<back_insert_iterator<buffer<Char>>,
Char>::iterator
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>>
args) {
using iterator = std::back_insert_iterator<buffer<char>>;
using context = basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>;
using iterator = back_insert_iterator<buffer<char>>;
using context =
basic_format_context<back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>;
auto out = iterator(buf);
format_handler<iterator, Char, context> h(out, format_str, args, {});
parse_format_string<false>(format_str, h);
return out;
}
template basic_format_context<std::back_insert_iterator<buffer<char>>,
template basic_format_context<back_insert_iterator<buffer<char>>,
char>::iterator
vformat_to(buffer<char>&, string_view,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<char>>>,
back_insert_iterator<buffer<type_identity_t<char>>>,
type_identity_t<char>>>);
} // namespace detail

View File

@ -292,7 +292,7 @@ VISIT_TYPE(unsigned long, unsigned long long);
{ \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
EXPECT_CALL(visitor, visit(expected)); \
using iterator = std::back_insert_iterator<buffer<Char>>; \
using iterator = fmt::detail::back_insert_iterator<buffer<Char>>; \
fmt::visit_format_arg( \
visitor, make_arg<fmt::basic_format_context<iterator, Char>>(value)); \
}

View File

@ -175,6 +175,8 @@ TEST(IteratorTest, IsOutputIterator) {
EXPECT_TRUE(
(fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>,
char>::value));
EXPECT_TRUE((fmt::detail::is_output_iterator<
fmt::detail::back_insert_iterator<std::string>, char>::value));
EXPECT_TRUE(
(fmt::detail::is_output_iterator<std::string::iterator, char>::value));
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator,