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