fork(3) download
  1. #include <cstddef>
  2. #include <cstdio>
  3. #include <stdexcept>
  4. #include <boost/utility/string_ref.hpp>
  5. #include <boost/format.hpp>
  6.  
  7. #ifndef BOOST_PP_VARIADICS
  8. # define BOOST_PP_VARIADICS
  9. #endif
  10. #include <boost/preprocessor.hpp>
  11.  
  12. template<typename... Ts>
  13. struct Format
  14. {
  15. template<std::size_t N>
  16. static constexpr bool check(const char (&fmt)[N], std::size_t n);
  17. };
  18.  
  19. //////////////////////
  20.  
  21. template<std::size_t N>
  22. constexpr bool checkValidFormats(const char (&fmt)[N], size_t n, char c)
  23. {
  24. return n >= N ?
  25. throw std::logic_error("invalid format for type")
  26. : fmt[n] == c ?
  27. true
  28. : checkValidFormats(fmt, n + 1, c);
  29. }
  30.  
  31. template<class>
  32. struct Type;
  33.  
  34. #define SUPPORTED_TYPE(T, Fmts) \
  35. template<> \
  36. struct Type<T> \
  37. { \
  38.   template<std::size_t N> \
  39.   constexpr static bool check(const char (&fmt)[N], std::size_t n) \
  40.   { \
  41.   return n >= N ? \
  42.   throw std::logic_error("invalid format for type") \
  43.   : checkValidFormats(Fmts, 0, fmt[n]); \
  44.   } \
  45. }
  46.  
  47. SUPPORTED_TYPE(char, "c");
  48. SUPPORTED_TYPE(int8_t, "cd");
  49. SUPPORTED_TYPE(uint8_t, "cu");
  50. SUPPORTED_TYPE(int16_t, "d");
  51. SUPPORTED_TYPE(uint16_t, "u");
  52. SUPPORTED_TYPE(int32_t, "d");
  53. SUPPORTED_TYPE(uint32_t, "u");
  54. SUPPORTED_TYPE(char*, "s");
  55. SUPPORTED_TYPE(unsigned char*, "s");
  56. SUPPORTED_TYPE(const char*, "s");
  57. SUPPORTED_TYPE(std::string, "s");
  58. SUPPORTED_TYPE(boost::string_ref, "s");
  59. SUPPORTED_TYPE(double, "f");
  60. SUPPORTED_TYPE(float, "f");
  61.  
  62. #define SUPPORTED_LL_TYPE(T, C) \
  63. template<> \
  64. struct Type<T> \
  65. { \
  66.   template<std::size_t N> \
  67.   static constexpr bool check(const char (&fmt)[N], std::size_t n) \
  68.   { \
  69.   return n < N && \
  70.   n - 2 >= 0 && \
  71.   fmt[n] == C && \
  72.   fmt[n - 1] == 'l' && \
  73.   fmt[n - 2] == 'l' ? \
  74.   true \
  75.   : throw std::logic_error("invalid format for type"); \
  76.   } \
  77. }
  78.  
  79. SUPPORTED_LL_TYPE(int64_t, 'd');
  80. SUPPORTED_LL_TYPE(uint64_t, 'u');
  81.  
  82. template<typename... Ts>
  83. struct Argument
  84. {
  85. template<std::size_t N>
  86. static constexpr bool check(const char (&)[N], std::size_t)
  87. {
  88. return false;
  89. }
  90. };
  91.  
  92. template<typename T, typename... Ts>
  93. struct Argument<T, Ts...>
  94. {
  95. template<std::size_t N>
  96. static constexpr bool check(const char (&fmt)[N], std::size_t n)
  97. {
  98. // %[<flags>][<width>][.<precision>][<length>]<specifier>
  99. // specifier := d|i|u|o|x|X|f|F|e|E|g|G|a|A|c|s|p|n
  100. return Type< typename std::decay<T>::type>::check(fmt, n) &&
  101. Format<Ts...>::check(fmt, n + 1);
  102. }
  103. };
  104.  
  105. ///////////////////////////
  106.  
  107. template<size_t N>
  108. constexpr bool isDoubleLengthSpecifier(const char (&fmt)[N], std::size_t n)
  109. {
  110. // hh | ll
  111. return n + 2 < N &&
  112. ((fmt[n] == 'h' && fmt[n + 1] == 'h') ||
  113. (fmt[n] == 'l' && fmt[n + 1] == 'l'));
  114. }
  115.  
  116. template<size_t N>
  117. constexpr bool isSingleLengthSpecifier(const char (&fmt)[N], std::size_t n)
  118. {
  119. // h | l | j | z | t | L
  120. return n + 1 < N &&
  121. (fmt[n] == 'h' ||
  122. fmt[n] == 'l' ||
  123. fmt[n] == 'j' ||
  124. fmt[n] == 'z' ||
  125. fmt[n] == 't' ||
  126. fmt[n] == 'L');
  127. }
  128.  
  129. template<size_t N>
  130. constexpr size_t nextNonLengthSpecifier(const char (&fmt)[N], std::size_t n)
  131. {
  132. return
  133. isDoubleLengthSpecifier(fmt, n) ? n + 2
  134. : isSingleLengthSpecifier(fmt, n) ? n + 1
  135. : n;
  136. }
  137.  
  138. template<typename... Ts>
  139. struct Length
  140. {
  141. template<std::size_t N>
  142. static constexpr bool check(const char (&)[N], std::size_t)
  143. {
  144. return false;
  145. }
  146. };
  147.  
  148. template<typename T, typename... Ts>
  149. struct Length<T, Ts...>
  150. {
  151. template<std::size_t N>
  152. static constexpr bool check(const char (&fmt)[N], std::size_t n)
  153. {
  154. // %[<flags>][<width>][.<precision>][<length>]<specifier>
  155. // length := hh|h|l|ll|j|z|t|L
  156. return Argument<T, Ts...>::check(fmt, nextNonLengthSpecifier(fmt, n));
  157. }
  158. };
  159.  
  160. ///////////////////////////
  161.  
  162. template<std::size_t N>
  163. constexpr size_t nextNonLiteralPrecision(const char (&fmt)[N], std::size_t n)
  164. {
  165. return
  166. n >= N ?
  167. throw std::logic_error("invalid format string - parsing precision")
  168. : fmt[n] >= '0' && fmt[n] <= '9' ?
  169. nextNonLiteralPrecision(fmt, n + 1)
  170. : n;
  171. }
  172.  
  173. template<typename... Ts>
  174. struct Precision
  175. {
  176. template<std::size_t N>
  177. static constexpr bool check(const char (&)[N], std::size_t)
  178. {
  179. return false;
  180. }
  181. };
  182.  
  183. template<typename T, typename... Ts>
  184. struct Precision<T, Ts...>
  185. {
  186. template<std::size_t N>
  187. static constexpr bool check(const char (&fmt)[N], std::size_t n)
  188. {
  189. // %[<flags>][<width>][.<precision>][<length>]<specifier>
  190. // precision := <number>|'*' // A number or a '*'
  191.  
  192. // if precision is a provided argument, validate it is integral
  193. return n + 1 < N && fmt[n] == '.' && fmt[n + 1] == '*' ?
  194. std::is_integral<T>::value && Length<Ts...>::check(fmt, n + 2)
  195.  
  196. // otherwise skip over any literal precision
  197. : n + 1 < N && fmt[n] == '.' ?
  198. Length<T, Ts...>::check(fmt, nextNonLiteralPrecision(fmt, n + 1))
  199.  
  200. : Length<T, Ts...>::check(fmt, n);
  201. }
  202. };
  203.  
  204. ///////////////////////////
  205.  
  206. template<std::size_t N>
  207. constexpr size_t nextNonLiteralWidth(const char (&fmt)[N], std::size_t n)
  208. {
  209. return
  210. n >= N ?
  211. throw std::logic_error("invalid format string - parsing width")
  212. : fmt[n] >= '0' && fmt[n] <= '9' ?
  213. nextNonLiteralWidth(fmt, n + 1)
  214. : n;
  215. }
  216.  
  217. template<typename... Ts>
  218. struct Width
  219. {
  220. template<std::size_t N>
  221. static constexpr bool check(const char (&)[N], std::size_t)
  222. {
  223. return false;
  224. }
  225. };
  226.  
  227. template<typename T, typename... Ts>
  228. struct Width<T, Ts...>
  229. {
  230. template<std::size_t N>
  231. static constexpr bool check(const char (&fmt)[N], std::size_t n)
  232. {
  233. // %[<flags>][<width>][.<precision>][<length>]<specifier>
  234. // width := <number>|'*' // A number or a '*'
  235.  
  236. // if width is a provided argument, validate it is integral
  237. return fmt[n] == '*' ?
  238. std::is_integral<T>::value && Precision<Ts...>::check(fmt, n + 1)
  239.  
  240. // otherwise skip over any literal width
  241. : Precision<T, Ts...>::check(fmt, nextNonLiteralWidth(fmt, n));
  242. }
  243. };
  244.  
  245. ///////////////////////////
  246.  
  247. template<size_t N>
  248. constexpr bool isFlag(const char (&fmt)[N], std::size_t n)
  249. {
  250. return n + 1 < N &&
  251. (fmt[n] == '-' ||
  252. fmt[n] == '+' ||
  253. fmt[n] == ' ' ||
  254. fmt[n] == '#' ||
  255. fmt[n] == '0');
  256. }
  257.  
  258. template<std::size_t N>
  259. constexpr size_t nextNonFlag(const char (&fmt)[N], std::size_t n)
  260. {
  261. return
  262. n >= N ?
  263. throw std::logic_error("invalid format string")
  264. : isFlag(fmt, n) ?
  265. nextNonFlag(fmt, n + 1)
  266. : n;
  267. }
  268.  
  269. template<typename T, typename... Ts>
  270. struct Flags
  271. {
  272. template<std::size_t N>
  273. static constexpr bool check(const char (&fmt)[N], std::size_t n)
  274. {
  275. // %[<flags>][<width>][.<precision>][<length>]<specifier>
  276. // flags := [-+ #0]* // Zero or more
  277. return Width<T, Ts...>::check(fmt, nextNonFlag(fmt, n));
  278. }
  279. };
  280.  
  281. ///////////////////////////
  282.  
  283. template<size_t N>
  284. constexpr bool isLiteralPercent(const char (&fmt)[N], std::size_t n)
  285. {
  286. return n + 1 <= N && fmt[n] == '%' && fmt[n + 1] == '%';
  287. }
  288.  
  289. template<typename T, typename... Ts>
  290. struct Format<T, Ts...>
  291. {
  292. template<std::size_t N>
  293. static constexpr bool check(const char (&fmt)[N], std::size_t n)
  294. {
  295. return
  296. n >= N ?
  297. throw std::logic_error("too many arguments for provided format string")
  298.  
  299. // skip non-format specifiers (ie: not a % character)
  300. : fmt[n] != '%' ?
  301. Format<T, Ts...>::check(fmt, n + 1)
  302.  
  303. // %%
  304. : isLiteralPercent(fmt, n) ?
  305. Format<T, Ts...>::check(fmt, n + 2)
  306.  
  307. // we've found a format specifier
  308. : Flags<T, Ts...>::check(fmt, n + 1);
  309. }
  310. };
  311.  
  312. template<>
  313. struct Format<>
  314. {
  315. template<std::size_t N>
  316. static constexpr bool check(const char (&fmt)[N], std::size_t n)
  317. {
  318. return
  319. n>= N ?
  320. true
  321. : fmt[n] != '%' ?
  322. check(fmt, n + 1)
  323. : fmt[n + 1] == '%' ?
  324. check(fmt, n + 2)
  325. : throw std::logic_error("too few arguments for provided format string");
  326. }
  327. };
  328.  
  329. ////////////////
  330.  
  331. // printing...
  332.  
  333. void add(boost::format&)
  334. { }
  335.  
  336. template<typename T, typename... Ts>
  337. void add(boost::format& f, const T& arg, const Ts&... ts)
  338. {
  339. f % arg;
  340. add(f, ts...);
  341. }
  342.  
  343. ////////////////
  344.  
  345. #define PP_PARENTHESISE_WITH_TOKEN(r, token, i, e) \
  346.   BOOST_PP_COMMA_IF(i) token(e)
  347.  
  348. #define PP_CSV_SEQ_PARENTHESISE_WITH_TOKEN(...) \
  349.   BOOST_PP_SEQ_FOR_EACH_I(PP_PARENTHESISE_WITH_TOKEN, decltype, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
  350.  
  351. #define PP_PERFORM_LOG_FORMAT_CHECK(fmt, ...) \
  352.   Format<BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE((,##__VA_ARGS__)), 1), \
  353.   BOOST_PP_EXPAND, PP_CSV_SEQ_PARENTHESISE_WITH_TOKEN)(__VA_ARGS__)>::check(fmt, 0)
  354.  
  355. #define LOG(fmt, ...) \
  356.   { \
  357.   static_assert(PP_PERFORM_LOG_FORMAT_CHECK(fmt, ##__VA_ARGS__), ""); \
  358.   boost::format f(fmt); \
  359.   add(f, ##__VA_ARGS__); \
  360.   std::cout << f.str() << std::endl; \
  361.   }
  362.  
  363. int main()
  364. {
  365. // nothing
  366. LOG("hello world");
  367.  
  368. // char
  369. LOG("%c", 'x');
  370.  
  371. // integral
  372. LOG("%d", -123);
  373. LOG("%ld", -123);
  374. LOG("%u", 123u);
  375. LOG("%lu", 123u);
  376.  
  377. // strings
  378. LOG("%s", "hello world");
  379. LOG("%-s", "hello world");
  380. LOG("%s", std::string("hello world"));
  381. { const char* s = "hello world"; LOG("%s", s); }
  382. { std::string s = "hello world"; LOG("%s", s); }
  383. { std::string s = "hello world"; boost::string_ref r(s); LOG("%s", r); }
  384.  
  385. // floating point
  386. LOG("%f", 1.23);
  387. LOG("%f", 1.23f);
  388.  
  389. // width & precision
  390. LOG("%02d", 1);
  391. LOG("% 3s", "hello");
  392. LOG("% 3s", "yo");
  393. LOG("%.2d", 123);
  394. LOG("%.2f", 1.2345);
  395. LOG("%2f", 1.23456789);
  396. LOG("%02f", 0.1);
  397. LOG("%02.2f", 0.1);
  398.  
  399. // width & precision as arguments
  400. // not supported by boost::format
  401. // LOG("%*d", 3, 12);
  402. // LOG("%.*s", 3, "hello");
  403. // LOG("%.*d", 3, 12345);
  404. // LOG("%*.*s", 3, 3, "hello");
  405. // LOG("%*.*d", 3, 3, 12345);
  406.  
  407. // mix of multiple different arguments
  408. LOG("%s", "hi");
  409. LOG("%s %d", "hi", 1);
  410. LOG("%s %d %u %lf %f %c", "hi", -1, 12u, 1.23, 1.33, 'c');
  411.  
  412. // too few arguments
  413. // LOG("%s %d %u %lf %f %c", "hi", -1, 12u, 1.23, 1.33);
  414.  
  415. // too many arguments
  416. // LOG("%s %d %u %lf %f %c", "hi", -1, 12u, 1.23, 1.33, 'c', 1);
  417.  
  418. // incorrect argument for format string
  419. // LOG("%s %d %u %lf %f %c", "hi", -1, 12u, 1, 1.33, 'c');
  420. }
Success #stdin #stdout 0s 3356KB
stdin
Standard input is empty
stdout
hello world
x
-123
-123
123
123
hello world
hello world
hello world
hello world
hello world
hello world
1.230000
1.230000
01
 hello
 yo
123
1.23
1.234568
0.100000
0.10
hi
hi 1
hi -1 12 1.230000 1.330000 c