/prelude/type_strings.hpp

http://github.com/Eelis/geordi · C++ Header · 501 lines · 362 code · 118 blank · 21 comment · 25 complexity · 4bf37d431acf1d4a91180520f0d5b7c5 MD5 · raw file

  1. // Todo: Automatic parentheses placement.
  2. #ifndef TYPE_STRINGS_HPP
  3. #define TYPE_STRINGS_HPP
  4. #if __cplusplus >= 201103
  5. #include "lvalue_rvalue.hpp"
  6. #endif
  7. #include <list>
  8. #include <map>
  9. #include <string>
  10. #include <vector>
  11. #include <stack>
  12. #include <queue>
  13. #include <set>
  14. #include <utility>
  15. #include <deque>
  16. #include <iostream>
  17. #include <fstream>
  18. #include <sstream>
  19. #include <ios>
  20. #include <cassert>
  21. #include <memory>
  22. #include <cstring>
  23. #if __cplusplus >= 201103
  24. #include <unordered_map>
  25. #include <unordered_set>
  26. #include <tuple>
  27. #include <array>
  28. #include <type_traits>
  29. #include "tlists.hpp"
  30. #endif
  31. #if __cplusplus >= 201500
  32. #include <experimental/type_traits>
  33. #endif
  34. namespace type_strings_detail {
  35. using std::basic_ostream;
  36. template <typename I>
  37. std::string commas_and (I b, I e)
  38. {
  39. assert(b != e);
  40. if (e - b == 3) { std::string r = *b++; r += ", " + *b++; return r + ", and " + *b; }
  41. else if (e - b == 2) { I c = b; ++c; return *b + " and " + *c; }
  42. else if (e - b == 1) return *b;
  43. else { I c = b; ++c; return *b + ", " + commas_and(c, e); }
  44. }
  45. // Type strings in ordinary C++ syntax
  46. template <typename T>
  47. char const * helper()
  48. {
  49. return __PRETTY_FUNCTION__ + sizeof(
  50. #ifdef __clang__
  51. "const char* type_strings_detail::helper() [T = "
  52. #else
  53. "const char* type_strings_detail::helper() [with T = "
  54. #endif
  55. ) - 1;
  56. }
  57. template <typename T> std::string type() {
  58. char const * const p = helper<T>();
  59. return std::string(p, std::strlen(p) - sizeof(']'));
  60. }
  61. #define TYPEDEF_TYPE(n) template <> inline std::string type<std::n> () { return #n; }
  62. TYPEDEF_TYPE(ios)
  63. TYPEDEF_TYPE(fstream)
  64. TYPEDEF_TYPE(ifstream)
  65. TYPEDEF_TYPE(ofstream)
  66. TYPEDEF_TYPE(ostringstream)
  67. TYPEDEF_TYPE(istringstream)
  68. TYPEDEF_TYPE(stringstream)
  69. TYPEDEF_TYPE(streambuf)
  70. TYPEDEF_TYPE(iostream)
  71. TYPEDEF_TYPE(ostream)
  72. TYPEDEF_TYPE(istream)
  73. TYPEDEF_TYPE(string)
  74. TYPEDEF_TYPE(wstring)
  75. #undef TYPEDEF_TYPE
  76. // Verbose type description strings
  77. template <typename> std::string type_desc (bool plural = false);
  78. #if __cplusplus >= 201103
  79. namespace textual_type_descriptions
  80. {
  81. inline std::string pl (std::string const & s, bool const b) { return s + (b ? "s" : ""); }
  82. template <typename T> std::string many () { return type_desc<T>(true); }
  83. template <typename> struct type_desc_t;
  84. template <typename T> std::string an ()
  85. { return (type_desc_t<T>::vowel ? "an " : "a ") + type_desc<T>(); }
  86. template <typename T> std::string an_or_many (bool const plural = false)
  87. { if (plural) return many<T>(); else return an<T>(); }
  88. inline std::string to_string(int i)
  89. {
  90. std::ostringstream s;
  91. s << i;
  92. return s.str();
  93. }
  94. template <typename T> std::string count (size_t const i)
  95. { return to_string(i) + " " + type_desc<T>(i != 1); }
  96. template <typename T> std::string an_or_count (size_t const i) { return i == 1 ? an<T>() : count<T>(i); }
  97. struct Vowel { enum { vowel = true }; };
  98. struct consonant { enum { vowel = false }; };
  99. template <typename T> struct type_desc_t: consonant
  100. { static std::string s (bool b) { return pl(type<T>(), b); } };
  101. template <typename T> std::string returning (bool const plural)
  102. { return "returning " + an_or_many<T>(plural); }
  103. template <> inline std::string returning<void> (bool) { return "returning nothing"; }
  104. // built-in types
  105. #define DEF_BUILTIN_SPEC(type, str, vow) \
  106. template <> struct type_desc_t<type> \
  107. { static std::string s (bool plural) { return pl(str, plural); } enum { vowel = vow }; };
  108. DEF_BUILTIN_SPEC(bool, "boolean", false)
  109. DEF_BUILTIN_SPEC(char, "character", false)
  110. DEF_BUILTIN_SPEC(signed char, "signed character", false)
  111. DEF_BUILTIN_SPEC(unsigned char, "unsigned character", true)
  112. DEF_BUILTIN_SPEC(wchar_t, "wide character", false)
  113. DEF_BUILTIN_SPEC(int, "integer", true)
  114. DEF_BUILTIN_SPEC(long int, "long integer", false)
  115. DEF_BUILTIN_SPEC(unsigned int, "unsigned integer", true)
  116. DEF_BUILTIN_SPEC(long unsigned int, "long unsigned integer", false)
  117. #undef DEF_BUILTIN_SPEC
  118. // stdlib typedefs
  119. #define TYPEDEF_TYPE_DESC(n, pl, vow) \
  120. template <> struct type_desc_t<std::n> \
  121. { static std::string s (bool b) { return b ? #n pl : #n; } enum { vowel = vow }; };
  122. TYPEDEF_TYPE_DESC(ios, "es", true)
  123. TYPEDEF_TYPE_DESC(fstream, "s", false)
  124. TYPEDEF_TYPE_DESC(ifstream, "s", true)
  125. TYPEDEF_TYPE_DESC(ofstream, "s", true)
  126. TYPEDEF_TYPE_DESC(ostringstream, "s", true)
  127. TYPEDEF_TYPE_DESC(istringstream, "s", true)
  128. TYPEDEF_TYPE_DESC(stringstream, "s", false)
  129. TYPEDEF_TYPE_DESC(streambuf, "s", false)
  130. TYPEDEF_TYPE_DESC(iostream, "s", true)
  131. TYPEDEF_TYPE_DESC(ostream, "s", true)
  132. TYPEDEF_TYPE_DESC(istream, "s", true)
  133. TYPEDEF_TYPE_DESC(string, "s", false)
  134. TYPEDEF_TYPE_DESC(wstring, "s", false)
  135. #undef TYPEDEF_TYPE_DESC
  136. // primitive constructs
  137. template <> struct type_desc_t<void>
  138. { static std::string s (bool) { return "void"; } }; // has no plural
  139. template <typename T> struct type_desc_t<T const>: consonant
  140. { static std::string s (bool b) { return "constant " + type_desc<T>(b); } };
  141. template <typename T> struct type_desc_t<T volatile>: consonant
  142. { static std::string s (bool b) { return "volatile " + type_desc<T>(b); } };
  143. template <typename T> struct type_desc_t<T const volatile>: consonant
  144. { static std::string s (bool b) { return "constant volatile " + type_desc<T>(b); } };
  145. template <typename T> struct type_desc_t<T *>: consonant
  146. { static std::string s (bool b) { return pl("pointer", b) + " to " + an_or_many<T>(b); } };
  147. template <> struct type_desc_t<void *>: consonant
  148. { static std::string s (bool b) { return pl("pointer", b) + " to anything"; } };
  149. template <typename T> struct type_desc_t<T &>: Vowel
  150. { static std::string s (bool b) { return pl("lvalue reference", b) + " to " + an_or_many<T>(b); } };
  151. #if __cplusplus >= 201103
  152. template <typename T> struct type_desc_t<T &&>: Vowel
  153. { static std::string s (bool b) { return pl("rvalue reference", b) + " to " + an_or_many<T>(b); } };
  154. #endif
  155. #define TYPE_STRINGS_EMPTY
  156. #define ARRAY_SPEC(cv) \
  157. template <typename T> struct type_desc_t<T cv []>: Vowel \
  158. { static std::string s (bool b) { return pl("array", b) + " of " + many<T cv>(); } };
  159. ARRAY_SPEC(TYPE_STRINGS_EMPTY)
  160. ARRAY_SPEC(const)
  161. ARRAY_SPEC(volatile)
  162. ARRAY_SPEC(const volatile)
  163. #undef ARRAY_SPEC
  164. #define ARRAY_SPEC(cv) \
  165. template <typename T, size_t N> struct type_desc_t<T cv [N]>: Vowel \
  166. { static std::string s (bool b) { return pl("array", b) + " of " + count<T cv>(N); } };
  167. ARRAY_SPEC(TYPE_STRINGS_EMPTY)
  168. ARRAY_SPEC(const)
  169. ARRAY_SPEC(volatile)
  170. ARRAY_SPEC(const volatile)
  171. #undef ARRAY_SPEC
  172. #undef TYPE_STRINGS_EMPTY
  173. template <typename> struct group_list_desc;
  174. template<size_t N, typename T, typename... U>
  175. struct group_list_desc<tlists::tlist<tlists::group<N, T>, U...> > {
  176. static void s (std::vector<std::string> & v) {
  177. v.push_back(an_or_count<T>(N)); group_list_desc<tlists::tlist<U...> >::s(v);
  178. } };
  179. template <> struct group_list_desc<tlists::tlist<> > { static void s (std::vector<std::string> &) {} };
  180. template <typename... T>
  181. struct list_desc: group_list_desc<typename tlists::group_successive<T...>::type> {};
  182. // functions
  183. template <typename T> struct type_desc_t<T ()>: consonant
  184. { static std::string s (bool b) { return pl("nullary function", b) + " " + returning<T>(b); } };
  185. template <typename T> struct type_desc_t<T (...)>: consonant
  186. { static std::string s (bool b) { return pl("variadic function", b) + " " + returning<T>(b); } };
  187. template <typename T, typename... U> struct type_desc_t<T (U...)>: consonant {
  188. static std::string s (bool b) {
  189. std::vector<std::string> v; list_desc<U...>::s(v); v.push_back(returning<T>(b));
  190. return pl("function", b) + " taking " + commas_and(v.begin(), v.end());
  191. } };
  192. template <typename T, typename... U> struct type_desc_t<T (U..., ...)>: consonant {
  193. static std::string s (bool b) {
  194. std::vector<std::string> v; list_desc<U...>::s(v); v.push_back(returning<T>(b));
  195. return pl("variadic function", b) + " taking at least " + commas_and(v.begin(), v.end());
  196. } };
  197. template<typename T> std::string class_key()
  198. { return std::is_union<T>::value ? "union" : "class"; }
  199. // data members
  200. template <typename T, typename U> struct type_desc_t<T U:: *>: consonant
  201. { static std::string s (bool b) { return pl("pointer", b) + " to data " + pl("member", b) + " of " + class_key<U>() + " " + type<U>() + " of type " + type_desc<T>(); } };
  202. // member function pointers
  203. enum CV { cv_, cv_c, cv_v, cv_cv };
  204. inline char const * to_string(CV const cv)
  205. {
  206. switch (cv) {
  207. case cv_: return "";
  208. case cv_c: return "constant ";
  209. case cv_v: return "volatile ";
  210. case cv_cv: return "constant volatile ";
  211. default: abort();
  212. }
  213. }
  214. template <typename T, typename U, CV cv> struct type_desc_t_mem0: consonant
  215. { static std::string s (bool b) { return pl("pointer", b) + " to " + to_string(cv) + "nullary member " + pl("function", b) + " of " + class_key<U>() + " " + type<U>() + " " + returning<T>(b); } };
  216. template <typename T, typename U>
  217. struct type_desc_t<T (U:: *) ()>: type_desc_t_mem0<T, U, cv_> {};
  218. template <typename T, typename U>
  219. struct type_desc_t<T (U:: *) () const>: type_desc_t_mem0<T, U, cv_c> {};
  220. template <typename T, typename U>
  221. struct type_desc_t<T (U:: *) () volatile>: type_desc_t_mem0<T, U, cv_v> {};
  222. template <typename T, typename U>
  223. struct type_desc_t<T (U:: *) () const volatile>: type_desc_t_mem0<T, U, cv_cv> {};
  224. template <typename T, typename U, CV cv> struct type_desc_t_mem_vari: consonant
  225. { static std::string s (bool b) { return pl("pointer", b) + " to " + to_string(cv) + "variadic member " + pl("function", b) + " of " + class_key<U>() + " " + type<U>() + " " + returning<T>(b); } };
  226. template <typename T, typename U>
  227. struct type_desc_t<T (U:: *) (...)>: type_desc_t_mem_vari<T, U, cv_> {};
  228. template <typename T, typename U>
  229. struct type_desc_t<T (U:: *) (...) const>: type_desc_t_mem_vari<T, U, cv_c> {};
  230. template <typename T, typename U>
  231. struct type_desc_t<T (U:: *) (...) volatile>: type_desc_t_mem_vari<T, U, cv_v> {};
  232. template <typename T, typename U>
  233. struct type_desc_t<T (U:: *) (...) const volatile>: type_desc_t_mem_vari<T, U, cv_cv> {};
  234. template <typename T, typename U, CV cv, typename... P>
  235. struct type_desc_t_memN: consonant {
  236. static std::string s (bool b) {
  237. std::vector<std::string> v; list_desc<P...>::s(v); v.push_back(returning<T>(b));
  238. return pl("pointer", b) + " to " + to_string(cv) + "member " + pl("function", b) + " of " + class_key<U>() + " " + type<U>() + " taking " + commas_and(v.begin(), v.end());
  239. }
  240. };
  241. template <typename T, typename U, typename... P>
  242. struct type_desc_t<T (U:: *) (P...)>: type_desc_t_memN<T, U, cv_, P...> {};
  243. template <typename T, typename U, typename... P>
  244. struct type_desc_t<T (U:: *) (P...) const>: type_desc_t_memN<T, U, cv_c, P...> {};
  245. template <typename T, typename U, typename... P>
  246. struct type_desc_t<T (U:: *) (P...) volatile>: type_desc_t_memN<T, U, cv_v, P...> {};
  247. template <typename T, typename U, typename... P>
  248. struct type_desc_t<T (U:: *) (P...) const volatile>: type_desc_t_memN<T, U, cv_cv, P...> {};
  249. template <typename T, typename U, CV cv, typename... P>
  250. struct type_desc_t_memN_vari: consonant {
  251. static std::string s (bool b) {
  252. std::vector<std::string> v; list_desc<P...>::s(v); v.push_back(returning<T>(b));
  253. return pl("pointer", b) + " to " + to_string(cv) + "variadic member " + pl("function", b) + " of " + class_key<U>() + " " + type<U>() + " taking at least " + commas_and(v.begin(), v.end());
  254. }
  255. };
  256. template <typename T, typename U, typename... P>
  257. struct type_desc_t<T (U:: *) (P..., ...)>: type_desc_t_memN_vari<T, U, cv_, P...> {};
  258. template <typename T, typename U, typename... P>
  259. struct type_desc_t<T (U:: *) (P..., ...) const>: type_desc_t_memN_vari<T, U, cv_c, P...> {};
  260. template <typename T, typename U, typename... P>
  261. struct type_desc_t<T (U:: *) (P..., ...) volatile>: type_desc_t_memN_vari<T, U, cv_v, P...> {};
  262. template <typename T, typename U, typename... P>
  263. struct type_desc_t<T (U:: *) (P..., ...) const volatile>: type_desc_t_memN_vari<T, U, cv_cv, P...> {};
  264. // Library components:
  265. // C++03
  266. template <typename T, typename A> struct type_desc_t<std::vector<T, A> >: consonant
  267. { static std::string s (bool b) { return pl("vector", b) + " of " + many<T>(); } };
  268. template <typename T, typename U> struct type_desc_t<std::pair<T, U> >: consonant
  269. { static std::string s (bool b) { return pl("pair", b) + " of " + an_or_many<T>(b) + " and " + an_or_many<U>(b); } };
  270. template <typename T> struct type_desc_t<std::pair<T, T> >: consonant
  271. { static std::string s (bool b) { return pl("pair", b) + " of " + many<T>(); } };
  272. template <typename T, typename A> struct type_desc_t<std::set<T, A> >: consonant
  273. { static std::string s (bool b) { return pl("set", b) + " of " + many<T>(); } };
  274. template <typename T> struct type_desc_t<std::multiset<T> >: consonant
  275. { static std::string s (bool b) { return pl("multi-set", b) + " of " + many<T>(); } };
  276. /* Like most containers, std::map has a couple of parameters with default arguments. We only want to return a nice verbose string for specializations which use the default arguments. Naively, we might expect that to get this effect, we should simply partially specialize for type_desc_t<map<T, U> >. However, that partial specialization will not match type_desc_t<map<int const, int> >, because this is really:
  277. type_desc_t<map<int const, int, less<int const>, ...> > (note the double-const collapse),
  278. while the partial specialization is really for:
  279. type_desc_t<map<T, U, less<T const>, ...> >,
  280. which doesn't match! This is why we use std::is_same below. As far as I can see map is the only container affected because it has this double-const collapse in its default arguments. */
  281. template <typename T, typename U, typename V, typename W>
  282. struct type_desc_t<std::map<T, U, V, W> >: consonant
  283. { static std::string s (bool const b) {
  284. if (std::is_same<std::map<T, U>, std::map<T, U, V, W> >::value)
  285. return pl("map", b) + " from " + many<T>() + " to " + many<U>();
  286. else return pl(type<std::map<T, U, V, W> >(), b);
  287. } };
  288. template <typename T, typename U> struct type_desc_t<std::multimap<T, U> >: consonant
  289. { static std::string s (bool b) { return pl("multi-map", b) + " from " + many<T>() + " to " + many<U>(); } };
  290. template <typename T> struct type_desc_t<std::list<T> >: consonant
  291. { static std::string s (bool b) { return pl("list", b) + " of " + many<T>(); } };
  292. template <typename T> struct type_desc_t<std::deque<T> >: consonant
  293. { static std::string s (bool b) { return pl("double-ended queue", b) + " of " + many<T>(); } };
  294. template <typename T> struct type_desc_t<std::queue<T> >: consonant
  295. { static std::string s (bool b) { return pl("queue", b) + " of " + many<T>(); } };
  296. template <typename T> struct type_desc_t<std::priority_queue<T> >: consonant
  297. { static std::string s (bool b) { return pl("priority queue", b) + " of " + many<T>(); } };
  298. template <typename T> struct type_desc_t<std::stack<T> >: consonant
  299. { static std::string s (bool b) { return pl("stack", b) + " of " + many<T>(); } };
  300. // C++0x
  301. template <typename T> struct type_desc_t<std::shared_ptr<T> >: consonant
  302. { static std::string s (bool b) { return "shared " + type_desc<T*>(b); } };
  303. template <typename T, size_t N> struct type_desc_t<std::array<T, N> >: Vowel
  304. { static std::string s (bool b) { return pl("array", b) + " of " + count<T>(N); } };
  305. template<size_t N> struct num_vowel { enum { vowel = N == 8 || N == 11 || N == 18 }; };
  306. template <typename... T> struct type_desc_t<std::tuple<T...>> {
  307. static std::string s (bool b) {
  308. std::vector<std::string> v; list_desc<T...>::s(v);
  309. return std::to_string(sizeof...(T)) + "-" + pl("tuple", b) + " of " + commas_and(v.begin(), v.end());
  310. }
  311. enum { vowel = num_vowel<sizeof...(T)>::vowel };
  312. };
  313. #ifdef _UNIQUE_PTR_H
  314. template <typename T> struct type_desc_t<std::unique_ptr<T>>: consonant
  315. { static std::string s (bool b) { return "unique " + type_desc<T*>(b); } };
  316. #endif
  317. template <typename T> struct type_desc_t<std::reference_wrapper<T>>: consonant
  318. { static std::string s (bool b) { return "reference wrapped " + type_desc_t<T>::s(b); } };
  319. template <typename T, typename U, typename H, typename P, typename A> struct type_desc_t<std::unordered_map<T, U, H, P, A>>: consonant
  320. { static std::string s (bool b) { return pl("unordered map", b) + " from " + many<T>() + " to " + many<U>(); } };
  321. template <typename T, typename U, typename H, typename P, typename A> struct type_desc_t<std::unordered_multimap<T, U, H, P, A>>: consonant
  322. { static std::string s (bool b) { return pl("unordered multi-map", b) + " from " + many<T>() + " to " + many<U>(); } };
  323. template <typename T, typename H, typename P, typename A>
  324. struct type_desc_t<std::unordered_set<T, H, P, A>>: consonant
  325. { static std::string s (bool b) { return pl("unordered set", b) + " of " + many<T>(); } };
  326. template <typename T, typename H, typename P, typename A> struct type_desc_t<std::unordered_multiset<T, H, P, A>>: consonant
  327. { static std::string s (bool b) { return pl("unordered multi-set", b) + " of " + many<T>(); } };
  328. } // textual_type_descriptions
  329. #endif
  330. enum expr_cat { lvalue, xvalue, prvalue };
  331. inline char const * to_string(expr_cat c)
  332. {
  333. switch (c)
  334. {
  335. case lvalue: return "lvalue";
  336. case xvalue: return "xvalue";
  337. case prvalue: return "prvalue";
  338. default: return "?";
  339. }
  340. }
  341. template <typename Ch, typename Tr>
  342. basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, expr_cat c)
  343. {
  344. return o << to_string(c);
  345. }
  346. template<typename T>
  347. expr_cat expression_category()
  348. {
  349. if (std::is_lvalue_reference<T>::value) return lvalue;
  350. if (std::is_rvalue_reference<T>::value) return xvalue;
  351. return prvalue;
  352. }
  353. template <typename T> std::string type_desc (bool const plural)
  354. { return textual_type_descriptions::type_desc_t<T>::s(plural); }
  355. template <typename> struct type_desc_tag {};
  356. template <typename> struct type_tag {};
  357. template <typename> struct etype_tag {};
  358. template <typename> struct etype_desc_tag {};
  359. struct adl_hint {};
  360. template <typename Ch, typename Tr, typename T>
  361. basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, adl_hint(type_desc_tag<T>))
  362. { return o << type_desc<T>(); }
  363. template <typename Ch, typename Tr, typename T>
  364. basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, adl_hint(type_tag<T>))
  365. { return o << type<T>(); }
  366. template <typename Ch, typename Tr, typename T>
  367. basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, etype_tag<T>)
  368. { return o << expression_category<T>() << ' ' << type<typename std::remove_reference<T>::type>(); }
  369. template <typename Ch, typename Tr, typename T>
  370. basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, etype_desc_tag<T>)
  371. { return o << expression_category<T>() << ' ' << type_desc<typename std::remove_reference<T>::type>(); }
  372. } // type_strings_detail
  373. template <typename T> type_strings_detail::adl_hint
  374. TYPE_DESC(type_strings_detail::type_desc_tag<T>) { return type_strings_detail::adl_hint(); }
  375. template <typename T> type_strings_detail::adl_hint
  376. TYPE(type_strings_detail::type_tag<T>) { return type_strings_detail::adl_hint(); }
  377. #define TYPE(...) ::type_strings_detail::etype_tag<decltype(void(), (__VA_ARGS__))>{}
  378. #define TYPE_DESC(...) ::type_strings_detail::etype_desc_tag<decltype(void(), (__VA_ARGS__))>{}
  379. #endif // header guard