diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2018-09-03 19:49:31 +0100 | 
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2018-09-03 19:49:31 +0100 | 
| commit | 98c81cc1ed3e42c7c8d80711517d101553b6f5c5 (patch) | |
| tree | 1361f776a088119293b511f885e781f43cd837f3 | |
| parent | Add basic support for setting the width/precision in the format string (diff) | |
| download | libadhocutil-98c81cc1ed3e42c7c8d80711517d101553b6f5c5.tar.bz2 libadhocutil-98c81cc1ed3e42c7c8d80711517d101553b6f5c5.tar.xz libadhocutil-98c81cc1ed3e42c7c8d80711517d101553b6f5c5.zip | |
Greatly improved CTF printf support
| -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)  { | 
