summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2018-07-04 21:13:41 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2018-07-04 21:13:41 +0100
commit72f85f4d49862b67ebfdd404d8f9451d0a277abf (patch)
tree5f714f097bafde42af1aba2fb6f0e2569514e8ef
parentAny stream type with CTF (diff)
downloadlibadhocutil-72f85f4d49862b67ebfdd404d8f9451d0a277abf.tar.bz2
libadhocutil-72f85f4d49862b67ebfdd404d8f9451d0a277abf.tar.xz
libadhocutil-72f85f4d49862b67ebfdd404d8f9451d0a277abf.zip
CTF printf
Adds support for a lot (but not all) of printf like formatters
-rw-r--r--libadhocutil/compileTimeFormatter.h8
-rw-r--r--libadhocutil/detail/compileTimeFormatters.h128
-rw-r--r--libadhocutil/unittests/testCompileTimeFormatter.cpp105
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);
+}
+