Tidy up traits

fix #406
This commit is contained in:
Vinnie Falco
2017-06-03 15:36:56 -07:00
parent 4b158e45ba
commit 8a22350119
6 changed files with 445 additions and 144 deletions

View File

@@ -2,6 +2,7 @@ Version 48
* Make buffer_prefix_view public
* Remove detail::sync_ostream
* Tidy up core type traits
API Changes:

View File

@@ -16,6 +16,31 @@
#include <type_traits>
#include <string>
// A few workarounds to keep things working
namespace boost {
namespace asio {
// for has_get_io_service
class io_service;
// for is_dynamic_buffer
template<class Allocator>
class basic_streambuf;
namespace detail {
// for is_buffer_sequence
template<class Buffer, class Buffers>
class consuming_buffers;
} // detail
} // asio
} // boost
//------------------------------------------------------------------------------
namespace beast {
namespace detail {
@@ -32,6 +57,11 @@ struct make_void
template<class... Ts>
using void_t = typename make_void<Ts...>::type;
template<class T>
inline
void
accept_rv(T){}
template<class... Ts>
inline
void
@@ -109,8 +139,7 @@ is_invocable_test(C&& c, long, A&& ...a);
*/
/** @{ */
template<class C, class F>
struct is_invocable
: std::false_type
struct is_invocable : std::false_type
{
};
@@ -145,72 +174,57 @@ using ConstBufferSequence =
using MutableBufferSequence =
BufferSequence<boost::asio::mutable_buffer>;
template<class T, class BufferType>
class is_buffer_sequence
template<class T, class B, class = void>
struct is_buffer_sequence : std::false_type {};
template<class T, class B>
struct is_buffer_sequence<T, B, void_t<decltype(
std::declval<typename T::value_type>(),
std::declval<typename T::const_iterator&>() =
std::declval<T const&>().begin(),
std::declval<typename T::const_iterator&>() =
std::declval<T const&>().end(),
(void)0)>> : std::integral_constant<bool,
std::is_convertible<typename T::value_type, B>::value &&
#if 0
std::is_base_of<std::bidirectional_iterator_tag,
typename std::iterator_traits<
typename T::const_iterator>::iterator_category>::value
#else
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
std::is_base_of<std::forward_iterator_tag,
typename std::iterator_traits<
typename T::const_iterator>::iterator_category>::value
#endif
>
{
template<class U, class R = std::is_convertible<
typename U::value_type, BufferType> >
static R check1(int);
template<class>
static std::false_type check1(...);
using type1 = decltype(check1<T>(0));
template<class U, class R = std::is_base_of<
#if 0
std::bidirectional_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#else
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
std::forward_iterator_tag,
typename std::iterator_traits<
typename U::const_iterator>::iterator_category>>
#endif
static R check2(int);
template<class>
static std::false_type check2(...);
using type2 = decltype(check2<T>(0));
template<class U, class R = typename
std::is_convertible<decltype(
std::declval<U>().begin()),
typename U::const_iterator>::type>
static R check3(int);
template<class>
static std::false_type check3(...);
using type3 = decltype(check3<T>(0));
template<class U, class R = typename std::is_convertible<decltype(
std::declval<U>().end()),
typename U::const_iterator>::type>
static R check4(int);
template<class>
static std::false_type check4(...);
using type4 = decltype(check4<T>(0));
public:
using type = std::integral_constant<bool,
std::is_copy_constructible<T>::value &&
std::is_destructible<T>::value &&
type1::value && type2::value &&
type3::value && type4::value>;
};
//------------------------------------------------------------------------------
#if 0
// workaround:
// boost::asio::detail::consuming_buffers::const_iterator
// is not bidirectional
template<class Buffer, class Buffers, class B>
struct is_buffer_sequence<
boost::asio::detail::consuming_buffers<Buffer, Buffers>>
: std::true_type
{
};
#endif
template<class B1, class... Bn>
struct is_all_const_buffer_sequence
: std::integral_constant<bool,
is_buffer_sequence<B1, boost::asio::const_buffer>::type::value &&
is_buffer_sequence<B1, boost::asio::const_buffer>::value &&
is_all_const_buffer_sequence<Bn...>::value>
{
};
template<class B1>
struct is_all_const_buffer_sequence<B1>
: is_buffer_sequence<B1, boost::asio::const_buffer>::type
: is_buffer_sequence<B1, boost::asio::const_buffer>
{
};
@@ -345,14 +359,4 @@ public:
} // detail
} // beast
namespace boost {
namespace asio {
// for is_dynamic_buffer
template<class Allocator>
class basic_streambuf;
} // asio
} // boost
#endif

View File

@@ -21,31 +21,86 @@ namespace beast {
//
//------------------------------------------------------------------------------
/// Determine if `T` meets the requirements of @b ConstBufferSequence.
/** Determine if `T` meets the requirements of @b ConstBufferSequence.
@par Example
Use with `static_assert`:
@code
template<class ConstBufferSequence>
void f(ConstBufferSequence const& buffers)
{
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class ConstBufferSequence>
typename std::enable_if<is_const_buffer_sequence<ConstBufferSequence>::value>::type
f(ConstBufferSequence const& buffers);
@endcode
*/
template<class T>
#if BEAST_DOXYGEN
struct is_const_buffer_sequence : std::integral_constant<bool, ...>
#else
struct is_const_buffer_sequence :
detail::is_buffer_sequence<T,
boost::asio::const_buffer>::type
detail::is_buffer_sequence<T, boost::asio::const_buffer>
#endif
{
};
/// Determine if `T` meets the requirements of @b MutableBufferSequence.
/** Determine if `T` meets the requirements of @b MutableBufferSequence.
@par Example
Use with `static_assert`:
@code
template<class MutableBufferSequence>
void f(MutableBufferSequence const& buffers)
{
static_assert(is_const_buffer_sequence<MutableBufferSequence>::value,
"MutableBufferSequence requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class MutableBufferSequence>
typename std::enable_if<is_mutable_buffer_sequence<MutableBufferSequence>::value>::type
f(MutableBufferSequence const& buffers);
@endcode
*/
template<class T>
#if BEAST_DOXYGEN
struct is_mutable_buffer_sequence : std::integral_constant<bool, ...>
#else
struct is_mutable_buffer_sequence :
detail::is_buffer_sequence<T,
boost::asio::mutable_buffer>::type
detail::is_buffer_sequence<T, boost::asio::mutable_buffer>
#endif
{
};
/// Determine if `T` meets the requirements of @b DynamicBuffer.
/** Determine if `T` meets the requirements of @b DynamicBuffer.
@par Example
Use with `static_assert`:
@code
template<class DynamicBuffer>
void f(DynamicBuffer& buffer)
{
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class DynamicBuffer>
typename std::enable_if<is_dynamic_buffer<DynamicBuffer>::value>::type
f(DynamicBuffer const& buffer);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_dynamic_buffer : std::integral_constant<bool, ...> {};
@@ -54,8 +109,7 @@ template<class T, class = void>
struct is_dynamic_buffer : std::false_type {};
template<class T>
struct is_dynamic_buffer<T, beast::detail::void_t<
decltype(
struct is_dynamic_buffer<T, detail::void_t<decltype(
std::declval<std::size_t&>() =
std::declval<T const&>().size(),
std::declval<std::size_t&>() =
@@ -64,7 +118,7 @@ struct is_dynamic_buffer<T, beast::detail::void_t<
std::declval<T const&>().capacity(),
std::declval<T&>().commit(std::declval<std::size_t>()),
std::declval<T&>().consume(std::declval<std::size_t>()),
(void)0)>> : std::integral_constant<bool,
(void)0)> > : std::integral_constant<bool,
is_const_buffer_sequence<
typename T::const_buffers_type>::value &&
is_mutable_buffer_sequence<
@@ -93,32 +147,77 @@ struct is_dynamic_buffer<
//
//------------------------------------------------------------------------------
/// Determine if `T` meets the requirements of @b CompletionHandler.
/** Determine if `T` meets the requirements of @b CompletionHandler.
This metafunction will be equivalent to `std::true_type` if `T`
meets the requirements for a completion handler callable with
the given signature.
@par Example
@code
struct handler
{
void operator()(error_code&);
};
static_assert(is_completion_handler<handler, void(error_code&)>::value,
"Not a completion handler");
@endcode
*/
template<class T, class Signature>
#if BEAST_DOXYGEN
using is_completion_handler = std::integral_constant<bool, ...>;
#else
using is_completion_handler = std::integral_constant<bool,
std::is_copy_constructible<typename std::decay<T>::type>::value &&
detail::is_invocable<T, Signature>::value>;
detail::is_invocable<T, Signature>::value>;
#endif
//------------------------------------------------------------------------------
//
// Stream concepts
//
//------------------------------------------------------------------------------
/// Determine if `T` has the `get_io_service` member.
template<class T>
/** Determine if `T` has the `get_io_service` member.
@par Example
@code
struct stream
{
boost::asio::io_service& get_io_service();
};
static_assert(has_get_io_service<stream>::value,
"Missing get_io_service member");
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct has_get_io_service : std::integral_constant<bool, ...>{};
#else
using has_get_io_service = typename detail::has_get_io_service<T>::type;
template<class T, class = void>
struct has_get_io_service : std::false_type {};
template<class T>
struct has_get_io_service<T, beast::detail::void_t<decltype(
detail::accept_rv<boost::asio::io_service&>(
std::declval<T&>().get_io_service()),
(void)0)>> : std::true_type {};
#endif
/// Returns `T::lowest_layer_type` if it exists, else `T`
/** Returns `T::lowest_layer_type` if it exists, else `T`
@par Example
@code
template<class Stream>
struct stream_wrapper
{
using next_layer_type = typename std::remove_reference<Stream>::type;
using lowest_layer_type = typename get_lowest_layer<stream_type>::type;
};
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct get_lowest_layer;
@@ -137,52 +236,213 @@ struct get_lowest_layer<T, detail::void_t<
};
#endif
/// Determine if `T` meets the requirements of @b AsyncReadStream.
template<class T>
/** Determine if `T` meets the requirements of @b AsyncReadStream.
@par Example
Use with `static_assert`:
@code
template<class AsyncReadStream>
void f(AsyncReadStream& stream)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class AsyncReadStream>
typename std::enable_if<is_async_read_stream<AsyncReadStream>::value>::type
f(AsyncReadStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_async_read_stream : std::integral_constant<bool, ...>{};
#else
using is_async_read_stream = typename detail::is_async_read_stream<T>::type;
template<class T, class = void>
struct is_async_read_stream : std::false_type {};
template<class T>
struct is_async_read_stream<T, detail::void_t<decltype(
std::declval<T>().async_read_some(
std::declval<detail::MutableBufferSequence>(),
std::declval<detail::ReadHandler>()),
(void)0)>> : std::integral_constant<bool,
has_get_io_service<T>::value
> {};
#endif
/// Determine if `T` meets the requirements of @b AsyncWriteStream.
template<class T>
/** Determine if `T` meets the requirements of @b AsyncWriteStream.
@par Example
Use with `static_assert`:
@code
template<class AsyncWriteStream>
void f(AsyncWriteStream& stream)
{
static_assert(is_async_write_stream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class AsyncWriteStream>
typename std::enable_if<is_async_write_stream<AsyncWriteStream>::value>::type
f(AsyncWriteStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_async_write_stream : std::integral_constant<bool, ...>{};
#else
using is_async_write_stream = typename detail::is_async_write_stream<T>::type;
template<class T, class = void>
struct is_async_write_stream : std::false_type {};
template<class T>
struct is_async_write_stream<T, detail::void_t<decltype(
std::declval<T>().async_write_some(
std::declval<detail::ConstBufferSequence>(),
std::declval<detail::WriteHandler>()),
(void)0)>> : std::integral_constant<bool,
has_get_io_service<T>::value
> {};
#endif
/// Determine if `T` meets the requirements of @b SyncReadStream.
template<class T>
/** Determine if `T` meets the requirements of @b SyncReadStream.
@par Example
Use with `static_assert`:
@code
template<class SyncReadStream>
void f(SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class SyncReadStream>
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
f(SyncReadStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_sync_read_stream : std::integral_constant<bool, ...>{};
#else
using is_sync_read_stream = typename detail::is_sync_read_stream<T>::type;
template<class T, class = void>
struct is_sync_read_stream : std::false_type {};
template<class T>
struct is_sync_read_stream<T, detail::void_t<decltype(
std::declval<std::size_t&>() = std::declval<T>().read_some(
std::declval<detail::MutableBufferSequence>()),
std::declval<std::size_t&>() = std::declval<T>().read_some(
std::declval<detail::MutableBufferSequence>(),
std::declval<boost::system::error_code&>()),
(void)0)>> : std::integral_constant<bool,
has_get_io_service<T>::value
> {};
#endif
/// Determine if `T` meets the requirements of @b SyncWriterStream.
template<class T>
/** Determine if `T` meets the requirements of @b SyncWriterStream.
@par Example
Use with `static_assert`:
@code
template<class SyncReadStream>
void f(SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class SyncReadStream>
typename std::enable_if<is_sync_read_stream<SyncReadStream>::value>::type
f(SyncReadStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_sync_write_stream : std::integral_constant<bool, ...>{};
#else
using is_sync_write_stream = typename detail::is_sync_write_stream<T>::type;
template<class T, class = void>
struct is_sync_write_stream : std::false_type {};
template<class T>
struct is_sync_write_stream<T, detail::void_t<decltype(
std::declval<std::size_t&>() = std::declval<T&>().write_some(
std::declval<detail::ConstBufferSequence>()),
std::declval<std::size_t&>() = std::declval<T&>().write_some(
std::declval<detail::ConstBufferSequence>(),
std::declval<boost::system::error_code&>()),
(void)0)>> : std::integral_constant<bool,
has_get_io_service<T>::value
> {};
#endif
/// Determine if `T` meets the requirements of @b AsyncStream.
template<class T>
/** Determine if `T` meets the requirements of @b AsyncStream.
@par Example
Use with `static_assert`:
@code
template<class AsyncStream>
void f(AsyncStream& stream)
{
static_assert(is_async_stream<AsyncStream>::value,
"AsyncStream requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class AsyncStream>
typename std::enable_if<is_async_stream<AsyncStream>::value>::type
f(AsyncStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_async_stream : std::integral_constant<bool, ...>{};
#else
template<class T>
using is_async_stream = std::integral_constant<bool,
is_async_read_stream<T>::value && is_async_write_stream<T>::value>;
#endif
/// Determine if `T` meets the requirements of @b SyncStream.
template<class T>
/** Determine if `T` meets the requirements of @b SyncStream.
@par Example
Use with `static_assert`:
@code
template<class SyncStream>
void f(SyncStream& stream)
{
static_assert(is_sync_stream<SyncStream>::value,
"SyncStream requirements not met");
...
}
@endcode
Use with `std::enable_if`
@code
template<class SyncStream>
typename std::enable_if<is_sync_stream<SyncStream>::value>::type
f(SyncStream& stream);
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
struct is_sync_stream : std::integral_constant<bool, ...>{};
#else
template<class T>
using is_sync_stream = std::integral_constant<bool,
is_sync_read_stream<T>::value && is_sync_write_stream<T>::value>;
#endif

View File

@@ -16,6 +16,9 @@ namespace http {
template<bool isRequest, class Fields>
struct header;
template<bool, class, class>
struct message;
namespace detail {
template<class T>
@@ -32,6 +35,35 @@ public:
template<class T>
using is_header = typename is_header_impl<T>::type;
struct fields_model
{
string_view method() const;
string_view reason() const;
string_view target() const;
};
template<class T, class = beast::detail::void_t<>>
struct has_value_type : std::false_type {};
template<class T>
struct has_value_type<T, beast::detail::void_t<
typename T::value_type
> > : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct has_content_length : std::false_type {};
template<class T>
struct has_content_length<T, beast::detail::void_t<decltype(
std::declval<T>().content_length()
)> > : std::true_type
{
static_assert(std::is_convertible<
decltype(std::declval<T>().content_length()),
std::uint64_t>::value,
"Writer::content_length requirements not met");
};
} // detail
} // http
} // beast

View File

@@ -12,6 +12,7 @@
#include <beast/core/error.hpp>
#include <beast/core/string_view.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/http/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/optional.hpp>
#include <type_traits>
@@ -20,43 +21,23 @@
namespace beast {
namespace http {
template<bool, class, class>
struct message;
/** Determine if `T` meets the requirements of @b Body.
namespace detail {
This metafunction is equivalent to `std::true_type`
if `T` has a nested type named `value_type`.
struct fields_model
{
string_view method() const;
string_view reason() const;
string_view target() const;
};
@tparam T The body type to test.
template<class T, class = beast::detail::void_t<>>
struct has_value_type : std::false_type {};
template<class T>
struct has_value_type<T, beast::detail::void_t<
typename T::value_type
> > : std::true_type {};
template<class T, class = beast::detail::void_t<>>
struct has_content_length : std::false_type {};
template<class T>
struct has_content_length<T, beast::detail::void_t<decltype(
std::declval<T>().content_length()
)> > : std::true_type
{
static_assert(std::is_convertible<
decltype(std::declval<T>().content_length()),
std::uint64_t>::value,
"Writer::content_length requirements not met");
};
} // detail
/// Determine if `T` meets the requirements of @b Body.
@par Example
@code
template<bool isRequest, class Body, class Fields>
void check_body(message<isRequest, Body, Fields> const&)
{
static_assert(is_body<Body>::value,
"Body requirements not met");
}
@endcode
*/
template<class T>
#if BEAST_DOXYGEN
struct is_body : std::integral_constant<bool, ...>{};
@@ -68,11 +49,20 @@ using is_body = detail::has_value_type<T>;
This metafunction is equivalent to `std::true_type` if:
@li @b T has a nested type named `reader`
@li `T` has a nested type named `reader`
@li The nested type meets the requirements of @b BodyReader.
@tparam T The body type to test.
@par Example
@code
template<bool isRequest, class Body, class Fields>
void check_can_serialize(message<isRequest, Body, Fields> const&)
{
static_assert(is_body_reader<Body>::value,
"Cannot serialize Body, no reader");
@endcode
*/
#if BEAST_DOXYGEN
template<class T>
@@ -95,20 +85,27 @@ struct is_body_reader<T, beast::detail::void_t<
typename T::reader::const_buffers_type>::value &&
std::is_constructible<typename T::reader,
message<true, T, detail::fields_model> const& >::value
>
{
};
> {};
#endif
/** Determine if a @b Body type has a writer.
This metafunction is equivalent to `std::true_type` if:
@li @b T has a nested type named `writer`
@li `T` has a nested type named `writer`
@li The nested type meets the requirements of @b BodyWriter.
@tparam T The body type to test.
@par Example
@code
template<bool isRequest, class Body, class Fields>
void check_can_parse(message<isRequest, Body, Fields>&)
{
static_assert(is_body_writer<Body>::value,
"Cannot parse Body, no writer");
@endcode
*/
#if BEAST_DOXYGEN
template<class T>

View File

@@ -10,6 +10,7 @@
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/asio/detail/consuming_buffers.hpp>
namespace beast {
@@ -125,10 +126,16 @@ BOOST_STATIC_ASSERT(! is_completion_handler<H, void(void)>::value);
// stream concepts
//
//namespace {
namespace {
using stream_type = boost::asio::ip::tcp::socket;
struct not_a_stream
{
void
get_io_service();
};
BOOST_STATIC_ASSERT(has_get_io_service<stream_type>::value);
BOOST_STATIC_ASSERT(is_async_read_stream<stream_type>::value);
BOOST_STATIC_ASSERT(is_async_write_stream<stream_type>::value);
@@ -137,12 +144,12 @@ BOOST_STATIC_ASSERT(is_sync_read_stream<stream_type>::value);
BOOST_STATIC_ASSERT(is_sync_write_stream<stream_type>::value);
BOOST_STATIC_ASSERT(is_sync_stream<stream_type>::value);
BOOST_STATIC_ASSERT(! has_get_io_service<int>::value);
BOOST_STATIC_ASSERT(! is_async_read_stream<int>::value);
BOOST_STATIC_ASSERT(! is_async_write_stream<int>::value);
BOOST_STATIC_ASSERT(! is_sync_read_stream<int>::value);
BOOST_STATIC_ASSERT(! is_sync_write_stream<int>::value);
BOOST_STATIC_ASSERT(! has_get_io_service<not_a_stream>::value);
BOOST_STATIC_ASSERT(! is_async_read_stream<not_a_stream>::value);
BOOST_STATIC_ASSERT(! is_async_write_stream<not_a_stream>::value);
BOOST_STATIC_ASSERT(! is_sync_read_stream<not_a_stream>::value);
BOOST_STATIC_ASSERT(! is_sync_write_stream<not_a_stream>::value);
//} // (anonymous)
} // (anonymous)
} // beast