From 8a2235011962ac9eb7e2794053e4081611cf729e Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 3 Jun 2017 15:36:56 -0700 Subject: [PATCH] Tidy up traits fix #406 --- CHANGELOG.md | 1 + include/beast/core/detail/type_traits.hpp | 134 ++++----- include/beast/core/type_traits.hpp | 326 +++++++++++++++++++--- include/beast/http/detail/type_traits.hpp | 32 +++ include/beast/http/type_traits.hpp | 75 +++-- test/core/type_traits.cpp | 21 +- 6 files changed, 445 insertions(+), 144 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 234a73c3..deebdcd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Version 48 * Make buffer_prefix_view public * Remove detail::sync_ostream +* Tidy up core type traits API Changes: diff --git a/include/beast/core/detail/type_traits.hpp b/include/beast/core/detail/type_traits.hpp index 13511848..065e3a00 100644 --- a/include/beast/core/detail/type_traits.hpp +++ b/include/beast/core/detail/type_traits.hpp @@ -16,6 +16,31 @@ #include #include +// 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 basic_streambuf; + +namespace detail { + +// for is_buffer_sequence +template +class consuming_buffers; + +} // detail + +} // asio +} // boost + +//------------------------------------------------------------------------------ + namespace beast { namespace detail { @@ -32,6 +57,11 @@ struct make_void template using void_t = typename make_void::type; +template +inline +void +accept_rv(T){} + template inline void @@ -109,8 +139,7 @@ is_invocable_test(C&& c, long, A&& ...a); */ /** @{ */ template -struct is_invocable - : std::false_type +struct is_invocable : std::false_type { }; @@ -145,72 +174,57 @@ using ConstBufferSequence = using MutableBufferSequence = BufferSequence; -template -class is_buffer_sequence +template +struct is_buffer_sequence : std::false_type {}; + +template +struct is_buffer_sequence(), + std::declval() = + std::declval().begin(), + std::declval() = + std::declval().end(), + (void)0)>> : std::integral_constant::value && +#if 0 + std::is_base_of::iterator_category>::value +#else + // workaround: + // boost::asio::detail::consuming_buffers::const_iterator + // is not bidirectional + std::is_base_of::iterator_category>::value +#endif + > { - template > - static R check1(int); - template - static std::false_type check1(...); - using type1 = decltype(check1(0)); - - template::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 - static std::false_type check2(...); - using type2 = decltype(check2(0)); - - template().begin()), - typename U::const_iterator>::type> - static R check3(int); - template - static std::false_type check3(...); - using type3 = decltype(check3(0)); - - template().end()), - typename U::const_iterator>::type> - static R check4(int); - template - static std::false_type check4(...); - using type4 = decltype(check4(0)); - -public: - using type = std::integral_constant::value && - std::is_destructible::value && - type1::value && type2::value && - type3::value && type4::value>; }; -//------------------------------------------------------------------------------ +#if 0 +// workaround: +// boost::asio::detail::consuming_buffers::const_iterator +// is not bidirectional +template +struct is_buffer_sequence< + boost::asio::detail::consuming_buffers> + : std::true_type +{ +}; +#endif template struct is_all_const_buffer_sequence : std::integral_constant::type::value && + is_buffer_sequence::value && is_all_const_buffer_sequence::value> { }; template struct is_all_const_buffer_sequence - : is_buffer_sequence::type + : is_buffer_sequence { }; @@ -345,14 +359,4 @@ public: } // detail } // beast -namespace boost { -namespace asio { - -// for is_dynamic_buffer -template -class basic_streambuf; - -} // asio -} // boost - #endif diff --git a/include/beast/core/type_traits.hpp b/include/beast/core/type_traits.hpp index 87ff89b8..4596c260 100644 --- a/include/beast/core/type_traits.hpp +++ b/include/beast/core/type_traits.hpp @@ -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 + void f(ConstBufferSequence const& buffers) + { + static_assert(is_const_buffer_sequence::value, + "ConstBufferSequence requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(ConstBufferSequence const& buffers); + @endcode +*/ template #if BEAST_DOXYGEN struct is_const_buffer_sequence : std::integral_constant #else struct is_const_buffer_sequence : - detail::is_buffer_sequence::type + detail::is_buffer_sequence #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 + void f(MutableBufferSequence const& buffers) + { + static_assert(is_const_buffer_sequence::value, + "MutableBufferSequence requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(MutableBufferSequence const& buffers); + @endcode +*/ template #if BEAST_DOXYGEN struct is_mutable_buffer_sequence : std::integral_constant #else struct is_mutable_buffer_sequence : - detail::is_buffer_sequence::type + detail::is_buffer_sequence #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 + void f(DynamicBuffer& buffer) + { + static_assert(is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(DynamicBuffer const& buffer); + @endcode +*/ #if BEAST_DOXYGEN template struct is_dynamic_buffer : std::integral_constant {}; @@ -54,8 +109,7 @@ template struct is_dynamic_buffer : std::false_type {}; template -struct is_dynamic_buffer() = std::declval().size(), std::declval() = @@ -64,7 +118,7 @@ struct is_dynamic_buffer().capacity(), std::declval().commit(std::declval()), std::declval().consume(std::declval()), - (void)0)>> : std::integral_constant > : std::integral_constant::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::value, + "Not a completion handler"); + @endcode +*/ template #if BEAST_DOXYGEN using is_completion_handler = std::integral_constant; #else using is_completion_handler = std::integral_constant::type>::value && - detail::is_invocable::value>; + detail::is_invocable::value>; #endif - //------------------------------------------------------------------------------ // // Stream concepts // //------------------------------------------------------------------------------ -/// Determine if `T` has the `get_io_service` member. -template +/** 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::value, + "Missing get_io_service member"); + @endcode +*/ #if BEAST_DOXYGEN +template struct has_get_io_service : std::integral_constant{}; #else -using has_get_io_service = typename detail::has_get_io_service::type; +template +struct has_get_io_service : std::false_type {}; + +template +struct has_get_io_service( + std::declval().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 + struct stream_wrapper + { + using next_layer_type = typename std::remove_reference::type; + using lowest_layer_type = typename get_lowest_layer::type; + }; + @endcode +*/ #if BEAST_DOXYGEN template struct get_lowest_layer; @@ -137,52 +236,213 @@ struct get_lowest_layer +/** Determine if `T` meets the requirements of @b AsyncReadStream. + + @par Example + Use with `static_assert`: + @code + template + void f(AsyncReadStream& stream) + { + static_assert(is_async_read_stream::value, + "AsyncReadStream requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(AsyncReadStream& stream); + @endcode +*/ #if BEAST_DOXYGEN +template struct is_async_read_stream : std::integral_constant{}; #else -using is_async_read_stream = typename detail::is_async_read_stream::type; +template +struct is_async_read_stream : std::false_type {}; + +template +struct is_async_read_stream().async_read_some( + std::declval(), + std::declval()), + (void)0)>> : std::integral_constant::value + > {}; #endif -/// Determine if `T` meets the requirements of @b AsyncWriteStream. -template +/** Determine if `T` meets the requirements of @b AsyncWriteStream. + + @par Example + Use with `static_assert`: + @code + template + void f(AsyncWriteStream& stream) + { + static_assert(is_async_write_stream::value, + "AsyncWriteStream requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(AsyncWriteStream& stream); + @endcode +*/ #if BEAST_DOXYGEN +template struct is_async_write_stream : std::integral_constant{}; #else -using is_async_write_stream = typename detail::is_async_write_stream::type; +template +struct is_async_write_stream : std::false_type {}; + +template +struct is_async_write_stream().async_write_some( + std::declval(), + std::declval()), + (void)0)>> : std::integral_constant::value + > {}; #endif -/// Determine if `T` meets the requirements of @b SyncReadStream. -template +/** Determine if `T` meets the requirements of @b SyncReadStream. + + @par Example + Use with `static_assert`: + @code + template + void f(SyncReadStream& stream) + { + static_assert(is_sync_read_stream::value, + "SyncReadStream requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(SyncReadStream& stream); + @endcode +*/ #if BEAST_DOXYGEN +template struct is_sync_read_stream : std::integral_constant{}; #else -using is_sync_read_stream = typename detail::is_sync_read_stream::type; +template +struct is_sync_read_stream : std::false_type {}; + +template +struct is_sync_read_stream() = std::declval().read_some( + std::declval()), + std::declval() = std::declval().read_some( + std::declval(), + std::declval()), + (void)0)>> : std::integral_constant::value + > {}; #endif -/// Determine if `T` meets the requirements of @b SyncWriterStream. -template +/** Determine if `T` meets the requirements of @b SyncWriterStream. + + @par Example + Use with `static_assert`: + @code + template + void f(SyncReadStream& stream) + { + static_assert(is_sync_read_stream::value, + "SyncReadStream requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(SyncReadStream& stream); + @endcode +*/ #if BEAST_DOXYGEN +template struct is_sync_write_stream : std::integral_constant{}; #else -using is_sync_write_stream = typename detail::is_sync_write_stream::type; +template +struct is_sync_write_stream : std::false_type {}; + +template +struct is_sync_write_stream() = std::declval().write_some( + std::declval()), + std::declval() = std::declval().write_some( + std::declval(), + std::declval()), + (void)0)>> : std::integral_constant::value + > {}; #endif -/// Determine if `T` meets the requirements of @b AsyncStream. -template +/** Determine if `T` meets the requirements of @b AsyncStream. + + @par Example + Use with `static_assert`: + @code + template + void f(AsyncStream& stream) + { + static_assert(is_async_stream::value, + "AsyncStream requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(AsyncStream& stream); + @endcode +*/ #if BEAST_DOXYGEN +template struct is_async_stream : std::integral_constant{}; #else +template using is_async_stream = std::integral_constant::value && is_async_write_stream::value>; #endif -/// Determine if `T` meets the requirements of @b SyncStream. -template +/** Determine if `T` meets the requirements of @b SyncStream. + + + @par Example + Use with `static_assert`: + @code + template + void f(SyncStream& stream) + { + static_assert(is_sync_stream::value, + "SyncStream requirements not met"); + ... + } + @endcode + Use with `std::enable_if` + @code + template + typename std::enable_if::value>::type + f(SyncStream& stream); + @endcode +*/ #if BEAST_DOXYGEN +template struct is_sync_stream : std::integral_constant{}; #else +template using is_sync_stream = std::integral_constant::value && is_sync_write_stream::value>; #endif diff --git a/include/beast/http/detail/type_traits.hpp b/include/beast/http/detail/type_traits.hpp index afc6f681..d27f6fbc 100644 --- a/include/beast/http/detail/type_traits.hpp +++ b/include/beast/http/detail/type_traits.hpp @@ -16,6 +16,9 @@ namespace http { template struct header; +template +struct message; + namespace detail { template @@ -32,6 +35,35 @@ public: template using is_header = typename is_header_impl::type; +struct fields_model +{ + string_view method() const; + string_view reason() const; + string_view target() 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"); +}; + } // detail } // http } // beast diff --git a/include/beast/http/type_traits.hpp b/include/beast/http/type_traits.hpp index 6aa51b2a..20a74930 100644 --- a/include/beast/http/type_traits.hpp +++ b/include/beast/http/type_traits.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -20,43 +21,23 @@ namespace beast { namespace http { -template -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> -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"); -}; - -} // detail - -/// Determine if `T` meets the requirements of @b Body. + @par Example + @code + template + void check_body(message const&) + { + static_assert(is_body::value, + "Body requirements not met"); + } + @endcode +*/ template #if BEAST_DOXYGEN struct is_body : std::integral_constant{}; @@ -68,11 +49,20 @@ using is_body = detail::has_value_type; 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 + void check_can_serialize(message const&) + { + static_assert(is_body_reader::value, + "Cannot serialize Body, no reader"); + @endcode */ #if BEAST_DOXYGEN template @@ -95,20 +85,27 @@ struct is_body_reader::value && std::is_constructible 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 + void check_can_parse(message&) + { + static_assert(is_body_writer::value, + "Cannot parse Body, no writer"); + @endcode */ #if BEAST_DOXYGEN template diff --git a/test/core/type_traits.cpp b/test/core/type_traits.cpp index d9ffba1f..4706155b 100644 --- a/test/core/type_traits.cpp +++ b/test/core/type_traits.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace beast { @@ -125,10 +126,16 @@ BOOST_STATIC_ASSERT(! is_completion_handler::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::value); BOOST_STATIC_ASSERT(is_async_read_stream::value); BOOST_STATIC_ASSERT(is_async_write_stream::value); @@ -137,12 +144,12 @@ BOOST_STATIC_ASSERT(is_sync_read_stream::value); BOOST_STATIC_ASSERT(is_sync_write_stream::value); BOOST_STATIC_ASSERT(is_sync_stream::value); -BOOST_STATIC_ASSERT(! has_get_io_service::value); -BOOST_STATIC_ASSERT(! is_async_read_stream::value); -BOOST_STATIC_ASSERT(! is_async_write_stream::value); -BOOST_STATIC_ASSERT(! is_sync_read_stream::value); -BOOST_STATIC_ASSERT(! is_sync_write_stream::value); +BOOST_STATIC_ASSERT(! has_get_io_service::value); +BOOST_STATIC_ASSERT(! is_async_read_stream::value); +BOOST_STATIC_ASSERT(! is_async_write_stream::value); +BOOST_STATIC_ASSERT(! is_sync_read_stream::value); +BOOST_STATIC_ASSERT(! is_sync_write_stream::value); -//} // (anonymous) +} // (anonymous) } // beast