diff --git a/CHANGELOG.md b/CHANGELOG.md index f11864d6..1bac2016 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,20 @@ * Add basic_streambuf::alloc_size * Parser callbacks may not throw * Fix Reader concept doc typo +* Add is_Reader trait API Changes: * Added init() to Reader requirements * Reader must be nothrow constructible * Reader is now constructed right before reading the body - - The message passed on construction is filled in + - The message passed on construction is filled in +* Rework HTTP concepts: + - Writer uses write instead of operator() + - Refactor traits to use void_t + - Remove is_ReadableBody, is_WritableBody + - Add has_reader, has_writer, is_Reader, is_Writer + - More friendly compile errors on failed concept checks -------------------------------------------------------------------------------- diff --git a/doc/quickref.xml b/doc/quickref.xml index b9c5c647..19ab73c6 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -50,8 +50,10 @@ is_Body is_Parser - is_ReadableBody - is_WritableBody + is_Reader + is_Reader + has_reader + has_writer diff --git a/doc/types/Writer.qbk b/doc/types/Writer.qbk index 48c91aee..32ea7227 100644 --- a/doc/types/Writer.qbk +++ b/doc/types/Writer.qbk @@ -72,7 +72,7 @@ In this table: ] ] [ - [`a(rc, ec, wf)`] + [`a.write(rc, ec, wf)`] [`boost::tribool`] [ Called repeatedly after `init` succeeds. `wf` is a function object @@ -175,10 +175,10 @@ public: */ template boost::tribool - operator()( + write( resume_context&&, error_code&, - WriteFunction&& write) noexcept; + WriteFunction&& wf) noexcept; }; ``` diff --git a/examples/file_body.hpp b/examples/file_body.hpp index 6df8dc86..8e7bb55b 100644 --- a/examples/file_body.hpp +++ b/examples/file_body.hpp @@ -58,14 +58,15 @@ struct file_body } std::uint64_t - content_length() const + content_length() const noexcept { return size_; } - template + template boost::tribool - operator()(resume_context&&, error_code&, Write&& write) + write(resume_context&&, error_code&, + WriteFunction&& wf) noexcept { if(size_ - offset_ < sizeof(buf_)) buf_len_ = static_cast( @@ -75,7 +76,7 @@ struct file_body auto const nread = fread(buf_, 1, sizeof(buf_), file_); (void)nread; offset_ += buf_len_; - write(boost::asio::buffer(buf_, buf_len_)); + wf(boost::asio::buffer(buf_, buf_len_)); return offset_ >= size_; } }; diff --git a/include/beast/core/detail/type_traits.hpp b/include/beast/core/detail/type_traits.hpp new file mode 100644 index 00000000..3944e514 --- /dev/null +++ b/include/beast/core/detail/type_traits.hpp @@ -0,0 +1,26 @@ +// +// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_DETAIL_VOID_T_HPP +#define BEAST_DETAIL_VOID_T_HPP + +namespace beast { +namespace detail { + +template +struct make_void +{ + using type = void; +}; + +template +using void_t = typename make_void::type; + +} // detail +} // beast + +#endif diff --git a/include/beast/http/basic_dynabuf_body.hpp b/include/beast/http/basic_dynabuf_body.hpp index 3a46aed0..f39defc7 100644 --- a/include/beast/http/basic_dynabuf_body.hpp +++ b/include/beast/http/basic_dynabuf_body.hpp @@ -62,33 +62,31 @@ private: DynamicBuffer const& body_; public: - writer(writer const&) = delete; - writer& operator=(writer const&) = delete; - template explicit writer(message< - isRequest, basic_dynabuf_body, Headers> const& m) + isRequest, basic_dynabuf_body, Headers> const& m) noexcept : body_(m.body) { } void - init(error_code& ec) + init(error_code& ec) noexcept { } std::uint64_t - content_length() const + content_length() const noexcept { return body_.size(); } - template + template boost::tribool - operator()(resume_context&&, error_code&, Write&& write) + write(resume_context&&, error_code&, + WriteFunction&& wf) noexcept { - write(body_.data()); + wf(body_.data()); return true; } }; diff --git a/include/beast/http/concepts.hpp b/include/beast/http/concepts.hpp index fc457a3f..d490196d 100644 --- a/include/beast/http/concepts.hpp +++ b/include/beast/http/concepts.hpp @@ -9,7 +9,10 @@ #define BEAST_HTTP_TYPE_CHECK_HPP #include +#include +#include #include +#include #include #include @@ -18,63 +21,107 @@ namespace http { namespace detail { -template -class has_value_type +struct write_function { - template - static std::true_type check(int); + template + void + operator()(ConstBufferSequence const&); +}; + +template> +struct has_value_type : std::false_type {}; + +template +struct has_value_type > : std::true_type {}; + +template> +struct has_content_length : std::false_type {}; + +template +struct has_content_length().content_length() + )> > : std::true_type +{ + static_assert(std::is_convertible< + decltype(std::declval().content_length()), + std::uint64_t>::value, + "Writer::content_length requirements not met"); +}; + +#if 0 +template> +struct is_Writer : std::false_type {}; + +template +struct is_Writer().init( + std::declval()) + // VFALCO This is unfortunate, we have to provide the template + // argument type because this is not a deduced context? + // + ,std::declval().template write( + std::declval(), + std::declval(), + std::declval()) + )> > : std::integral_constant::value && + std::is_convertible().template write( + std::declval(), + std::declval(), + std::declval())), + boost::tribool>::value + > +{ + static_assert(std::is_same< + typename M::body_type::writer, T>::value, + "Mismatched writer and message"); +}; + +#else + +template +class is_Writer +{ + template().init(std::declval()), + std::true_type{})> + static R check1(int); template - static std::false_type check(...); - using type = decltype(check(0)); -public: - static bool constexpr value = type::value; -}; + static std::false_type check1(...); + using type1 = decltype(check1(0)); -template::value> -struct extract_value_type -{ - using type = void; -}; - -template -struct extract_value_type -{ - using type = typename T::value_type; -}; - -template -class has_reader -{ + // VFALCO This is unfortunate, we have to provide the template + // argument type because this is not a deduced context? + // template - static std::true_type check(int); + std::is_convertible().template write( + std::declval(), + std::declval(), + std::declval())) + , boost::tribool>> + static R check2(int); template - static std::false_type check(...); -public: - using type = decltype(check(0)); -}; + static std::false_type check2(...); + using type2 = decltype(check2(0)); -template -class has_writer -{ - template - static std::true_type check(int); - template - static std::false_type check(...); public: - using type = decltype(check(0)); -}; + static_assert(std::is_same< + typename M::body_type::writer, T>::value, + "Mismatched writer and message"); -template -struct is_Body -{ using type = std::integral_constant::value + std::is_nothrow_constructible::value + && type1::value + && type2::value >; }; +#endif + template class is_Parser { @@ -116,31 +163,97 @@ public: } // detail -/// Determine if `T` meets the requirements of `Body`. +/// Determine if `T` meets the requirements of @b Body. template #if GENERATING_DOCS struct is_Body : std::integral_constant{}; #else -using is_Body = typename detail::is_Body::type; +using is_Body = detail::has_value_type; #endif -/// Determine if `T` meets the requirements of `ReadableBody`. -template +/** Determine if a @ref Body has a nested type `reader`. + + @tparam T The type to check, which must meet the + requirements of @b Body. +*/ #if GENERATING_DOCS -struct is_ReadableBody : std::integral_constant{}; -#else -using is_ReadableBody = typename detail::has_reader::type; -#endif - -/// Determine if `T` meets the requirements of `WritableBody`. template -#if GENERATING_DOCS -struct is_WritableBody : std::integral_constant{}; +struct has_reader : std::integral_constant{}; #else -using is_WritableBody = typename detail::has_writer::type; +template> +struct has_reader : std::false_type {}; + +template +struct has_reader > : std::true_type {}; #endif -/// Determine if `T` meets the requirements of `Parser`. +/** Determine if a @ref Body has a nested type `writer`. + + @tparam T The type to check, which must meet the + requirements of @b Body. +*/ +#if GENERATING_DOCS +template +struct has_writer : std::integral_constant{}; +#else +template> +struct has_writer : std::false_type {}; + +template +struct has_writer > : std::true_type {}; +#endif + +/** Determine if `T` meets the requirements of @b Reader for `M`. + + @tparam T The type to test. + + @tparam M The message type to test with, which must be of + type `message`. +*/ +#if GENERATING_DOCS +template +struct is_Reader : std::integral_constant {}; +#else +template> +struct is_Reader : std::false_type {}; + +template +struct is_Reader().init( + std::declval()), + std::declval().write( + std::declval(), + std::declval(), + std::declval()) + )> > : std::integral_constant::value + > +{ + static_assert(std::is_same< + typename M::body_type::reader, T>::value, + "Mismatched reader and message"); +}; +#endif + +/** Determine if `T` meets the requirements of @b Writer for `M`. + + @tparam T The type to test. + + @tparam M The message type to test with, which must be of + type `message`. +*/ +template +#if GENERATING_DOCS +struct is_Writer : std::integral_constant {}; +#else +using is_Writer = typename detail::is_Writer::type; +#endif + +/// Determine if `T` meets the requirements of @b Parser. template #if GENERATING_DOCS struct is_Parser : std::integral_constant{}; diff --git a/include/beast/http/detail/has_content_length.hpp b/include/beast/http/detail/has_content_length.hpp deleted file mode 100644 index 09584a5e..00000000 --- a/include/beast/http/detail/has_content_length.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// - -#ifndef BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP -#define BEAST_HTTP_DETAIL_HAS_CONTENT_LENGTH_HPP - -#include -#include - -namespace beast { -namespace http { -namespace detail { - -template -class has_content_length_value -{ - template().content_length()), - std::uint64_t>> - static R check(int); - template - static std::false_type check(...); - using type = decltype(check(0)); -public: - // `true` if `T` meets the requirements. - static bool const value = type::value; -}; - -// Determines if the writer can provide the content length -template -using has_content_length = - std::integral_constant::value>; - -} // detail -} // http -} // beast - -#endif diff --git a/include/beast/http/empty_body.hpp b/include/beast/http/empty_body.hpp index 152a71fe..1c1d45a7 100644 --- a/include/beast/http/empty_body.hpp +++ b/include/beast/http/empty_body.hpp @@ -35,31 +35,29 @@ private: struct writer { - writer(writer const&) = delete; - writer& operator=(writer const&) = delete; - template explicit - writer(message const& m) + writer(message const& m) noexcept { } void - init(error_code& ec) + init(error_code& ec) noexcept { } std::uint64_t - content_length() const + content_length() const noexcept { return 0; } - template + template boost::tribool - operator()(resume_context&&, error_code&, Write&& write) + write(resume_context&&, error_code&, + WriteFunction&& wf) noexcept { - write(boost::asio::null_buffers{}); + wf(boost::asio::null_buffers{}); return true; } }; diff --git a/include/beast/http/impl/message_v1.ipp b/include/beast/http/impl/message_v1.ipp index dafe19d6..2cc21203 100644 --- a/include/beast/http/impl/message_v1.ipp +++ b/include/beast/http/impl/message_v1.ipp @@ -9,9 +9,9 @@ #define BEAST_HTTP_IMPL_MESSAGE_V1_IPP #include +#include #include #include -#include #include #include @@ -116,8 +116,13 @@ prepare(message_v1& msg, Options&&... options) { // VFALCO TODO - //static_assert(is_WritableBody::value, - // "WritableBody requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_writer::value, + "Body has no writer"); + static_assert(is_Writer>::value, + "Writer requirements not met"); detail::prepare_info pi; detail::prepare_content_length(pi, msg, detail::has_content_length{}); diff --git a/include/beast/http/impl/read.ipp b/include/beast/http/impl/read.ipp index 1c3c6bb3..7d5d1c65 100644 --- a/include/beast/http/impl/read.ipp +++ b/include/beast/http/impl/read.ipp @@ -150,8 +150,13 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf, "SyncReadStream requirements not met"); static_assert(is_DynamicBuffer::value, "DynamicBuffer requirements not met"); - static_assert(is_ReadableBody::value, - "ReadableBody requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_reader::value, + "Body has no reader"); + static_assert(is_Reader>::value, + "Reader requirements not met"); error_code ec; beast::http::read(stream, dynabuf, msg, ec); if(ec) @@ -169,8 +174,13 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf, "SyncReadStream requirements not met"); static_assert(is_DynamicBuffer::value, "DynamicBuffer requirements not met"); - static_assert(is_ReadableBody::value, - "ReadableBody requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_reader::value, + "Body has no reader"); + static_assert(is_Reader>::value, + "Reader requirements not met"); parser_v1 p; beast::http::parse(stream, dynabuf, p, ec); if(ec) @@ -192,8 +202,13 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf, "AsyncReadStream requirements not met"); static_assert(is_DynamicBuffer::value, "DynamicBuffer requirements not met"); - static_assert(is_ReadableBody::value, - "ReadableBody requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_reader::value, + "Body has no reader"); + static_assert(is_Reader>::value, + "Reader requirements not met"); beast::async_completion completion(handler); detail::read_op #include #include -#include #include #include #include @@ -184,7 +183,7 @@ class write_op } template - void operator()(ConstBufferSequence const& buffers) + void operator()(ConstBufferSequence const& buffers) const { auto& d = *self_.d_; // write headers and body @@ -212,7 +211,7 @@ class write_op } template - void operator()(ConstBufferSequence const& buffers) + void operator()(ConstBufferSequence const& buffers) const { auto& d = *self_.d_; // write body @@ -323,7 +322,7 @@ operator()(error_code ec, std::size_t, bool again) case 1: { - auto const result = d.wp.w( + boost::tribool const result = d.wp.w.write( std::move(d.copy), ec, writef0_lambda{*this}); if(ec) { @@ -354,7 +353,7 @@ operator()(error_code ec, std::size_t, bool again) case 3: { - auto const result = d.wp.w( + boost::tribool result = d.wp.w.write( std::move(d.copy), ec, writef_lambda{*this}); if(ec) { @@ -420,7 +419,7 @@ public: } template - void operator()(ConstBufferSequence const& buffers) + void operator()(ConstBufferSequence const& buffers) const { // write headers and body if(chunked_) @@ -449,7 +448,7 @@ public: } template - void operator()(ConstBufferSequence const& buffers) + void operator()(ConstBufferSequence const& buffers) const { // write body if(chunked_) @@ -472,8 +471,13 @@ write(SyncWriteStream& stream, { static_assert(is_SyncWriteStream::value, "SyncWriteStream requirements not met"); - static_assert(is_WritableBody::value, - "WritableBody requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_writer::value, + "Body has no writer"); + static_assert(is_Writer>::value, + "Writer requirements not met"); error_code ec; write(stream, msg, ec); if(ec) @@ -489,8 +493,13 @@ write(SyncWriteStream& stream, { static_assert(is_SyncWriteStream::value, "SyncWriteStream requirements not met"); - static_assert(is_WritableBody::value, - "WritableBody requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_writer::value, + "Body has no writer"); + static_assert(is_Writer>::value, + "Writer requirements not met"); detail::write_preparation wp(msg); wp.init(ec); if(ec) @@ -506,9 +515,11 @@ write(SyncWriteStream& stream, cv.notify_one(); }}; auto copy = resume; - boost::tribool result = wp.w(std::move(copy), - ec, detail::writef0_lambda{stream, wp.sb, wp.chunked, ec}); + boost::tribool result = + wp.w.write(std::move(copy), ec, + detail::writef0_lambda{stream, + wp.sb, wp.chunked, ec}); if(ec) return; if(boost::indeterminate(result)) @@ -527,11 +538,16 @@ write(SyncWriteStream& stream, wp.sb.consume(wp.sb.size()); if(! result) { + detail::writef_lambda wf{ + stream, wp.chunked, ec}; for(;;) { - result = wp.w(std::move(copy), ec, - detail::writef_lambda{ - stream, wp.chunked, ec}); +#if 0 + result = wp.w.write(std::move(copy), ec, wf); +#else + result = wp.w.write(std::move(copy), ec, + detail::writef_lambda{stream, wp.chunked, ec}); +#endif if(ec) return; if(result) @@ -573,8 +589,13 @@ async_write(AsyncWriteStream& stream, { static_assert(is_AsyncWriteStream::value, "AsyncWriteStream requirements not met"); - static_assert(is_WritableBody::value, - "WritableBody requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_writer::value, + "Body has no writer"); + static_assert(is_Writer>::value, + "Writer requirements not met"); beast::async_completion completion(handler); detail::write_op const& msg) { - static_assert(is_WritableBody::value, - "WritableBody requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_writer::value, + "Body has no writer"); + static_assert(is_Writer>::value, + "Writer requirements not met"); detail::ostream_SyncStream oss(os); error_code ec; write(oss, msg, ec); diff --git a/include/beast/http/parser_v1.hpp b/include/beast/http/parser_v1.hpp index dc1f4f99..ea610b3b 100644 --- a/include/beast/http/parser_v1.hpp +++ b/include/beast/http/parser_v1.hpp @@ -91,13 +91,12 @@ private: using reader = typename message_type::body_type::reader; - static_assert(is_ReadableBody::value, - "ReadableBody requirements not met"); - - // Reader must be nothrow constructible - static_assert(std::is_nothrow_constructible< - reader, message_type&>::value, - "Reader requirements not met"); + static_assert(is_Body::value, + "Body requirements not met"); + static_assert(has_reader::value, + "Body has no reader"); + static_assert(is_Reader::value, + "Reader requirements not met"); std::string field_; std::string value_; diff --git a/include/beast/http/string_body.hpp b/include/beast/http/string_body.hpp index 1148c67f..25790487 100644 --- a/include/beast/http/string_body.hpp +++ b/include/beast/http/string_body.hpp @@ -62,33 +62,31 @@ private: value_type const& body_; public: - writer(writer const&) = delete; - writer& operator=(writer const&) = delete; - template explicit writer(message< - isRequest, string_body, Headers> const& msg) + isRequest, string_body, Headers> const& msg) noexcept : body_(msg.body) { } void - init(error_code& ec) + init(error_code& ec) noexcept { } std::uint64_t - content_length() const + content_length() const noexcept { return body_.size(); } - template + template boost::tribool - operator()(resume_context&&, error_code&, Write&& write) + write(resume_context&&, error_code&, + WriteFunction&& wf) noexcept { - write(boost::asio::buffer(body_)); + wf(boost::asio::buffer(body_)); return true; } }; diff --git a/test/http/write.cpp b/test/http/write.cpp index f08c6b62..e1041703 100644 --- a/test/http/write.cpp +++ b/test/http/write.cpp @@ -104,21 +104,22 @@ public: public: template explicit - writer(message const& msg) + writer(message const& msg) noexcept : body_(msg.body) { } void - init(error_code& ec) + init(error_code& ec) noexcept { } - template + template boost::tribool - operator()(resume_context&&, error_code&, Write&& write) + write(resume_context&&, error_code&, + WriteFunction&& wf) noexcept { - write(boost::asio::buffer(body_)); + wf(boost::asio::buffer(body_)); return true; } }; @@ -168,13 +169,13 @@ public: public: template explicit - writer(message const& msg) + writer(message const& msg) noexcept : body_(msg.body) { } void - init(error_code& ec) + init(error_code& ec) noexcept { body_.fc_.fail(ec); } @@ -197,9 +198,10 @@ public: } }; - template + template boost::tribool - operator()(resume_context&& rc, error_code& ec, Write&& write) + write(resume_context&& rc, error_code& ec, + WriteFunction&& wf) noexcept { if(body_.fc_.fail(ec)) return false; @@ -211,7 +213,7 @@ public: } if(n_ >= body_.s_.size()) return true; - write(boost::asio::buffer(body_.s_.data() + n_, 1)); + wf(boost::asio::buffer(body_.s_.data() + n_, 1)); ++n_; return n_ == body_.s_.size(); }