fork(5) download
  1. #include <stdexcept>
  2. #include <boost/format.hpp>
  3. #include <boost/utility/string_ref.hpp>
  4.  
  5. template<std::size_t N>
  6. constexpr bool checkValidFormats(const char (&fmt)[N], size_t n, char c)
  7. {
  8. return n >= N ?
  9. false
  10. : fmt[n] == c ?
  11. true
  12. : checkValidFormats(fmt, n + 1, c);
  13. }
  14.  
  15. template<class>
  16. struct FormatSupportedType;
  17.  
  18. #define SUPPORTED_TYPE(T, Fmts) \
  19. template<> \
  20. struct FormatSupportedType<T> \
  21. { \
  22.   constexpr static bool supports(char c) \
  23.   { return checkValidFormats(Fmts, 0, c) \
  24.   ? true : throw std::logic_error("invalid fmt for type"); } \
  25. }
  26.  
  27. SUPPORTED_TYPE(char, "c");
  28. SUPPORTED_TYPE(int, "d*");
  29. SUPPORTED_TYPE(unsigned, "u*");
  30. SUPPORTED_TYPE(char*, "s");
  31. SUPPORTED_TYPE(const char*, "s");
  32. SUPPORTED_TYPE(std::string, "s");
  33. SUPPORTED_TYPE(boost::string_ref, "s");
  34. SUPPORTED_TYPE(double, "f");
  35. SUPPORTED_TYPE(float, "f");
  36.  
  37. /////////////////
  38.  
  39. constexpr bool isDigit(char c)
  40. {
  41. return c >= '0' && c <= '9';
  42. }
  43.  
  44. constexpr bool isModifier(char c)
  45. {
  46. return c == 'l' ||
  47. c == 'h' ||
  48. c == 'j' ||
  49. c == 'z' ||
  50. c == 't' ||
  51. c == 'L' ||
  52. c == '#' ||
  53. c == '+' ||
  54. c == '-' ||
  55. c == ' ' ||
  56. c == '\'' ||
  57. c == 'I' ||
  58. c == '.' ||
  59. c == '=' ||
  60. isDigit(c);
  61. }
  62.  
  63. template<std::size_t N>
  64. constexpr size_t nextNonModifier(const char (&fmt)[N], std::size_t n)
  65. {
  66. return
  67. n >= N ?
  68. throw std::logic_error("invalid format string")
  69. : isModifier(fmt[n]) ?
  70. nextNonModifier(fmt, n + 1)
  71. : n;
  72. }
  73.  
  74. ////////////////////
  75.  
  76. template<std::size_t N>
  77. constexpr bool checkFormatHelper(const char (&fmt)[N], std::size_t n);
  78. template<std::size_t N, class T, class... Ts>
  79. constexpr bool checkFormatHelper(const char (&fmt)[N], std::size_t n, const T& arg, const Ts&... args);
  80.  
  81. ////////////////////
  82.  
  83. template<std::size_t N, typename T1, typename T2, typename T3, typename... Ts>
  84. constexpr auto checkWidthAndPrecision(const char (&fmt)[N], std::size_t n, const T1& /*width*/, const T2& /*precision*/, const T3& /* arg */, const Ts&... args)
  85. -> typename std::enable_if<
  86. std::is_integral<T1>::value &&
  87. std::is_integral<T2>::value,
  88. bool>::type
  89. {
  90. return FormatSupportedType< typename std::decay<T3>::type>::supports(fmt[n]) &&
  91. checkFormatHelper(fmt, n + 1, args...);
  92. }
  93.  
  94. template<std::size_t N, typename... Ts>
  95. constexpr bool checkWidthAndPrecision(const char (&)[N], std::size_t, const Ts&...)
  96. {
  97. return false;
  98. }
  99.  
  100. ////////////////////
  101.  
  102. template<std::size_t N, typename T1, typename T2, typename... Ts>
  103. constexpr auto checkWidthOrPrecision(const char (&fmt)[N], std::size_t n, const T1& /*precision*/, const T2& /* arg */, const Ts&... args)
  104. -> typename std::enable_if<
  105. std::is_integral<T1>::value,
  106. bool>::type
  107. {
  108. return FormatSupportedType< typename std::decay<T2>::type>::supports(fmt[n]) &&
  109. checkFormatHelper(fmt, n + 1, args...);
  110. }
  111.  
  112. template<std::size_t N, typename... Ts>
  113. constexpr bool checkWidthOrPrecision(const char (&)[N], std::size_t, const Ts&...)
  114. {
  115. return false;
  116. }
  117.  
  118. ////////////////////
  119.  
  120. template<std::size_t N>
  121. constexpr bool checkFormatHelper(const char (&fmt)[N], std::size_t n)
  122. {
  123. return
  124. n>= N ?
  125. true
  126. : fmt[n] != '%' ?
  127. checkFormatHelper(fmt, n + 1)
  128. : fmt[n + 1] == '%' ?
  129. checkFormatHelper(fmt, n + 2)
  130. : false;
  131. }
  132.  
  133. template<std::size_t N, class T, class... Ts>
  134. constexpr bool checkFormatHelper(const char (&fmt)[N], std::size_t n, const T& arg, const Ts&... args)
  135. {
  136. return
  137. n >= N ?
  138. throw std::logic_error("too many arguments for provided format string")
  139.  
  140. : fmt[n] != '%' ?
  141. checkFormatHelper(fmt, n + 1, arg, args...)
  142.  
  143. // literal percent character
  144. : (fmt[n + 1] == '%') ?
  145. checkFormatHelper(fmt, n + 2, arg, args...)
  146.  
  147. // long-long modifier
  148. : (fmt[n + 1] == 'l' && fmt[n + 2] == 'l') ?
  149. FormatSupportedType< typename std::decay<T>::type >::supports(fmt[n + 3]) &&
  150. checkFormatHelper(fmt, n + 4, args...)
  151.  
  152. // width & precision modifier
  153. : (fmt[n + 1] == '*' && fmt[n + 2] == '.' && fmt[n + 3] == '*') ?
  154. checkWidthAndPrecision(fmt, n + 4, arg, args...)
  155.  
  156. // width or precision modifier
  157. : ((fmt[n + 1] == '.' && fmt[n + 2] == '*') || (fmt[n + 1] == '*')) ?
  158. checkWidthOrPrecision(fmt, (fmt[n + 1] == '.' ? n + 3 : n + 2), arg, args...)
  159.  
  160. // other modifier
  161. : (isModifier(fmt[n + 1])) ?
  162. FormatSupportedType< typename std::decay<T>::type>::supports(fmt[nextNonModifier(fmt, n + 2)]) &&
  163. checkFormatHelper(fmt, nextNonModifier(fmt, n + 2) + 1, args...)
  164.  
  165. // no modifier
  166. : FormatSupportedType< typename std::decay<T>::type>::supports(fmt[n + 1]) &&
  167. checkFormatHelper(fmt, n + 2, args...);
  168. }
  169.  
  170. template<std::size_t N, class... Ts>
  171. constexpr bool checkFormat(const char (&fmt)[N], const Ts&... args)
  172. {
  173. return checkFormatHelper(fmt, 0, args...);
  174. }
  175.  
  176. // printing...
  177.  
  178. void add(boost::format&)
  179. { }
  180.  
  181. template<typename T, typename... Ts>
  182. void add(boost::format& f, const T& arg, const Ts&... ts)
  183. {
  184. f % arg;
  185. add(f, ts...);
  186. }
  187.  
  188. #define LOG(fmt, ...) \
  189.   { \
  190.   static_assert(checkFormat(fmt, ##__VA_ARGS__), "Format is incorrect"); \
  191.   boost::format f(fmt); \
  192.   add(f, ##__VA_ARGS__); \
  193.   std::cout << f.str() << std::endl; \
  194.   }
  195.  
  196. int main()
  197. {
  198. // char
  199. LOG("%c", 'x');
  200.  
  201. // integral
  202. LOG("%d", -123);
  203. LOG("%ld", -123);
  204. LOG("%u", 123u);
  205. LOG("%lu", 123u);
  206.  
  207. // strings
  208. LOG("%s", "hello world");
  209. { const char* s = "hello world"; LOG("%s", s); }
  210. { std::string s = "hello world"; LOG("%s", s); }
  211. { std::string s = "hello world"; boost::string_ref r(s); LOG("%s", r); }
  212.  
  213. // floating point
  214. LOG("%f", 1.23);
  215. LOG("%f", 1.23f);
  216.  
  217. // width / precision
  218. LOG("%02d", 1);
  219. LOG("%.2d", 123);
  220. LOG("% 3s", "hello");
  221. LOG("% 3s", "yo");
  222. LOG("%.3s", "hello");
  223. LOG("%.3s", "yo");
  224.  
  225. // incorrect format string
  226. // LOG("%f", 1);
  227. // LOG("%d", 1.23);
  228.  
  229. // not supported by boost::format
  230. // LOG("%*s", 3, "yo");
  231. // LOG("%*d", 3, 12);
  232. // LOG("%.*s", 3, "hello");
  233. // LOG("%.*d", 3, 12345);
  234. // LOG("%*.*s", 3, 3, "hello");
  235. // LOG("%*.*d", 3, 3, 12345);
  236. }
  237.  
Compilation error #stdin compilation error #stdout 0s 3360KB
stdin
Standard input is empty
compilation info
prog.cpp: In function 'int main()':
prog.cpp:190:9: error: non-constant condition for static assertion
         static_assert(checkFormat(fmt, ##__VA_ARGS__), "Format is incorrect"); \
         ^
prog.cpp:226:5: note: in expansion of macro 'LOG'
     LOG("%f", 1);
     ^
prog.cpp:226:5:   in constexpr expansion of 'checkFormat<3u, {int}>((*"%f"), (* &1))'
prog.cpp:173:45:   in constexpr expansion of 'checkFormatHelper<3u, int, {}>((* & fmt), 0u, (* & args#0))'
prog.cpp:166:82:   in constexpr expansion of 'FormatSupportedType<int>::supports(((int)fmt[(n + 1u)]))'
prog.cpp:24:67: error: expression '<throw-expression>' is not a constant-expression
             ? true : throw std::logic_error("invalid fmt for type"); } \
                                                                   ^
prog.cpp:28:1: note: in expansion of macro 'SUPPORTED_TYPE'
 SUPPORTED_TYPE(int,               "d*");
 ^
stdout
Standard output is empty