From db59a920112bd111188619591e51f3d9ce0a9956 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 7 Jul 2017 14:32:51 -0700 Subject: [PATCH] BodyReader may construct from a non-const message fix #619 --- CHANGELOG.md | 1 + doc/concept/BodyReader.qbk | 13 +++++- include/beast/http/impl/serializer.ipp | 13 +++++- include/beast/http/serializer.hpp | 56 +++++++++++++++++++++---- include/beast/http/type_traits.hpp | 4 +- test/http/serializer.cpp | 57 ++++++++++++++++++++++++++ 6 files changed, 132 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da4c4468..b6c16337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Version 76: * Always go through write_some * Use Boost.Config +* BodyReader may construct from a non-const message -------------------------------------------------------------------------------- diff --git a/doc/concept/BodyReader.qbk b/doc/concept/BodyReader.qbk index 693fab35..4ede1415 100644 --- a/doc/concept/BodyReader.qbk +++ b/doc/concept/BodyReader.qbk @@ -29,7 +29,7 @@ In this table: * `a` denotes a value of type `X`. -* `m` denotes a value of type `message const&` where +* `m` denotes a possibly const value of type `message&` where `std::is_same:value == true`. * `ec` is a value of type [link beast.ref.beast__error_code `error_code&`]. @@ -51,6 +51,17 @@ In this table: [ Constructible from `m`. The lifetime of `m` is guaranteed to end no earlier than after the `X` is destroyed. + The constructor may optionally require that `m` is const, which + has these consequences: + + * If `X` requires that `m` is a const reference, then serializers + constructed for messages with this body type will also require a + `const` reference to a message, otherwise: + + * If `X` requires that `m` is a non-const reference, then serializers + constructed for messages with this body type will aso require + a non-const reference to a message. + The function will ensure that `!ec` is `true` if there was no error or set to the appropriate error code if there was one. ] diff --git a/include/beast/http/impl/serializer.ipp b/include/beast/http/impl/serializer.ipp index 4173b2c6..689bd23c 100644 --- a/include/beast/http/impl/serializer.ipp +++ b/include/beast/http/impl/serializer.ipp @@ -52,11 +52,20 @@ do_visit(error_code& ec, Visit& visit) boost::get(pv_))); } +//------------------------------------------------------------------------------ + template serializer:: -serializer(message const& m, - ChunkDecorator const& d) +serializer(value_type& m) + : m_(m) +{ +} + +template +serializer:: +serializer(value_type& m, ChunkDecorator const& d) : m_(m) , d_(d) { diff --git a/include/beast/http/serializer.hpp b/include/beast/http/serializer.hpp index b37a829d..5f679c06 100644 --- a/include/beast/http/serializer.hpp +++ b/include/beast/http/serializer.hpp @@ -127,12 +127,34 @@ template< class ChunkDecorator = no_chunk_decorator> class serializer { +public: static_assert(is_body::value, "Body requirements not met"); - + static_assert(is_body_reader::value, "BodyReader requirements not met"); + /** The type of the message referenced by this object. + + This may be const or non-const depending on the + implementation of the corresponding @b BodyReader. + */ +#if BEAST_DOXYGEN + using value_type = implementation_defined; +#else + using value_type = + typename std::conditional< + std::is_constructible&, + error_code&>::value && + ! std::is_constructible const&, + error_code&>::value, + message, + message const>::type; +#endif + +private: enum { do_construct = 0, @@ -225,7 +247,7 @@ class serializer boost::asio::const_buffers_1>>; // crlf using pcb8_t = buffer_prefix_view; - message const& m_; + value_type& m_; boost::optional frd_; boost::optional rd_; boost::variant const& msg, - ChunkDecorator const& decorator = ChunkDecorator{}); + serializer(value_type& msg); + + /** Constructor + + The implementation guarantees that the message passed on + construction will not be accessed until the first call to + @ref next. This allows the message to be lazily created. + For example, if the header is filled in before serialization. + + @param msg A reference to the message to serialize, which must + remain valid for the lifetime of the serializer. Depending on + the type of Body used, this may or may not be a `const` reference. + + @param decorator The decorator to use. + + @note This function participates in overload resolution only if + Body::reader is constructible from a `const` message reference. + */ + explicit + serializer(value_type& msg, ChunkDecorator const& decorator); /// Returns the serialized buffer size limit std::size_t diff --git a/include/beast/http/type_traits.hpp b/include/beast/http/type_traits.hpp index e73817a8..6a4dd5c6 100644 --- a/include/beast/http/type_traits.hpp +++ b/include/beast/http/type_traits.hpp @@ -87,10 +87,10 @@ struct is_body_reader::value && std::is_constructible const&, + message&, error_code&>::value && std::is_constructible const&, + message&, error_code&>::value > {}; #endif diff --git a/test/http/serializer.cpp b/test/http/serializer.cpp index 022a09cd..20826307 100644 --- a/test/http/serializer.cpp +++ b/test/http/serializer.cpp @@ -17,6 +17,63 @@ namespace http { class serializer_test : public beast::unit_test::suite { public: + struct const_body + { + struct value_type{}; + + struct reader + { + using const_buffers_type = + boost::asio::const_buffers_1; + + template + reader(message const&, + error_code&); + + boost::optional> + get(error_code&); + }; + }; + + struct mutable_body + { + struct value_type{}; + + struct reader + { + using const_buffers_type = + boost::asio::const_buffers_1; + + template + reader(message&, + error_code&); + + boost::optional> + get(error_code&); + }; + }; + + BOOST_STATIC_ASSERT(std::is_const< serializer< + true, const_body>::value_type>::value); + BOOST_STATIC_ASSERT(! std::is_const::value_type>::value); + + BOOST_STATIC_ASSERT(std::is_constructible< + serializer, + message &>::value); + + BOOST_STATIC_ASSERT(std::is_constructible< + serializer, + message const&>::value); + + BOOST_STATIC_ASSERT(std::is_constructible< + serializer, + message &>::value); + + BOOST_STATIC_ASSERT(! std::is_constructible< + serializer, + message const&>::value); + struct lambda { std::size_t size;