diff options
| -rw-r--r-- | libadhocutil/compileTimeFormatter.h | 8 | ||||
| -rw-r--r-- | libadhocutil/detail/compileTimeFormatters.h | 128 | ||||
| -rw-r--r-- | libadhocutil/unittests/testCompileTimeFormatter.cpp | 105 | 
3 files changed, 241 insertions, 0 deletions
diff --git a/libadhocutil/compileTimeFormatter.h b/libadhocutil/compileTimeFormatter.h index fecbd91..b05e9f4 100644 --- a/libadhocutil/compileTimeFormatter.h +++ b/libadhocutil/compileTimeFormatter.h @@ -95,6 +95,12 @@ namespace AdHoc {  		s.write(p, n);  	} +	template<typename stream> +	static inline auto streamLength(stream & s) +	{ +		return s.tellp(); +	} +  	/**  	 * Compile time string formatter.  	 * @param S the format string. @@ -161,6 +167,8 @@ 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/detail/compileTimeFormatters.h new file mode 100644 index 0000000..6fa77d0 --- /dev/null +++ b/libadhocutil/detail/compileTimeFormatters.h @@ -0,0 +1,128 @@ +#ifndef ADHOCUTIL_COMPILE_TIME_FORMATTER_PRINTF_H +#define ADHOCUTIL_COMPILE_TIME_FORMATTER_PRINTF_H + +#include "../compileTimeFormatter.h" +#include <boost/assert.hpp> +#include <iomanip> + +namespace AdHoc { +#define BASICCONV(PARAMTYPE, OP, ...) \ +	StreamWriterT(__VA_ARGS__) { \ +		template<typename ... Pn> \ +		static inline void write(stream & s, const PARAMTYPE & p, const Pn & ... pn) \ +		{ \ +			OP; \ +			s.copyfmt(std::ios(NULL)); \ +			StreamWriter::next(s, pn...); \ +		} \ +	} + +	// Integers (d, i, o, u, x, X) +#define INTCONV(BASE, OP, CONV) \ +	BASICCONV(BASE, OP, CONV); \ +	BASICCONV(short BASE, OP, 'h', CONV); \ +	BASICCONV(long BASE, OP, 'l', CONV); \ +	BASICCONV(long long BASE, OP, 'l', 'l', CONV); +	INTCONV(int, s << std::dec << p, 'i'); +	INTCONV(int, s << std::dec << p, 'd'); +	INTCONV(unsigned int, s << std::oct << p, 'o'); +	INTCONV(unsigned int, s << std::dec << p, 'u'); +	INTCONV(unsigned int, s << std::nouppercase << std::hex << p, 'x'); +	INTCONV(unsigned int, s << std::uppercase << std::hex << p, 'X'); +#undef INTCONV + +	BASICCONV(intmax_t, s << std::dec << p, 'j', 'd'); +	BASICCONV(uintmax_t, s << std::dec << p, 'j', 'u'); +	BASICCONV(ssize_t, s << std::dec << p, 'z', 'd'); +	BASICCONV(size_t, s << std::dec << p, 'z', 'u'); +	BASICCONV(short int, s << std::dec << p, 'h', 'h', 'i'); // char +	BASICCONV(short int, s << std::dec << p, 'h', 'h', 'd'); // char +	BASICCONV(unsigned char, s << std::dec << p, 'h', 'h', 'u'); +	BASICCONV(unsigned char, s << std::oct << p, 'h', 'h', 'o'); +	BASICCONV(unsigned char, s << std::nouppercase << std::hex << p, 'h', 'h', 'x'); +	BASICCONV(unsigned char, s << std::uppercase << std::hex << p, 'h', 'h', 'X'); + +	// Floating point (a, A, e, E, f, F, g, G) +#define FPCONV(BASE, OP, CONV) \ +	BASICCONV(BASE, OP, CONV); \ +	BASICCONV(long BASE, OP, 'L', CONV); +	FPCONV(double, s << std::nouppercase << std::hexfloat << p, 'a'); +	FPCONV(double, s << std::uppercase << std::hexfloat << p, 'A'); +	FPCONV(double, s << std::nouppercase << std::scientific << p, 'e'); +	FPCONV(double, s << std::uppercase << std::scientific << p, 'E'); +	FPCONV(double, s << std::nouppercase << std::fixed << p, 'f'); +	FPCONV(double, s << std::uppercase << std::fixed << p, 'F'); +	FPCONV(double, s << std::nouppercase << std::defaultfloat << p, 'g'); +	FPCONV(double, s << std::uppercase << std::defaultfloat << p, 'G'); +#undef FPCONV + +	BASICCONV(std::string_view, s << p, 's'); +	BASICCONV(std::wstring_view, s << p, 'l', 's'); +	BASICCONV(char, s << p, 'c'); +	BASICCONV(wchar_t, s << p, 'l', 'c'); +#undef BASICCONV +	StreamWriterT('p') { +		template<typename Obj, typename ... Pn> +		static inline void write(stream & s, Obj * const ptr, const Pn & ... pn) +		{ +			s << std::showbase << std::hex << (long unsigned int)ptr; +			StreamWriter::next(s, pn...); +		} +		template<typename Ptr, typename ... Pn> +		static inline void write(stream & s, const Ptr & ptr, const Pn & ... pn) +		{ +			write(s, ptr.get(), pn...); +		} +	}; + +	StreamWriterT('m') { +		template<typename ... Pn> +		static inline void write(stream & s, const Pn & ... pn) +		{ +			s << strerror(errno); +			StreamWriter::next(s, pn...); +		} +	}; +	StreamWriterT('n') { +		template<typename ... Pn> +		static inline void write(stream & s, int * n, const Pn & ... pn) +		{ +			BOOST_ASSERT_MSG(n, "%n conversion requires non-null parameter"); +			*n = streamLength(s); +			StreamWriter::next(s, pn...); +		} +	}; +	StreamWriterT('.', '*') { +		template<typename ... Pn> +		static inline void write(stream & s, int l, const Pn & ... pn) +		{ +			s << std::setw(l) << std::setprecision(l); +			StreamWriter<S, L, pos + 2, stream, '%', sn...>::write(s, pn...); +		} +	}; +	StreamWriterT('.', '*', 's') { +		template<typename ... Pn> +		static inline void write(stream & s, int l, const std::string & p, const Pn & ... pn) +		{ +			s << p.substr(0, l); +			StreamWriter::next(s, pn...); +		} +	}; + +	// Flags +#define FLAGCONV(OP, ...) \ +	StreamWriterT(__VA_ARGS__) { \ +		template<typename ... Pn> static inline void write(stream & s, const Pn & ... pn) { \ +			OP; \ +			StreamWriter<S, L, pos + 1, stream, '%', sn...>::write(s, pn...); \ +		} \ +	}; +	FLAGCONV(s << std::showbase, '#'); +	FLAGCONV(s.setfill('0'), '0'); +	FLAGCONV(s << std::left, '-'); +	FLAGCONV(s.setfill(' '), ' '); +#undef FLAGCONV +} + +#endif + diff --git a/libadhocutil/unittests/testCompileTimeFormatter.cpp b/libadhocutil/unittests/testCompileTimeFormatter.cpp index a04b92e..6ad491d 100644 --- a/libadhocutil/unittests/testCompileTimeFormatter.cpp +++ b/libadhocutil/unittests/testCompileTimeFormatter.cpp @@ -308,3 +308,108 @@ BOOST_AUTO_TEST_CASE( filestar )  	free(buf);  } +// The following tests represent CTF's [partial] emulation of many +// POSIX formatting features +#define GLIBC_FMT_TEST(NAME, FMT, ...) \ +	AdHocFormatter(NAME ## fmtr, FMT); \ +	BOOST_AUTO_TEST_CASE(NAME ## t) { \ +		BOOST_TEST_CONTEXT(FMT) { \ +			auto str = NAME ## fmtr::get(__VA_ARGS__); \ +			char * buf = NULL; \ +			int len = asprintf(&buf, FMT, __VA_ARGS__); \ +			BOOST_REQUIRE(buf); \ +			BOOST_CHECK_EQUAL(str.length(), len); \ +			BOOST_CHECK_EQUAL(str, buf); \ +			free(buf); \ +		} \ +	} + +GLIBC_FMT_TEST(s1, "in %s.", "string"); +GLIBC_FMT_TEST(s2, "in %s %s.", "string", "other"); +GLIBC_FMT_TEST(s3, "in %.*s.", 3, "other"); +GLIBC_FMT_TEST(s4, "in %.*s.", 5, "other"); +GLIBC_FMT_TEST(s5, "in %.*s.", 7, "other"); + +GLIBC_FMT_TEST(c1, "in %c.", 'b'); +GLIBC_FMT_TEST(c2, "in %c.", 'B'); + +GLIBC_FMT_TEST(d1, "in %d.", 123); +GLIBC_FMT_TEST(d2, "in %d.", 123456); +GLIBC_FMT_TEST(d3, "in %hd.", -12345); +GLIBC_FMT_TEST(d4, "in %hhd.", -123); +GLIBC_FMT_TEST(d5, "in %ld.", -123456L); +GLIBC_FMT_TEST(d6, "in %lld.", -123456LL); +GLIBC_FMT_TEST(i1, "in %i.", 123); +GLIBC_FMT_TEST(i2, "in %i.", -123); + +GLIBC_FMT_TEST(x1, "in %x.", 123); +GLIBC_FMT_TEST(x2, "in %x %d.", 123, 256); +GLIBC_FMT_TEST(x3, "in %d %x.", 123, 1024); +GLIBC_FMT_TEST(x4, "in %X %x.", 123, 13); +GLIBC_FMT_TEST(x5, "in %X %s.", 123, "miXED case after UPPER X"); +GLIBC_FMT_TEST(x6, "in %#x.", 123); +GLIBC_FMT_TEST(x7, "in %#X.", 123); +GLIBC_FMT_TEST(x8, "in %#X %x.", 123, 150); + +GLIBC_FMT_TEST(o1, "in %o.", 123); +GLIBC_FMT_TEST(o2, "in %o %d.", 123, 256); +GLIBC_FMT_TEST(o3, "in %d %o.", 123, 1024); + +GLIBC_FMT_TEST(a1, "in %a.", 123.456789); +GLIBC_FMT_TEST(a2, "in %a.", -123.456789); +GLIBC_FMT_TEST(a3, "in %a.", .123456789); +GLIBC_FMT_TEST(a4, "in %a.", -.123456789); +GLIBC_FMT_TEST(a5, "in %A.", -.123456789); +GLIBC_FMT_TEST(a6, "in %A.", -123.456789); +GLIBC_FMT_TEST(a7, "in %a.", 123456789.123); +GLIBC_FMT_TEST(a8, "in %a.", -123456789.123); + +GLIBC_FMT_TEST(e1, "in %e.", 123.456789); +GLIBC_FMT_TEST(e2, "in %e.", -123.456789); +GLIBC_FMT_TEST(e3, "in %e.", .123456789); +GLIBC_FMT_TEST(e4, "in %e.", -.123456789); +GLIBC_FMT_TEST(e5, "in %E.", -.123456789); +GLIBC_FMT_TEST(e6, "in %E.", -123.456789); +GLIBC_FMT_TEST(e7, "in %e.", 123456789.123); +GLIBC_FMT_TEST(e8, "in %e.", -123456789.123); + +GLIBC_FMT_TEST(f1, "in %f.", 123.456789); +GLIBC_FMT_TEST(f2, "in %f.", -123.456789); +GLIBC_FMT_TEST(f3, "in %f.", .123456789); +GLIBC_FMT_TEST(f4, "in %f.", -.123456789); +GLIBC_FMT_TEST(f5, "in %F.", -.123456789); +GLIBC_FMT_TEST(f6, "in %F.", -123.456789); +GLIBC_FMT_TEST(f7, "in %f.", 123456789.123); +GLIBC_FMT_TEST(f8, "in %f.", -123456789.123); + +GLIBC_FMT_TEST(g1, "in %g.", 123.456789); +GLIBC_FMT_TEST(g2, "in %g.", -123.456789); +GLIBC_FMT_TEST(g3, "in %g.", .123456789); +GLIBC_FMT_TEST(g4, "in %g.", -.123456789); +GLIBC_FMT_TEST(g5, "in %G.", -.123456789); +GLIBC_FMT_TEST(g6, "in %G.", -123.456789); +GLIBC_FMT_TEST(g7, "in %g.", 123456789.123); +GLIBC_FMT_TEST(g8, "in %g.", -123456789.123); + +AdHocFormatter(chars_written_fmt, "%n %s %n %d %n"); +BOOST_AUTO_TEST_CASE(chars_written) +{ +	int a = -1, b = -1, c = -1; +	auto s = chars_written_fmt::get(&a, "some string", &b, 10, &c); +	BOOST_CHECK_EQUAL(s, " some string  10 "); +	BOOST_CHECK_EQUAL(a, 0); +	BOOST_CHECK_EQUAL(b, s.length() - 4); +	BOOST_CHECK_EQUAL(c, s.length()); +} + +GLIBC_FMT_TEST(p2, "in %p.", this); + +AdHocFormatter(smartptr_fmt, "Address is %p."); +BOOST_AUTO_TEST_CASE(smartptr) +{ +	auto uni = std::make_unique<int>(42); +	smartptr_fmt::get(uni); +	auto shrd = std::make_shared<int>(42); +	smartptr_fmt::get(shrd); +} +  | 
