Better buffer_cat:

buffer_cat now determines if all of the buffer sequences in
the list of passed buffer sequences each have value types which
are convertible to mutable_buffer. The returned concatenated
sequence will be a MutableBufferSequence if all the passed
buffer sequences meet the requirements of MutableBufferSequence,
else the returned concatenated sequence will be a ConstBufferSequence.
This commit is contained in:
Vinnie Falco
2016-11-08 19:00:40 -05:00
parent 6afa6afc98
commit be67224390
7 changed files with 305 additions and 127 deletions

View File

@@ -5,6 +5,7 @@
* Use boost::lexical_cast instead of std::to_string * Use boost::lexical_cast instead of std::to_string
* Fix prepare_buffers value_type * Fix prepare_buffers value_type
* Fix consuming_buffers value_type * Fix consuming_buffers value_type
* Better buffer_cat
HTTP HTTP

View File

@@ -19,18 +19,21 @@
namespace beast { namespace beast {
/** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`. /** Concatenate 2 or more buffer sequences.
This function returns a @b `ConstBufferSequence` that when iterated, This function returns a constant or mutable buffer sequence which,
efficiently concatenates the input buffer sequences. Copies of the when iterated, efficiently concatenates the input buffer sequences.
arguments passed will be made; however, the returned object does Copies of the arguments passed will be made; however, the returned
not take ownership of the underlying memory. The application is still object does not take ownership of the underlying memory. The application
responsible for managing the lifetime of the referenced memory. is still responsible for managing the lifetime of the referenced memory.
@param buffers The list of buffer sequences to concatenate. @param buffers The list of buffer sequences to concatenate.
@return A new @b `ConstBufferSequence` that represents the @return A new buffer sequence that represents the concatenation of
concatenation of the input buffer sequences. the input buffer sequences. This buffer sequence will be a
@b MutableBufferSequence if each of the passed buffer sequences is
also a @b MutableBufferSequence, else the returned buffer sequence
will be a @b ConstBufferSequence.
*/ */
#if GENERATING_DOCS #if GENERATING_DOCS
template<class... BufferSequence> template<class... BufferSequence>
@@ -38,14 +41,15 @@ implementation_defined
buffer_cat(BufferSequence const&... buffers) buffer_cat(BufferSequence const&... buffers)
#else #else
template<class B1, class B2, class... Bn> template<class B1, class B2, class... Bn>
detail::buffer_cat_helper< detail::buffer_cat_helper<B1, B2, Bn...>
boost::asio::const_buffer, B1, B2, Bn...>
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn) buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
#endif #endif
{ {
static_assert(
detail::is_all_ConstBufferSequence<B1, B2, Bn...>::value,
"BufferSequence requirements not met");
return detail::buffer_cat_helper< return detail::buffer_cat_helper<
boost::asio::const_buffer, B1, B2, Bn...>{b1, b2, bn...};
B1, B2, Bn...>{b1, b2, bn...};
} }
} // beast } // beast

View File

@@ -8,6 +8,8 @@
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP #ifndef BEAST_DETAIL_BUFFER_CAT_HPP
#define BEAST_DETAIL_BUFFER_CAT_HPP #define BEAST_DETAIL_BUFFER_CAT_HPP
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <cstdint> #include <cstdint>
#include <iterator> #include <iterator>
@@ -19,24 +21,36 @@
namespace beast { namespace beast {
namespace detail { namespace detail {
template<class ValueType, class... Bs> template<class... Bn>
struct common_buffers_type
{
using type = typename std::conditional<
std::is_convertible<std::tuple<Bn...>,
typename repeat_tuple<sizeof...(Bn),
boost::asio::mutable_buffer>::type>::value,
boost::asio::mutable_buffer,
boost::asio::const_buffer>::type;
};
template<class... Bn>
class buffer_cat_helper class buffer_cat_helper
{ {
std::tuple<Bs...> bs_; std::tuple<Bn...> bn_;
public: public:
using value_type = ValueType; using value_type = typename
common_buffers_type<Bn...>::type;
class const_iterator; class const_iterator;
buffer_cat_helper(buffer_cat_helper&&) = default; buffer_cat_helper(buffer_cat_helper&&) = default;
buffer_cat_helper(buffer_cat_helper const&) = default; buffer_cat_helper(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = default; buffer_cat_helper& operator=(buffer_cat_helper&&) = delete;
buffer_cat_helper& operator=(buffer_cat_helper const&) = default; buffer_cat_helper& operator=(buffer_cat_helper const&) = delete;
explicit explicit
buffer_cat_helper(Bs const&... bs) buffer_cat_helper(Bn const&... bn)
: bs_(bs...) : bn_(bn...)
{ {
} }
@@ -47,39 +61,22 @@ public:
end() const; end() const;
}; };
template<class U> template<class... Bn>
std::size_t constexpr class buffer_cat_helper<Bn...>::const_iterator
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<class ValueType, class... Bs>
class buffer_cat_helper<
ValueType, Bs...>::const_iterator
{ {
std::size_t n_; std::size_t n_;
std::tuple<Bs...> const* bs_; std::tuple<Bn...> const* bn_;
std::array<std::uint8_t, std::array<std::uint8_t,
max_sizeof<typename Bs::const_iterator...>()> buf_; max_sizeof<typename Bn::const_iterator...>()> buf_;
friend class buffer_cat_helper<ValueType, Bs...>; friend class buffer_cat_helper<Bn...>;
template<std::size_t I> template<std::size_t I>
using C = std::integral_constant<std::size_t, I>; using C = std::integral_constant<std::size_t, I>;
template<std::size_t I> template<std::size_t I>
using iter_t = typename std::tuple_element< using iter_t = typename std::tuple_element<
I, std::tuple<Bs...>>::type::const_iterator; I, std::tuple<Bn...>>::type::const_iterator;
template<std::size_t I> template<std::size_t I>
iter_t<I>& iter_t<I>&
@@ -98,7 +95,8 @@ class buffer_cat_helper<
} }
public: public:
using value_type = ValueType; using value_type = typename
common_buffers_type<Bn...>::type;
using pointer = value_type const*; using pointer = value_type const*;
using reference = value_type; using reference = value_type;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
@@ -151,39 +149,39 @@ public:
private: private:
const_iterator( const_iterator(
std::tuple<Bs...> const& bs, bool at_end); std::tuple<Bn...> const& bn, bool at_end);
void void
construct(C<sizeof...(Bs)>) construct(C<sizeof...(Bn)> const&)
{ {
auto constexpr I = sizeof...(Bs); auto constexpr I = sizeof...(Bn);
n_ = I; n_ = I;
} }
template<std::size_t I> template<std::size_t I>
void void
construct(C<I>) construct(C<I> const&)
{ {
if(std::get<I>(*bs_).begin() != if(std::get<I>(*bn_).begin() !=
std::get<I>(*bs_).end()) std::get<I>(*bn_).end())
{ {
n_ = I; n_ = I;
new(buf_.data()) iter_t<I>{ new(buf_.data()) iter_t<I>{
std::get<I>(*bs_).begin()}; std::get<I>(*bn_).begin()};
return; return;
} }
construct(C<I+1>{}); construct(C<I+1>{});
} }
void void
destroy(C<sizeof...(Bs)>) destroy(C<sizeof...(Bn)> const&)
{ {
return; return;
} }
template<std::size_t I> template<std::size_t I>
void void
destroy(C<I>) destroy(C<I> const&)
{ {
if(n_ == I) if(n_ == I)
{ {
@@ -195,13 +193,15 @@ private:
} }
void void
move(C<sizeof...(Bs)>, const_iterator&&) move(const_iterator&&,
C<sizeof...(Bn)> const&)
{ {
} }
template<std::size_t I> template<std::size_t I>
void void
move(C<I>, const_iterator&& other) move(const_iterator&& other,
C<I> const&)
{ {
if(n_ == I) if(n_ == I)
{ {
@@ -209,17 +209,19 @@ private:
std::move(other.iter<I>())}; std::move(other.iter<I>())};
return; return;
} }
move(C<I+1>{}, std::move(other)); move(std::move(other), C<I+1>{});
} }
void void
copy(C<sizeof...(Bs)>, const_iterator const&) copy(const_iterator const&,
C<sizeof...(Bn)> const&)
{ {
} }
template<std::size_t I> template<std::size_t I>
void void
copy(C<I>, const_iterator const& other) copy(const_iterator const& other,
C<I> const&)
{ {
if(n_ == I) if(n_ == I)
{ {
@@ -227,35 +229,36 @@ private:
other.iter<I>()}; other.iter<I>()};
return; return;
} }
copy(C<I+1>{}, other); copy(other, C<I+1>{});
} }
bool bool
equal(C<sizeof...(Bs)>, equal(const_iterator const&,
const_iterator const&) const C<sizeof...(Bn)> const&) const
{ {
return true; return true;
} }
template<std::size_t I> template<std::size_t I>
bool bool
equal(C<I>, const_iterator const& other) const equal(const_iterator const& other,
C<I> const&) const
{ {
if(n_ == I) if(n_ == I)
return iter<I>() == other.iter<I>(); return iter<I>() == other.iter<I>();
return equal(C<I+1>{}, other); return equal(other, C<I+1>{});
} }
[[noreturn]] [[noreturn]]
reference reference
dereference(C<sizeof...(Bs)>) const dereference(C<sizeof...(Bn)> const&) const
{ {
throw std::logic_error("invalid iterator"); throw std::logic_error("invalid iterator");
} }
template<std::size_t I> template<std::size_t I>
reference reference
dereference(C<I>) const dereference(C<I> const&) const
{ {
if(n_ == I) if(n_ == I)
return *iter<I>(); return *iter<I>();
@@ -264,19 +267,19 @@ private:
[[noreturn]] [[noreturn]]
void void
increment(C<sizeof...(Bs)>) increment(C<sizeof...(Bn)> const&)
{ {
throw std::logic_error("invalid iterator"); throw std::logic_error("invalid iterator");
} }
template<std::size_t I> template<std::size_t I>
void void
increment(C<I>) increment(C<I> const&)
{ {
if(n_ == I) if(n_ == I)
{ {
if(++iter<I>() != if(++iter<I>() !=
std::get<I>(*bs_).end()) std::get<I>(*bn_).end())
return; return;
using Iter = iter_t<I>; using Iter = iter_t<I>;
iter<I>().~Iter(); iter<I>().~Iter();
@@ -286,23 +289,23 @@ private:
} }
void void
decrement(C<sizeof...(Bs)>) decrement(C<sizeof...(Bn)> const&)
{ {
auto constexpr I = sizeof...(Bs); auto constexpr I = sizeof...(Bn);
if(n_ == I) if(n_ == I)
{ {
--n_; --n_;
new(buf_.data()) iter_t<I-1>{ new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()}; std::get<I-1>(*bn_).end()};
} }
decrement(C<I-1>{}); decrement(C<I-1>{});
} }
void void
decrement(C<0>) decrement(C<0> const&)
{ {
auto constexpr I = 0; auto constexpr I = 0;
if(iter<I>() != std::get<I>(*bs_).begin()) if(iter<I>() != std::get<I>(*bn_).begin())
{ {
--iter<I>(); --iter<I>();
return; return;
@@ -312,11 +315,11 @@ private:
template<std::size_t I> template<std::size_t I>
void void
decrement(C<I>) decrement(C<I> const&)
{ {
if(n_ == I) if(n_ == I)
{ {
if(iter<I>() != std::get<I>(*bs_).begin()) if(iter<I>() != std::get<I>(*bn_).begin())
{ {
--iter<I>(); --iter<I>();
return; return;
@@ -325,7 +328,7 @@ private:
using Iter = iter_t<I>; using Iter = iter_t<I>;
iter<I>().~Iter(); iter<I>().~Iter();
new(buf_.data()) iter_t<I-1>{ new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()}; std::get<I-1>(*bn_).end()};
} }
decrement(C<I-1>{}); decrement(C<I-1>{});
} }
@@ -333,54 +336,54 @@ private:
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<class ValueType, class... Bs> template<class... Bn>
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::~const_iterator() const_iterator::~const_iterator()
{ {
destroy(C<0>{}); destroy(C<0>{});
} }
template<class ValueType, class... Bs> template<class... Bn>
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::const_iterator() const_iterator::const_iterator()
: n_(sizeof...(Bs)) : n_(sizeof...(Bn))
, bs_(nullptr) , bn_(nullptr)
{ {
} }
template<class ValueType, class... Bs> template<class... Bn>
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::const_iterator( const_iterator::const_iterator(
std::tuple<Bs...> const& bs, bool at_end) std::tuple<Bn...> const& bn, bool at_end)
: bs_(&bs) : bn_(&bn)
{ {
if(at_end) if(at_end)
n_ = sizeof...(Bs); n_ = sizeof...(Bn);
else else
construct(C<0>{}); construct(C<0>{});
} }
template<class ValueType, class... Bs> template<class... Bn>
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::const_iterator(const_iterator&& other) const_iterator::const_iterator(const_iterator&& other)
: n_(other.n_) : n_(other.n_)
, bs_(other.bs_) , bn_(other.bn_)
{ {
move(C<0>{}, std::move(other)); move(std::move(other), C<0>{});
} }
template<class ValueType, class... Bs> template<class... Bn>
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::const_iterator(const_iterator const& other) const_iterator::const_iterator(const_iterator const& other)
: n_(other.n_) : n_(other.n_)
, bs_(other.bs_) , bn_(other.bn_)
{ {
copy(C<0>{}, other); copy(other, C<0>{});
} }
template<class ValueType, class... Bs> template<class... Bn>
auto auto
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::operator=(const_iterator&& other) -> const_iterator::operator=(const_iterator&& other) ->
const_iterator& const_iterator&
{ {
@@ -388,14 +391,14 @@ const_iterator::operator=(const_iterator&& other) ->
return *this; return *this;
destroy(C<0>{}); destroy(C<0>{});
n_ = other.n_; n_ = other.n_;
bs_ = other.bs_; bn_ = other.bn_;
move(C<0>{}, std::move(other)); move(std::move(other), C<0>{});
return *this; return *this;
} }
template<class ValueType, class... Bs> template<class... Bn>
auto auto
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::operator=(const_iterator const& other) -> const_iterator::operator=(const_iterator const& other) ->
const_iterator& const_iterator&
{ {
@@ -403,35 +406,35 @@ const_iterator&
return *this; return *this;
destroy(C<0>{}); destroy(C<0>{});
n_ = other.n_; n_ = other.n_;
bs_ = other.bs_; bn_ = other.bn_;
copy(C<0>{}, other); copy(other, C<0>{});
return *this; return *this;
} }
template<class ValueType, class... Bs> template<class... Bn>
bool bool
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::operator==(const_iterator const& other) const const_iterator::operator==(const_iterator const& other) const
{ {
if(bs_ != other.bs_) if(bn_ != other.bn_)
return false; return false;
if(n_ != other.n_) if(n_ != other.n_)
return false; return false;
return equal(C<0>{}, other); return equal(other, C<0>{});
} }
template<class ValueType, class... Bs> template<class... Bn>
auto auto
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::operator*() const -> const_iterator::operator*() const ->
reference reference
{ {
return dereference(C<0>{}); return dereference(C<0>{});
} }
template<class ValueType, class... Bs> template<class... Bn>
auto auto
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::operator++() -> const_iterator::operator++() ->
const_iterator& const_iterator&
{ {
@@ -439,30 +442,32 @@ const_iterator::operator++() ->
return *this; return *this;
} }
template<class ValueType, class... Bs> template<class... Bn>
auto auto
buffer_cat_helper<ValueType, Bs...>:: buffer_cat_helper<Bn...>::
const_iterator::operator--() -> const_iterator::operator--() ->
const_iterator& const_iterator&
{ {
decrement(C<sizeof...(Bs)>{}); decrement(C<sizeof...(Bn)>{});
return *this; return *this;
} }
template<class ValueType, class... Bs> template<class... Bn>
inline
auto auto
buffer_cat_helper<ValueType, Bs...>::begin() const -> buffer_cat_helper<Bn...>::begin() const ->
const_iterator const_iterator
{ {
return const_iterator(bs_, false); return const_iterator{bn_, false};
} }
template<class ValueType, class... Bs> template<class... Bn>
inline
auto auto
buffer_cat_helper<ValueType, Bs...>::end() const -> buffer_cat_helper<Bn...>::end() const ->
const_iterator const_iterator
{ {
return const_iterator(bs_, true); return const_iterator{bn_, true};
} }
} // detail } // detail

View File

@@ -85,6 +85,20 @@ public:
type3::value && type4::value>; type3::value && type4::value>;
}; };
template<class B1, class... Bn>
struct is_all_ConstBufferSequence
: std::integral_constant<bool,
is_BufferSequence<B1, boost::asio::const_buffer>::type::value &&
is_all_ConstBufferSequence<Bn...>::value>
{
};
template<class B1>
struct is_all_ConstBufferSequence<B1>
: is_BufferSequence<B1, boost::asio::const_buffer>::type
{
};
template<class T> template<class T>
class is_DynamicBuffer class is_DynamicBuffer
{ {

View File

@@ -5,8 +5,10 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// //
#ifndef BEAST_DETAIL_VOID_T_HPP #ifndef BEAST_DETAIL_TYPE_TRAITS_HPP
#define BEAST_DETAIL_VOID_T_HPP #define BEAST_DETAIL_TYPE_TRAITS_HPP
#include <type_traits>
namespace beast { namespace beast {
namespace detail { namespace detail {
@@ -33,6 +35,48 @@ void
ignore_unused() ignore_unused()
{} {}
template<class U>
std::size_t constexpr
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<unsigned N, class T, class... Tn>
struct repeat_tuple_impl
{
using type = typename repeat_tuple_impl<
N - 1, T, T, Tn...>::type;
};
template<class T, class... Tn>
struct repeat_tuple_impl<0, T, Tn...>
{
using type = std::tuple<T, Tn...>;
};
template<unsigned N, class T>
struct repeat_tuple
{
using type =
typename repeat_tuple_impl<N-1, T>::type;
};
template<class T>
struct repeat_tuple<0, T>
{
using type = std::tuple<>;
};
} // detail } // detail
} // beast } // beast

View File

@@ -123,8 +123,10 @@ template<class ConstBufferSequence>
#if GENERATING_DOCS #if GENERATING_DOCS
implementation_defined implementation_defined
#else #else
beast::detail::buffer_cat_helper<boost::asio::const_buffer, beast::detail::buffer_cat_helper<
chunk_encode_text, ConstBufferSequence, boost::asio::const_buffers_1> chunk_encode_text,
ConstBufferSequence,
boost::asio::const_buffers_1>
#endif #endif
chunk_encode(bool fin, ConstBufferSequence const& buffers) chunk_encode(bool fin, ConstBufferSequence const& buffers)
{ {

View File

@@ -13,6 +13,7 @@
#include <boost/asio/streambuf.hpp> #include <boost/asio/streambuf.hpp>
#include <iterator> #include <iterator>
#include <list> #include <list>
#include <type_traits>
#include <vector> #include <vector>
namespace beast { namespace beast {
@@ -20,14 +21,65 @@ namespace beast {
class buffer_cat_test : public unit_test::suite class buffer_cat_test : public unit_test::suite
{ {
public: public:
template< class Iterator > template<class Iterator>
static static
std::reverse_iterator<Iterator> std::reverse_iterator<Iterator>
make_reverse_iterator( Iterator i ) make_reverse_iterator(Iterator i)
{ {
return std::reverse_iterator<Iterator>(i); return std::reverse_iterator<Iterator>(i);
} }
template<class ConstBufferSequence>
static
std::size_t
bsize1(ConstBufferSequence const& bs)
{
using boost::asio::buffer_size;
std::size_t n = 0;
for(auto it = bs.begin(); it != bs.end(); ++it)
n += buffer_size(*it);
return n;
}
template<class ConstBufferSequence>
static
std::size_t
bsize2(ConstBufferSequence const& bs)
{
using boost::asio::buffer_size;
std::size_t n = 0;
for(auto it = bs.begin(); it != bs.end(); it++)
n += buffer_size(*it);
return n;
}
template<class ConstBufferSequence>
static
std::size_t
bsize3(ConstBufferSequence const& bs)
{
using boost::asio::buffer_size;
std::size_t n = 0;
for(auto it = bs.end(); it != bs.begin();)
n += buffer_size(*--it);
return n;
}
template<class ConstBufferSequence>
static
std::size_t
bsize4(ConstBufferSequence const& bs)
{
using boost::asio::buffer_size;
std::size_t n = 0;
for(auto it = bs.end(); it != bs.begin();)
{
it--;
n += buffer_size(*it);
}
return n;
}
void testBufferCat() void testBufferCat()
{ {
using boost::asio::buffer_size; using boost::asio::buffer_size;
@@ -48,6 +100,10 @@ public:
auto bs = buffer_cat( auto bs = buffer_cat(
b1, b2, b3, b4, b5, b6); b1, b2, b3, b4, b5, b6);
BEAST_EXPECT(buffer_size(bs) == 10); BEAST_EXPECT(buffer_size(bs) == 10);
BEAST_EXPECT(bsize1(bs) == 10);
BEAST_EXPECT(bsize2(bs) == 10);
BEAST_EXPECT(bsize3(bs) == 10);
BEAST_EXPECT(bsize4(bs) == 10);
std::vector<const_buffer> v; std::vector<const_buffer> v;
for(auto iter = make_reverse_iterator(bs.end()); for(auto iter = make_reverse_iterator(bs.end());
iter != make_reverse_iterator(bs.begin()); ++iter) iter != make_reverse_iterator(bs.begin()); ++iter)
@@ -55,8 +111,6 @@ public:
BEAST_EXPECT(buffer_size(bs) == 10); BEAST_EXPECT(buffer_size(bs) == 10);
decltype(bs) bs2(bs); decltype(bs) bs2(bs);
auto bs3(std::move(bs)); auto bs3(std::move(bs));
bs = bs2;
bs3 = std::move(bs2);
{ {
boost::asio::streambuf sb1, sb2; boost::asio::streambuf sb1, sb2;
BEAST_EXPECT(buffer_size(buffer_cat( BEAST_EXPECT(buffer_size(buffer_cat(
@@ -153,6 +207,60 @@ public:
void run() override void run() override
{ {
using boost::asio::const_buffer;
using boost::asio::const_buffers_1;
using boost::asio::mutable_buffer;
using boost::asio::mutable_buffers_1;
struct user_defined : mutable_buffer
{
};
// Check is_all_ConstBufferSequence
static_assert(
detail::is_all_ConstBufferSequence<
const_buffers_1
>::value, "");
static_assert(
detail::is_all_ConstBufferSequence<
const_buffers_1, const_buffers_1
>::value, "");
static_assert(
detail::is_all_ConstBufferSequence<
mutable_buffers_1
>::value, "");
static_assert(
detail::is_all_ConstBufferSequence<
mutable_buffers_1, mutable_buffers_1
>::value, "");
static_assert(
detail::is_all_ConstBufferSequence<
const_buffers_1, mutable_buffers_1
>::value, "");
static_assert(
! detail::is_all_ConstBufferSequence<
const_buffers_1, mutable_buffers_1, int
>::value, "");
// Ensure that concatenating mutable buffer
// sequences results in a mutable buffer sequence
static_assert(std::is_same<
mutable_buffer,
decltype(buffer_cat(
std::declval<mutable_buffer>(),
std::declval<user_defined>(),
std::declval<mutable_buffer>()
))::value_type>::value, "");
// Ensure that concatenating mixed buffer
// sequences results in a const buffer sequence.
static_assert(std::is_same<
const_buffer,
decltype(buffer_cat(
std::declval<mutable_buffer>(),
std::declval<user_defined>(),
std::declval<const_buffer>()
))::value_type>::value, "");
testBufferCat(); testBufferCat();
testIterators(); testIterators();
} }