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();
}