diff options
-rw-r--r-- | libadhocutil/compileTimeFormatter.h | 2 | ||||
-rw-r--r-- | libadhocutil/ctf-impl/printf-compat.h (renamed from libadhocutil/detail/compileTimeFormatters.h) | 79 | ||||
-rw-r--r-- | libadhocutil/unittests/testCompileTimeFormatter.cpp | 26 |
3 files changed, 74 insertions, 33 deletions
diff --git a/libadhocutil/compileTimeFormatter.h b/libadhocutil/compileTimeFormatter.h index bb80edf..77fa753 100644 --- a/libadhocutil/compileTimeFormatter.h +++ b/libadhocutil/compileTimeFormatter.h @@ -176,8 +176,6 @@ namespace AdHoc { }; } -#include "detail/compileTimeFormatters.h" - #define AdHocFormatterTypedef(name, str, id) \ inline constexpr auto id = str; \ typedef ::AdHoc::Formatter<id> name diff --git a/libadhocutil/detail/compileTimeFormatters.h b/libadhocutil/ctf-impl/printf-compat.h index 00ee852..3367b4a 100644 --- a/libadhocutil/detail/compileTimeFormatters.h +++ b/libadhocutil/ctf-impl/printf-compat.h @@ -3,6 +3,11 @@ #include "../compileTimeFormatter.h" #include <boost/assert.hpp> +#include <boost/preprocessor/repetition/repeat.hpp> +#include <boost/preprocessor/cat.hpp> +#include <boost/preprocessor/if.hpp> +#include <boost/preprocessor/comma_if.hpp> +#include <boost/preprocessor/arithmetic/add.hpp> #include <iomanip> #include <type_traits> @@ -67,6 +72,7 @@ namespace AdHoc { static inline void write(stream & s, Obj * const ptr, const Pn & ... pn) { s << std::showbase << std::hex << (long unsigned int)ptr; + s.copyfmt(std::ios(NULL)); StreamWriter::next(s, pn...); } template<typename Ptr, typename ... Pn> @@ -81,6 +87,7 @@ namespace AdHoc { static inline void write(stream & s, const Pn & ... pn) { s << strerror(errno); + s.copyfmt(std::ios(NULL)); StreamWriter::next(s, pn...); } }; @@ -90,50 +97,58 @@ namespace AdHoc { { BOOST_ASSERT_MSG(n, "%n conversion requires non-null parameter"); *n = streamLength(s); + s.copyfmt(std::ios(NULL)); StreamWriter::next(s, pn...); } }; //// // Width/precision embedded in format string - // Limitted to 3 digits at the moment - template<const auto & S, auto L, auto pos, typename stream, auto n0, auto nn, auto ... sn> - struct StreamWriter<S, L, pos, stream, typename std::enable_if<ispositivedigit(n0) && !isdigit(nn)>::type, '%', n0, nn, sn...> { - template<typename ... Pn> - static inline void write(stream & s, const Pn & ... pn) - { - constexpr auto p = (n0 - '0'); - s << std::setw(p) << std::setprecision(p); - StreamWriter<S, L, pos + 1, stream, void, '%', nn, sn...>::write(s, pn...); - } - }; - template<const auto & S, auto L, auto pos, typename stream, auto n0, auto n1, auto nn, auto ... sn> - struct StreamWriter<S, L, pos, stream, typename std::enable_if<ispositivedigit(n0) && isdigit(n1) && !isdigit(nn)>::type, '%', n0, n1, nn, sn...> { - template<typename ... Pn> - static inline void write(stream & s, const Pn & ... pn) - { - constexpr auto p = ((n0 - '0') * 10) + (n1 - '0'); - s << std::setw(p) << std::setprecision(p); - StreamWriter<S, L, pos + 2, stream, void, '%', nn, sn...>::write(s, pn...); - } + template<auto ... chs> + constexpr auto decdigits() + { + static_assert((isdigit(chs) && ... && true)); + int n = 0; + ([&n](auto ch) { + n = (n * 10) + (ch - '0'); + }(chs), ...); + return n; + } +#define AUTON(z, n, data) BOOST_PP_COMMA_IF(n) auto BOOST_PP_CAT(data, n) +#define NS(z, n, data) BOOST_PP_COMMA_IF(n) BOOST_PP_CAT(data, n) +#define ISDIGIT(z, n, data) && isdigit(BOOST_PP_CAT(data, BOOST_PP_ADD(n, 1))) +#define FMTWIDTH(unused, d, data) \ + template<const auto & S, auto L, auto pos, typename stream, BOOST_PP_REPEAT(BOOST_PP_ADD(d, 1), AUTON, n), auto nn, auto ... sn> \ + struct StreamWriter<S, L, pos, stream, typename std::enable_if<ispositivedigit(n0) BOOST_PP_REPEAT(d, ISDIGIT, n) && !isdigit(nn)>::type, '%', BOOST_PP_REPEAT(BOOST_PP_ADD(d, 1), NS, n), nn, sn...> { \ + template<typename ... Pn> \ + static inline void write(stream & s, const Pn & ... pn) { \ + constexpr auto p = decdigits<BOOST_PP_REPEAT(BOOST_PP_ADD(d, 1), NS, n)>(); \ + s << std::setw(p); \ + StreamWriter<S, L, pos + BOOST_PP_ADD(d, 1), stream, void, '%', nn, sn...>::write(s, pn...); \ + } \ }; - template<const auto & S, auto L, auto pos, typename stream, auto n0, auto n1, auto n2, auto nn, auto ... sn> - struct StreamWriter<S, L, pos, stream, typename std::enable_if<ispositivedigit(n0) && isdigit(n1) && isdigit(n2) && !isdigit(nn)>::type, '%', n0, n1, n2, nn, sn...> { - template<typename ... Pn> - static inline void write(stream & s, const Pn & ... pn) - { - constexpr auto p = ((n0 - '0') * 100) + ((n1 - '0') * 10) + (n2 - '0'); - s << std::setw(p) << std::setprecision(p); - StreamWriter<S, L, pos + 3, stream, void, '%', nn, sn...>::write(s, pn...); - } + BOOST_PP_REPEAT(6, FMTWIDTH, void); +#define FMTPRECISION(unused, d, data) \ + template<const auto & S, auto L, auto pos, typename stream, BOOST_PP_REPEAT(BOOST_PP_ADD(d, 1), AUTON, n), auto nn, auto ... sn> \ + struct StreamWriter<S, L, pos, stream, typename std::enable_if<isdigit(n0) BOOST_PP_REPEAT(d, ISDIGIT, n) && !isdigit(nn)>::type, '%', '.', BOOST_PP_REPEAT(BOOST_PP_ADD(d, 1), NS, n), nn, sn...> { \ + template<typename ... Pn> \ + static inline void write(stream & s, const Pn & ... pn) { \ + constexpr auto p = decdigits<BOOST_PP_REPEAT(BOOST_PP_ADD(d, 1), NS, n)>(); \ + s << std::setprecision(p); \ + StreamWriter<S, L, pos + BOOST_PP_ADD(d, 2), stream, void, '%', nn, sn...>::write(s, pn...); \ + } \ }; - //// + BOOST_PP_REPEAT(6, FMTPRECISION, void); +#undef AUTON +#undef NS +#undef ISDIGIT +#undef FMTWIDTH StreamWriterT('.', '*') { template<typename ... Pn> static inline void write(stream & s, int l, const Pn & ... pn) { - s << std::setw(l) << std::setprecision(l); + s << std::setw(l); StreamWriter<S, L, pos + 2, stream, void, '%', sn...>::write(s, pn...); } }; @@ -142,6 +157,7 @@ namespace AdHoc { static inline void write(stream & s, int l, const std::string_view & p, const Pn & ... pn) { s << p.substr(0, l); + s.copyfmt(std::ios(NULL)); StreamWriter::next(s, pn...); } }; @@ -157,6 +173,7 @@ namespace AdHoc { FLAGCONV(s << std::showbase, '#'); FLAGCONV(s << std::setfill('0'), '0'); FLAGCONV(s << std::left, '-'); + FLAGCONV(s << std::showpos, '+'); FLAGCONV(s << std::setfill(' '), ' '); #undef FLAGCONV } diff --git a/libadhocutil/unittests/testCompileTimeFormatter.cpp b/libadhocutil/unittests/testCompileTimeFormatter.cpp index 138d5dc..b966078 100644 --- a/libadhocutil/unittests/testCompileTimeFormatter.cpp +++ b/libadhocutil/unittests/testCompileTimeFormatter.cpp @@ -308,6 +308,25 @@ BOOST_AUTO_TEST_CASE( filestar ) free(buf); } +#include "ctf-impl/printf-compat.h" + +static_assert(isdigit('0')); +static_assert(isdigit('9')); +static_assert(!isdigit('.')); +static_assert(!isdigit('a')); + +static_assert(!ispositivedigit('0')); +static_assert(ispositivedigit('1')); +static_assert(ispositivedigit('9')); +static_assert(!ispositivedigit('.')); +static_assert(!ispositivedigit('a')); + +static_assert(0 == decdigits<'0'>()); +static_assert(1 == decdigits<'1'>()); +static_assert(19 == decdigits<'1', '9'>()); +static_assert(419 == decdigits<'4', '1', '9'>()); +static_assert(419 == decdigits<'0', '4', '1', '9'>()); + // The following tests represent CTF's [partial] emulation of many // POSIX formatting features #define GLIBC_FMT_TEST(NAME, FMT, ...) \ @@ -332,6 +351,10 @@ GLIBC_FMT_TEST(s5, "in %.*s.", 7, "other"); GLIBC_FMT_TEST(s35, "in %3s.", "other"); GLIBC_FMT_TEST(s55, "in %5s.", "other"); GLIBC_FMT_TEST(s115, "in %11s.", "other"); +//std::setw does not truncate strings +//GLIBC_FMT_TEST(sd35, "in %.3s.", "other"); +GLIBC_FMT_TEST(sd55, "in %.5s.", "other"); +GLIBC_FMT_TEST(sd115, "in %.11s.", "other"); GLIBC_FMT_TEST(c1, "in %c.", 'b'); GLIBC_FMT_TEST(c2, "in %c.", 'B'); @@ -398,6 +421,9 @@ GLIBC_FMT_TEST(g6, "in %G.", -123.456789); GLIBC_FMT_TEST(g7, "in %g.", 123456789.123); GLIBC_FMT_TEST(g8, "in %g.", -123456789.123); +GLIBC_FMT_TEST(fmtlibt_fmt, "%0.10f:%04d:%+g:%s:%p:%c:%%\n", + 1.234, 42, 3.13, "str", (void*)1000, (int)'X'); + AdHocFormatter(chars_written_fmt, "%n %s %n %d %n"); BOOST_AUTO_TEST_CASE(chars_written) { |