From 7c0d59aab6a2e5a991fc13c73d671d3e6a002272 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 7 Dec 2016 23:23:26 +0000 Subject: Adds support for the compile time formatter --- libadhocutil/compileTimeFormatter.h | 111 +++++++++++++++++ libadhocutil/unittests/Jamfile.jam | 11 ++ .../unittests/testCompileTimeFormatter.cpp | 138 +++++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 libadhocutil/compileTimeFormatter.h create mode 100644 libadhocutil/unittests/testCompileTimeFormatter.cpp 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 +#include +#include + +namespace AdHoc { + template struct Buffer { }; + + template + struct Upto { + template + static auto stuff(stream &, const Buffer<> & f) + { + return f; + } + }; + template + struct Upto { + template + static auto stuff(stream & s, const Buffer &) + { + return Upto::stuff(s, Buffer()); + } + }; + template + struct Upto { + template + static auto stuff(stream & s, const Buffer &) + { + char buf[] = {sm...}; + s.write(buf, sizeof...(sm)); + return Buffer(); + } + }; + template + struct Upto { + template + static auto stuff(stream & s, const Buffer &) + { + char buf[] = {sm...}; + s.write(buf, sizeof...(sm)); + return Buffer<0, sn...>(); + } + }; + + template + struct StreamWriter { + template + static void write(stream & s, const Pn & ... pn) + { + next(s, Upto<'%', sn...>::stuff(s, Buffer<>()), pn...); + } + template class Buffer> + static void next(stream & s, const Buffer&, const Pn & ... pn) + { + StreamWriter::write(s, pn...); + } + }; + + template + struct StreamWriter { + template + static void write(stream &, const Pn & ...) { } + }; + + template + struct StreamWriter { + template + static void write(stream &, const Pn & ...) { } + }; + + // Default stream writer formatter + template + struct StreamWriter { + template + static void write(stream & s, const P & p, const Pn & ... pn) + { + s << p; + StreamWriter::write(s, pn...); + } + }; + + // Unknown stream writer formatter + template + struct StreamWriter { + template + static void write(stream &, const Pn & ...) + { + BOOST_STATIC_ASSERT_MSG(!&err, "invalid format string/arguments"); + } + static int err; + }; + + template + struct Formatter { + template + static void write(stream & s, const Pn & ... pn) + { + Formatter::write(s, pn...); + } + }; + + template + struct Formatter { + template + static void write(stream & s, const Pn & ... pn) + { + StreamWriter::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 @@ -8,6 +8,17 @@ lib boost_system ; lib boost_thread ; lib dl ; +run + testCompileTimeFormatter.cpp + : : : + ROOT=\"$(me)\" + BOOST_TEST_DYN_LINK + ..//adhocutil + boost_system + boost_filesystem + boost_utf + ; + run testUriParse.cpp : : : 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 + +#include + +#include + +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::write(buf); + BOOST_REQUIRE(buf.str().empty()); +} + +BOOST_AUTO_TEST_CASE ( single ) +{ + std::stringstream buf; + Formatter::write(buf); + BOOST_REQUIRE_EQUAL(buf.str(), "1"); +} + +BOOST_AUTO_TEST_CASE ( start ) +{ + std::stringstream buf; + Formatter::write(buf, 10); + BOOST_REQUIRE_EQUAL(buf.str(), "10 after"); +} + +BOOST_AUTO_TEST_CASE ( end ) +{ + std::stringstream buf; + Formatter::write(buf, 10); + BOOST_REQUIRE_EQUAL(buf.str(), "before 10"); +} + +BOOST_AUTO_TEST_CASE ( lonely ) +{ + std::stringstream buf; + Formatter::write(buf, 10); + BOOST_REQUIRE_EQUAL(buf.str(), "10"); +} + +BOOST_AUTO_TEST_CASE ( literal ) +{ + std::stringstream buf; + Formatter::write(buf); + BOOST_REQUIRE_EQUAL(buf.str(), "literal"); +} + +BOOST_AUTO_TEST_CASE ( singleInt ) +{ + std::stringstream buf; + Formatter::write(buf, 32); + BOOST_REQUIRE_EQUAL(buf.str(), "single 32."); +} + +BOOST_AUTO_TEST_CASE ( singleDouble ) +{ + std::stringstream buf; + Formatter::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::write(buf, p); + BOOST_REQUIRE_EQUAL(buf.str(), R"(single "/tmp/test/path".)"); +} + +BOOST_AUTO_TEST_CASE ( multi ) +{ + std::stringstream buf; + Formatter::write(buf, "one", "two"); + BOOST_REQUIRE_EQUAL(buf.str(), "First one, then two."); +} + +namespace AdHoc { + // Custom stream writer formatter, formats as (bracketed expression) + template + struct StreamWriter { + template + static void write(stream & s, const P & p, const Pn & ... pn) + { + s << "-( " << p << " )-"; + StreamWriter::write(s, pn...); + } + }; + // Custom stream writer formatter, formats + // right-aligned by given width + template + struct StreamWriter { + template + 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::write(s, pn...); + } + }; +} + +extern constexpr const char * formatStringCustom = "custom %()"; +BOOST_AUTO_TEST_CASE ( customBracketted ) +{ + std::stringstream buf; + Formatter::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::write(buf1, width, "something"); + Formatter::write(buf2, width, "something else"); + Formatter::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"); +} + -- cgit v1.2.3