diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-12-07 23:23:26 +0000 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2016-12-07 23:34:13 +0000 |
commit | 7c0d59aab6a2e5a991fc13c73d671d3e6a002272 (patch) | |
tree | 0e3ca5f8f296c5779f792352f4459bdb7b505897 | |
parent | Test exception message formatting (diff) | |
download | libadhocutil-7c0d59aab6a2e5a991fc13c73d671d3e6a002272.tar.bz2 libadhocutil-7c0d59aab6a2e5a991fc13c73d671d3e6a002272.tar.xz libadhocutil-7c0d59aab6a2e5a991fc13c73d671d3e6a002272.zip |
Adds support for the compile time formatter
-rw-r--r-- | libadhocutil/compileTimeFormatter.h | 111 | ||||
-rw-r--r-- | libadhocutil/unittests/Jamfile.jam | 11 | ||||
-rw-r--r-- | libadhocutil/unittests/testCompileTimeFormatter.cpp | 138 |
3 files changed, 260 insertions, 0 deletions
diff --git a/libadhocutil/compileTimeFormatter.h b/libadhocutil/compileTimeFormatter.h new file mode 100644 index 0000000..aa30e20 --- /dev/null +++ b/libadhocutil/compileTimeFormatter.h @@ -0,0 +1,111 @@ +#include <iostream> +#include <string.h> +#include <boost/static_assert.hpp> + +namespace AdHoc { + template <char...> struct Buffer { }; + + template <char stop, char ...> + struct Upto { + template<typename stream> + static auto stuff(stream &, const Buffer<> & f) + { + return f; + } + }; + template <char stop, char s0, char... sn> + struct Upto<stop, s0, sn...> { + template<typename stream, char... sm> + static auto stuff(stream & s, const Buffer<sm...> &) + { + return Upto<stop, sn...>::stuff(s, Buffer<sm..., s0>()); + } + }; + template <char stop, char... sn> + struct Upto<stop, stop, sn...> { + template<typename stream, char... sm> + static auto stuff(stream & s, const Buffer<sm...> &) + { + char buf[] = {sm...}; + s.write(buf, sizeof...(sm)); + return Buffer<stop, sn...>(); + } + }; + template <char stop, char... sn> + struct Upto<stop, 0, sn...> { + template<typename stream, char... sm> + static auto stuff(stream & s, const Buffer<sm...> &) + { + char buf[] = {sm...}; + s.write(buf, sizeof...(sm)); + return Buffer<0, sn...>(); + } + }; + + template <typename stream, char ... sn> + struct StreamWriter { + template<typename ... Pn> + static void write(stream & s, const Pn & ... pn) + { + next(s, Upto<'%', sn...>::stuff(s, Buffer<>()), pn...); + } + template<typename ... Pn, char... ssn, template <char...> class Buffer> + static void next(stream & s, const Buffer<ssn...>&, const Pn & ... pn) + { + StreamWriter<stream, ssn...>::write(s, pn...); + } + }; + + template<typename stream> + struct StreamWriter<stream> { + template<typename ... Pn> + static void write(stream &, const Pn & ...) { } + }; + + template<typename stream> + struct StreamWriter<stream, 0> { + template<typename ... Pn> + static void write(stream &, const Pn & ...) { } + }; + + // Default stream writer formatter + template<typename stream, char ... sn> + struct StreamWriter<stream, '%', '?', sn...> { + template<typename P, typename ... Pn> + static void write(stream & s, const P & p, const Pn & ... pn) + { + s << p; + StreamWriter<stream, sn...>::write(s, pn...); + } + }; + + // Unknown stream writer formatter + template<typename stream, char ... sn> + struct StreamWriter<stream, '%', sn...> { + template<typename ... Pn> + static void write(stream &, const Pn & ...) + { + BOOST_STATIC_ASSERT_MSG(!&err, "invalid format string/arguments"); + } + static int err; + }; + + template <const char * const & S, char s0 = *S, int offset = 0, char ... sn> + struct Formatter { + template<typename stream, typename ... Pn> + static void write(stream & s, const Pn & ... pn) + { + Formatter<S, S[offset], offset + 1, sn..., S[offset]>::write(s, pn...); + } + }; + + template <const char * const & S, int offset, char ... sn> + struct Formatter<S, 0, offset, sn...> { + template<typename stream, typename ... Pn> + static void write(stream & s, const Pn & ... pn) + { + StreamWriter<stream, sn...>::write(s, pn...); + } + }; +} + diff --git a/libadhocutil/unittests/Jamfile.jam b/libadhocutil/unittests/Jamfile.jam index 5072f39..2fb148b 100644 --- a/libadhocutil/unittests/Jamfile.jam +++ b/libadhocutil/unittests/Jamfile.jam @@ -9,6 +9,17 @@ lib boost_thread ; lib dl ; run + testCompileTimeFormatter.cpp + : : : + <define>ROOT=\"$(me)\" + <define>BOOST_TEST_DYN_LINK + <library>..//adhocutil + <library>boost_system + <library>boost_filesystem + <library>boost_utf + ; + +run testUriParse.cpp : : : <define>ROOT=\"$(me)\" diff --git a/libadhocutil/unittests/testCompileTimeFormatter.cpp b/libadhocutil/unittests/testCompileTimeFormatter.cpp new file mode 100644 index 0000000..4248cb6 --- /dev/null +++ b/libadhocutil/unittests/testCompileTimeFormatter.cpp @@ -0,0 +1,138 @@ +#define BOOST_TEST_MODULE CompileTimeFormatter +#include <boost/test/unit_test.hpp> + +#include <compileTimeFormatter.h> + +#include <boost/filesystem/path.hpp> + +using namespace AdHoc; + +extern constexpr const char * formatEdgeCaseEmpty = ""; +extern constexpr const char * formatEdgeCaseSingle = "1"; +extern constexpr const char * formatEdgeCaseFormatStart = "%? after"; +extern constexpr const char * formatEdgeCaseFormatEnd = "before %?"; +extern constexpr const char * formatEdgeCaseFormatLonely = "%?"; + +extern constexpr const char * formatStringLiteral = "literal"; +extern constexpr const char * formatStringSingle = "single %?."; +extern constexpr const char * formatStringMulti = "First %?, then %?."; + +BOOST_AUTO_TEST_CASE ( empty ) +{ + std::stringstream buf; + Formatter<formatEdgeCaseEmpty>::write(buf); + BOOST_REQUIRE(buf.str().empty()); +} + +BOOST_AUTO_TEST_CASE ( single ) +{ + std::stringstream buf; + Formatter<formatEdgeCaseSingle>::write(buf); + BOOST_REQUIRE_EQUAL(buf.str(), "1"); +} + +BOOST_AUTO_TEST_CASE ( start ) +{ + std::stringstream buf; + Formatter<formatEdgeCaseFormatStart>::write(buf, 10); + BOOST_REQUIRE_EQUAL(buf.str(), "10 after"); +} + +BOOST_AUTO_TEST_CASE ( end ) +{ + std::stringstream buf; + Formatter<formatEdgeCaseFormatEnd>::write(buf, 10); + BOOST_REQUIRE_EQUAL(buf.str(), "before 10"); +} + +BOOST_AUTO_TEST_CASE ( lonely ) +{ + std::stringstream buf; + Formatter<formatEdgeCaseFormatLonely>::write(buf, 10); + BOOST_REQUIRE_EQUAL(buf.str(), "10"); +} + +BOOST_AUTO_TEST_CASE ( literal ) +{ + std::stringstream buf; + Formatter<formatStringLiteral>::write(buf); + BOOST_REQUIRE_EQUAL(buf.str(), "literal"); +} + +BOOST_AUTO_TEST_CASE ( singleInt ) +{ + std::stringstream buf; + Formatter<formatStringSingle>::write(buf, 32); + BOOST_REQUIRE_EQUAL(buf.str(), "single 32."); +} + +BOOST_AUTO_TEST_CASE ( singleDouble ) +{ + std::stringstream buf; + Formatter<formatStringSingle>::write(buf, 3.14); + BOOST_REQUIRE_EQUAL(buf.str(), "single 3.14."); +} + +BOOST_AUTO_TEST_CASE ( singlePath ) +{ + std::stringstream buf; + boost::filesystem::path p("/tmp/test/path"); + Formatter<formatStringSingle>::write(buf, p); + BOOST_REQUIRE_EQUAL(buf.str(), R"(single "/tmp/test/path".)"); +} + +BOOST_AUTO_TEST_CASE ( multi ) +{ + std::stringstream buf; + Formatter<formatStringMulti>::write(buf, "one", "two"); + BOOST_REQUIRE_EQUAL(buf.str(), "First one, then two."); +} + +namespace AdHoc { + // Custom stream writer formatter, formats as (bracketed expression) + template<typename stream, char ... sn> + struct StreamWriter<stream, '%', '(', ')', sn...> { + template<typename P, typename ... Pn> + static void write(stream & s, const P & p, const Pn & ... pn) + { + s << "-( " << p << " )-"; + StreamWriter<stream, sn...>::write(s, pn...); + } + }; + // Custom stream writer formatter, formats + // right-aligned by given width + template<typename stream, char ... sn> + struct StreamWriter<stream, '%', 'r', 'a', sn...> { + template<typename P, typename ... Pn> + static void write(stream & s, int width, const P & p, const Pn & ... pn) + { + std::stringstream buf; + buf << p; + std::string spaces(width - buf.str().length(), ' '); + s << spaces << buf.str(); + StreamWriter<stream, sn...>::write(s, pn...); + } + }; +} + +extern constexpr const char * formatStringCustom = "custom %()"; +BOOST_AUTO_TEST_CASE ( customBracketted ) +{ + std::stringstream buf; + Formatter<formatStringCustom>::write(buf, "expr"); + BOOST_REQUIRE_EQUAL(buf.str(), "custom -( expr )-"); +} + +extern constexpr const char * formatStringMultiArg = "value%ra"; +BOOST_AUTO_TEST_CASE ( customMultiArgRightAlign ) +{ + std::stringstream buf1, buf2, buf3; + const int width = 20; + Formatter<formatStringMultiArg>::write(buf1, width, "something"); + Formatter<formatStringMultiArg>::write(buf2, width, "something else"); + Formatter<formatStringMultiArg>::write(buf3, width, 123.45); + BOOST_REQUIRE_EQUAL(buf1.str(), "value something"); + BOOST_REQUIRE_EQUAL(buf2.str(), "value something else"); + BOOST_REQUIRE_EQUAL(buf3.str(), "value 123.45"); +} + |