diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a4697b4..5c94d155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Version 54: * multi_buffer coverage * consuming_buffers members and coverage * basic_fields members and coverage +* Add string_param -------------------------------------------------------------------------------- diff --git a/doc/quickref.xml b/doc/quickref.xml index b64ac0de..90314233 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -177,6 +177,7 @@ static_buffer static_buffer_n static_string + string_param string_view system_error diff --git a/include/beast/core.hpp b/include/beast/core.hpp index 214c9f33..f7577253 100644 --- a/include/beast/core.hpp +++ b/include/beast/core.hpp @@ -14,9 +14,10 @@ #include #include #include +#include #include #include -#include +#include #include #include #include @@ -25,6 +26,8 @@ #include #include #include +#include +#include #include #endif diff --git a/include/beast/core/detail/static_ostream.hpp b/include/beast/core/detail/static_ostream.hpp new file mode 100644 index 00000000..d64e41b8 --- /dev/null +++ b/include/beast/core/detail/static_ostream.hpp @@ -0,0 +1,133 @@ +// +// Copyright (c) 2013-2017 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_STATIC_OSTREAM_HPP +#define BEAST_DETAIL_STATIC_OSTREAM_HPP + +#include +#include +#include + +namespace beast { +namespace detail { + +// http://www.mr-edd.co.uk/blog/beginners_guide_streambuf + +class static_ostream_buffer + : public std::basic_streambuf +{ + using CharT = char; + using Traits = std::char_traits; + using int_type = typename + std::basic_streambuf::int_type; + using traits_type = typename + std::basic_streambuf::traits_type; + + char buf_[128]; + std::string s_; + std::size_t len_ = 0; + +public: + static_ostream_buffer(static_ostream_buffer&&) = delete; + static_ostream_buffer(static_ostream_buffer const&) = delete; + + static_ostream_buffer() + { + this->setp(buf_, buf_ + sizeof(buf_) - 1); + } + + ~static_ostream_buffer() noexcept + { + } + + string_view + str() const + { + if(! s_.empty()) + return {s_.data(), len_}; + return {buf_, len_}; + } + + int_type + overflow(int_type ch) override + { + if(! Traits::eq_int_type(ch, Traits::eof())) + { + Traits::assign(*this->pptr(), ch); + flush(1); + prepare(); + return ch; + } + flush(); + return traits_type::eof(); + } + + int + sync() override + { + flush(); + prepare(); + return 0; + } + +private: + void + prepare() + { + static auto const growth_factor = 1.5; + + if(len_ < sizeof(buf_) - 1) + { + this->setp(&buf_[len_], + &buf_[sizeof(buf_) - 2]); + return; + } + if(s_.empty()) + { + s_.resize(static_cast( + growth_factor * len_)); + Traits::copy(&s_[0], buf_, len_); + } + else + { + s_.resize(static_cast( + growth_factor * len_)); + } + this->setp(&s_[len_], &s_[len_] + + s_.size() - len_ - 1); + } + + void + flush(int extra = 0) + { + len_ += static_cast( + this->pptr() - this->pbase() + extra); + } +}; + +class static_ostream : public std::basic_ostream +{ + static_ostream_buffer osb_; + +public: + static_ostream() + : std::basic_ostream(&this->osb_) + { + imbue(std::locale::classic()); + } + + string_view + str() const + { + return osb_.str(); + } +}; + +} // detail +} // beast + +#endif diff --git a/include/beast/core/string_param.hpp b/include/beast/core/string_param.hpp new file mode 100644 index 00000000..c71c5a6c --- /dev/null +++ b/include/beast/core/string_param.hpp @@ -0,0 +1,125 @@ +// +// Copyright (c) 2013-2017 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_STRING_PARAM_HPP +#define BEAST_STRING_PARAM_HPP + +#include +#include +#include +#include + +namespace beast { + +/** A function parameter which efficiently converts to string. + + This is used as a function parameter type to allow callers + notational convenience: objects other than strings may be + passed in contexts where a string is expected. The conversion + to string is made using `operator<<` to a non-dynamically + allocated static buffer if possible, else to a `std::string` + on overflow. +*/ +class string_param +{ + template + struct is_streamable : std::false_type{}; + +#if ! BEAST_DOXYGEN + template + struct is_streamable() << std::declval() + )>> : std::true_type {}; +#endif + + template + void + assign(T const& t, std::true_type) + { + sv_ = t; + } + + template + void + assign(T const& t, std::false_type) + { + os_ << t; + os_.flush(); + sv_ = os_.str(); + } + + detail::static_ostream os_; + string_view sv_; + +public: + /// Copy constructor (disallowed) + string_param(string_param const&) = delete; + + /// Copy assignment (disallowed) + string_param& operator=(string_param const&) = delete; + + /** Constructor + + This function constructs a string from the specified + parameter. + + If @ref string_view is constructible from `T` then + the complexity is O(1), and no dynamic allocation + takes place. + + Otherwise, the argument is converted to a string + as if by a call to `boost::lexical_cast(t)`. + An attempt is made to store this string in a reasonably + sized buffer inside the class, to avoid dynamic allocation. + + If the argument could not be converted given the space + of the class buffer, a final attempt is made to convert + the argument to a `std::string`. This attempt may cause + a memory allocation. + + @param t The argument to convert to string. + + @note This function participates in overload resolution + only if `T` is convertible to @ref string_view, or if + `operator<<(std::ostream&, T)` is defined. + */ +#if BEAST_DOXYGEN + template +#else + template::value || + is_streamable::value + >::type> +#endif + string_param(T const& t); + + /// Conversion to @ref string_view for the converted argument. + string_view const + str() const + { + return sv_; + } + + /// Conversion to @ref string_view operator + operator string_view const() const + { + return sv_; + } +}; + +#if ! BEAST_DOXYGEN +template +string_param:: +string_param(T const& t) +{ + assign(t, std::is_convertible{}); +} +#endif + +} // beast + +#endif diff --git a/test/Jamfile b/test/Jamfile index 5d6bb606..ccaab675 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -36,6 +36,7 @@ unit-test core-tests : core/ostream.cpp core/static_buffer.cpp core/static_string.cpp + core/string_param.cpp core/string_view.cpp core/type_traits.cpp core/base64.cpp diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 23f946b0..003f880f 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable (core-tests ostream.cpp static_buffer.cpp static_string.cpp + string_param.cpp string_view.cpp type_traits.cpp base64.cpp diff --git a/test/core/string_param.cpp b/test/core/string_param.cpp new file mode 100644 index 00000000..e55e1b2f --- /dev/null +++ b/test/core/string_param.cpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2013-2017 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) +// + +// Test that header file is self-contained. +#include + +#include +#include + +namespace beast { + +class string_param_test : public unit_test::suite +{ +public: + struct nop {}; + + static_assert(! std::is_constructible::value, ""); + + void + check(string_param const& v, string_view s) + { + BEAST_EXPECT(v.str() == s); + } + + void + run() override + { + check(std::string("hello"), "hello"); + check("xyz", "xyz"); + check(1, "1"); + check(12, "12"); + check(123, "123"); + check(1234, "1234"); + check(12345, "12345"); + } +}; + +BEAST_DEFINE_TESTSUITE(string_param,core,beast); + +} // beast