/prelude/type_strings.hpp
C++ Header | 501 lines | 362 code | 118 blank | 21 comment | 23 complexity | b3c715dd958614ce1dfadf08b90ff8f1 MD5 | raw file
1// Todo: Automatic parentheses placement. 2 3#ifndef TYPE_STRINGS_HPP 4#define TYPE_STRINGS_HPP 5 6#if __cplusplus >= 201103 7#include "lvalue_rvalue.hpp" 8#endif 9 10#include <list> 11#include <map> 12#include <string> 13#include <vector> 14#include <stack> 15#include <queue> 16#include <set> 17#include <utility> 18#include <deque> 19#include <iostream> 20#include <fstream> 21#include <sstream> 22#include <ios> 23#include <cassert> 24#include <memory> 25#include <cstring> 26 27#if __cplusplus >= 201103 28#include <unordered_map> 29#include <unordered_set> 30#include <tuple> 31#include <array> 32#include <type_traits> 33#include "tlists.hpp" 34#endif 35 36#if __cplusplus >= 201500 37#include <experimental/type_traits> 38#endif 39 40namespace type_strings_detail { 41 42using std::basic_ostream; 43 44template <typename I> 45std::string commas_and (I b, I e) 46{ 47 assert(b != e); 48 if (e - b == 3) { std::string r = *b++; r += ", " + *b++; return r + ", and " + *b; } 49 else if (e - b == 2) { I c = b; ++c; return *b + " and " + *c; } 50 else if (e - b == 1) return *b; 51 else { I c = b; ++c; return *b + ", " + commas_and(c, e); } 52} 53 54// Type strings in ordinary C++ syntax 55 56template <typename T> 57char const * helper() 58{ 59 return __PRETTY_FUNCTION__ + sizeof( 60 #ifdef __clang__ 61 "const char* type_strings_detail::helper() [T = " 62 #else 63 "const char* type_strings_detail::helper() [with T = " 64 #endif 65 ) - 1; 66} 67 68template <typename T> std::string type() { 69 char const * const p = helper<T>(); 70 return std::string(p, std::strlen(p) - sizeof(']')); 71} 72 73#define TYPEDEF_TYPE(n) template <> inline std::string type<std::n> () { return #n; } 74 75TYPEDEF_TYPE(ios) 76TYPEDEF_TYPE(fstream) 77TYPEDEF_TYPE(ifstream) 78TYPEDEF_TYPE(ofstream) 79TYPEDEF_TYPE(ostringstream) 80TYPEDEF_TYPE(istringstream) 81TYPEDEF_TYPE(stringstream) 82TYPEDEF_TYPE(streambuf) 83TYPEDEF_TYPE(iostream) 84TYPEDEF_TYPE(ostream) 85TYPEDEF_TYPE(istream) 86TYPEDEF_TYPE(string) 87TYPEDEF_TYPE(wstring) 88 89#undef TYPEDEF_TYPE 90 91// Verbose type description strings 92 93template <typename> std::string type_desc (bool plural = false); 94 95#if __cplusplus >= 201103 96namespace textual_type_descriptions 97{ 98 inline std::string pl (std::string const & s, bool const b) { return s + (b ? "s" : ""); } 99 100 template <typename T> std::string many () { return type_desc<T>(true); } 101 102 template <typename> struct type_desc_t; 103 104 template <typename T> std::string an () 105 { return (type_desc_t<T>::vowel ? "an " : "a ") + type_desc<T>(); } 106 107 template <typename T> std::string an_or_many (bool const plural = false) 108 { if (plural) return many<T>(); else return an<T>(); } 109 110 inline std::string to_string(int i) 111 { 112 std::ostringstream s; 113 s << i; 114 return s.str(); 115 } 116 117 template <typename T> std::string count (size_t const i) 118 { return to_string(i) + " " + type_desc<T>(i != 1); } 119 120 template <typename T> std::string an_or_count (size_t const i) { return i == 1 ? an<T>() : count<T>(i); } 121 122 struct Vowel { enum { vowel = true }; }; 123 struct consonant { enum { vowel = false }; }; 124 125 template <typename T> struct type_desc_t: consonant 126 { static std::string s (bool b) { return pl(type<T>(), b); } }; 127 128 template <typename T> std::string returning (bool const plural) 129 { return "returning " + an_or_many<T>(plural); } 130 131 template <> inline std::string returning<void> (bool) { return "returning nothing"; } 132 133 // built-in types 134 135 #define DEF_BUILTIN_SPEC(type, str, vow) \ 136 template <> struct type_desc_t<type> \ 137 { static std::string s (bool plural) { return pl(str, plural); } enum { vowel = vow }; }; 138 139 DEF_BUILTIN_SPEC(bool, "boolean", false) 140 DEF_BUILTIN_SPEC(char, "character", false) 141 DEF_BUILTIN_SPEC(signed char, "signed character", false) 142 DEF_BUILTIN_SPEC(unsigned char, "unsigned character", true) 143 DEF_BUILTIN_SPEC(wchar_t, "wide character", false) 144 DEF_BUILTIN_SPEC(int, "integer", true) 145 DEF_BUILTIN_SPEC(long int, "long integer", false) 146 DEF_BUILTIN_SPEC(unsigned int, "unsigned integer", true) 147 DEF_BUILTIN_SPEC(long unsigned int, "long unsigned integer", false) 148 149 #undef DEF_BUILTIN_SPEC 150 151 // stdlib typedefs 152 153 #define TYPEDEF_TYPE_DESC(n, pl, vow) \ 154 template <> struct type_desc_t<std::n> \ 155 { static std::string s (bool b) { return b ? #n pl : #n; } enum { vowel = vow }; }; 156 157 TYPEDEF_TYPE_DESC(ios, "es", true) 158 TYPEDEF_TYPE_DESC(fstream, "s", false) 159 TYPEDEF_TYPE_DESC(ifstream, "s", true) 160 TYPEDEF_TYPE_DESC(ofstream, "s", true) 161 TYPEDEF_TYPE_DESC(ostringstream, "s", true) 162 TYPEDEF_TYPE_DESC(istringstream, "s", true) 163 TYPEDEF_TYPE_DESC(stringstream, "s", false) 164 TYPEDEF_TYPE_DESC(streambuf, "s", false) 165 TYPEDEF_TYPE_DESC(iostream, "s", true) 166 TYPEDEF_TYPE_DESC(ostream, "s", true) 167 TYPEDEF_TYPE_DESC(istream, "s", true) 168 TYPEDEF_TYPE_DESC(string, "s", false) 169 TYPEDEF_TYPE_DESC(wstring, "s", false) 170 171 #undef TYPEDEF_TYPE_DESC 172 173 // primitive constructs 174 175 template <> struct type_desc_t<void> 176 { static std::string s (bool) { return "void"; } }; // has no plural 177 178 template <typename T> struct type_desc_t<T const>: consonant 179 { static std::string s (bool b) { return "constant " + type_desc<T>(b); } }; 180 181 template <typename T> struct type_desc_t<T volatile>: consonant 182 { static std::string s (bool b) { return "volatile " + type_desc<T>(b); } }; 183 184 template <typename T> struct type_desc_t<T const volatile>: consonant 185 { static std::string s (bool b) { return "constant volatile " + type_desc<T>(b); } }; 186 187 template <typename T> struct type_desc_t<T *>: consonant 188 { static std::string s (bool b) { return pl("pointer", b) + " to " + an_or_many<T>(b); } }; 189 190 template <> struct type_desc_t<void *>: consonant 191 { static std::string s (bool b) { return pl("pointer", b) + " to anything"; } }; 192 193 template <typename T> struct type_desc_t<T &>: Vowel 194 { static std::string s (bool b) { return pl("lvalue reference", b) + " to " + an_or_many<T>(b); } }; 195 196 #if __cplusplus >= 201103 197 template <typename T> struct type_desc_t<T &&>: Vowel 198 { static std::string s (bool b) { return pl("rvalue reference", b) + " to " + an_or_many<T>(b); } }; 199 #endif 200 201 #define TYPE_STRINGS_EMPTY 202 203 #define ARRAY_SPEC(cv) \ 204 template <typename T> struct type_desc_t<T cv []>: Vowel \ 205 { static std::string s (bool b) { return pl("array", b) + " of " + many<T cv>(); } }; 206 207 ARRAY_SPEC(TYPE_STRINGS_EMPTY) 208 ARRAY_SPEC(const) 209 ARRAY_SPEC(volatile) 210 ARRAY_SPEC(const volatile) 211 212 #undef ARRAY_SPEC 213 214 #define ARRAY_SPEC(cv) \ 215 template <typename T, size_t N> struct type_desc_t<T cv [N]>: Vowel \ 216 { static std::string s (bool b) { return pl("array", b) + " of " + count<T cv>(N); } }; 217 218 ARRAY_SPEC(TYPE_STRINGS_EMPTY) 219 ARRAY_SPEC(const) 220 ARRAY_SPEC(volatile) 221 ARRAY_SPEC(const volatile) 222 223 #undef ARRAY_SPEC 224 225 #undef TYPE_STRINGS_EMPTY 226 227 template <typename> struct group_list_desc; 228 229 template<size_t N, typename T, typename... U> 230 struct group_list_desc<tlists::tlist<tlists::group<N, T>, U...> > { 231 static void s (std::vector<std::string> & v) { 232 v.push_back(an_or_count<T>(N)); group_list_desc<tlists::tlist<U...> >::s(v); 233 } }; 234 235 template <> struct group_list_desc<tlists::tlist<> > { static void s (std::vector<std::string> &) {} }; 236 237 template <typename... T> 238 struct list_desc: group_list_desc<typename tlists::group_successive<T...>::type> {}; 239 240 // functions 241 242 template <typename T> struct type_desc_t<T ()>: consonant 243 { static std::string s (bool b) { return pl("nullary function", b) + " " + returning<T>(b); } }; 244 245 template <typename T> struct type_desc_t<T (...)>: consonant 246 { static std::string s (bool b) { return pl("variadic function", b) + " " + returning<T>(b); } }; 247 248 template <typename T, typename... U> struct type_desc_t<T (U...)>: consonant { 249 static std::string s (bool b) { 250 std::vector<std::string> v; list_desc<U...>::s(v); v.push_back(returning<T>(b)); 251 return pl("function", b) + " taking " + commas_and(v.begin(), v.end()); 252 } }; 253 254 template <typename T, typename... U> struct type_desc_t<T (U..., ...)>: consonant { 255 static std::string s (bool b) { 256 std::vector<std::string> v; list_desc<U...>::s(v); v.push_back(returning<T>(b)); 257 return pl("variadic function", b) + " taking at least " + commas_and(v.begin(), v.end()); 258 } }; 259 260 template<typename T> std::string class_key() 261 { return std::is_union<T>::value ? "union" : "class"; } 262 263 // data members 264 265 template <typename T, typename U> struct type_desc_t<T U:: *>: consonant 266 { 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>(); } }; 267 268 // member function pointers 269 270 enum CV { cv_, cv_c, cv_v, cv_cv }; 271 272 inline char const * to_string(CV const cv) 273 { 274 switch (cv) { 275 case cv_: return ""; 276 case cv_c: return "constant "; 277 case cv_v: return "volatile "; 278 case cv_cv: return "constant volatile "; 279 default: abort(); 280 } 281 } 282 283 template <typename T, typename U, CV cv> struct type_desc_t_mem0: consonant 284 { 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); } }; 285 286 template <typename T, typename U> 287 struct type_desc_t<T (U:: *) ()>: type_desc_t_mem0<T, U, cv_> {}; 288 template <typename T, typename U> 289 struct type_desc_t<T (U:: *) () const>: type_desc_t_mem0<T, U, cv_c> {}; 290 template <typename T, typename U> 291 struct type_desc_t<T (U:: *) () volatile>: type_desc_t_mem0<T, U, cv_v> {}; 292 template <typename T, typename U> 293 struct type_desc_t<T (U:: *) () const volatile>: type_desc_t_mem0<T, U, cv_cv> {}; 294 295 template <typename T, typename U, CV cv> struct type_desc_t_mem_vari: consonant 296 { 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); } }; 297 298 template <typename T, typename U> 299 struct type_desc_t<T (U:: *) (...)>: type_desc_t_mem_vari<T, U, cv_> {}; 300 template <typename T, typename U> 301 struct type_desc_t<T (U:: *) (...) const>: type_desc_t_mem_vari<T, U, cv_c> {}; 302 template <typename T, typename U> 303 struct type_desc_t<T (U:: *) (...) volatile>: type_desc_t_mem_vari<T, U, cv_v> {}; 304 template <typename T, typename U> 305 struct type_desc_t<T (U:: *) (...) const volatile>: type_desc_t_mem_vari<T, U, cv_cv> {}; 306 307 template <typename T, typename U, CV cv, typename... P> 308 struct type_desc_t_memN: consonant { 309 static std::string s (bool b) { 310 std::vector<std::string> v; list_desc<P...>::s(v); v.push_back(returning<T>(b)); 311 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()); 312 } 313 }; 314 315 template <typename T, typename U, typename... P> 316 struct type_desc_t<T (U:: *) (P...)>: type_desc_t_memN<T, U, cv_, P...> {}; 317 template <typename T, typename U, typename... P> 318 struct type_desc_t<T (U:: *) (P...) const>: type_desc_t_memN<T, U, cv_c, P...> {}; 319 template <typename T, typename U, typename... P> 320 struct type_desc_t<T (U:: *) (P...) volatile>: type_desc_t_memN<T, U, cv_v, P...> {}; 321 template <typename T, typename U, typename... P> 322 struct type_desc_t<T (U:: *) (P...) const volatile>: type_desc_t_memN<T, U, cv_cv, P...> {}; 323 324 template <typename T, typename U, CV cv, typename... P> 325 struct type_desc_t_memN_vari: consonant { 326 static std::string s (bool b) { 327 std::vector<std::string> v; list_desc<P...>::s(v); v.push_back(returning<T>(b)); 328 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()); 329 } 330 }; 331 332 template <typename T, typename U, typename... P> 333 struct type_desc_t<T (U:: *) (P..., ...)>: type_desc_t_memN_vari<T, U, cv_, P...> {}; 334 template <typename T, typename U, typename... P> 335 struct type_desc_t<T (U:: *) (P..., ...) const>: type_desc_t_memN_vari<T, U, cv_c, P...> {}; 336 template <typename T, typename U, typename... P> 337 struct type_desc_t<T (U:: *) (P..., ...) volatile>: type_desc_t_memN_vari<T, U, cv_v, P...> {}; 338 template <typename T, typename U, typename... P> 339 struct type_desc_t<T (U:: *) (P..., ...) const volatile>: type_desc_t_memN_vari<T, U, cv_cv, P...> {}; 340 341 // Library components: 342 343 // C++03 344 345 template <typename T, typename A> struct type_desc_t<std::vector<T, A> >: consonant 346 { static std::string s (bool b) { return pl("vector", b) + " of " + many<T>(); } }; 347 348 template <typename T, typename U> struct type_desc_t<std::pair<T, U> >: consonant 349 { static std::string s (bool b) { return pl("pair", b) + " of " + an_or_many<T>(b) + " and " + an_or_many<U>(b); } }; 350 351 template <typename T> struct type_desc_t<std::pair<T, T> >: consonant 352 { static std::string s (bool b) { return pl("pair", b) + " of " + many<T>(); } }; 353 354 template <typename T, typename A> struct type_desc_t<std::set<T, A> >: consonant 355 { static std::string s (bool b) { return pl("set", b) + " of " + many<T>(); } }; 356 357 template <typename T> struct type_desc_t<std::multiset<T> >: consonant 358 { static std::string s (bool b) { return pl("multi-set", b) + " of " + many<T>(); } }; 359 360 /* 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: 361 362 type_desc_t<map<int const, int, less<int const>, ...> > (note the double-const collapse), 363 364 while the partial specialization is really for: 365 366 type_desc_t<map<T, U, less<T const>, ...> >, 367 368 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. */ 369 370 template <typename T, typename U, typename V, typename W> 371 struct type_desc_t<std::map<T, U, V, W> >: consonant 372 { static std::string s (bool const b) { 373 if (std::is_same<std::map<T, U>, std::map<T, U, V, W> >::value) 374 return pl("map", b) + " from " + many<T>() + " to " + many<U>(); 375 else return pl(type<std::map<T, U, V, W> >(), b); 376 } }; 377 378 template <typename T, typename U> struct type_desc_t<std::multimap<T, U> >: consonant 379 { static std::string s (bool b) { return pl("multi-map", b) + " from " + many<T>() + " to " + many<U>(); } }; 380 381 template <typename T> struct type_desc_t<std::list<T> >: consonant 382 { static std::string s (bool b) { return pl("list", b) + " of " + many<T>(); } }; 383 384 template <typename T> struct type_desc_t<std::deque<T> >: consonant 385 { static std::string s (bool b) { return pl("double-ended queue", b) + " of " + many<T>(); } }; 386 387 template <typename T> struct type_desc_t<std::queue<T> >: consonant 388 { static std::string s (bool b) { return pl("queue", b) + " of " + many<T>(); } }; 389 390 template <typename T> struct type_desc_t<std::priority_queue<T> >: consonant 391 { static std::string s (bool b) { return pl("priority queue", b) + " of " + many<T>(); } }; 392 393 template <typename T> struct type_desc_t<std::stack<T> >: consonant 394 { static std::string s (bool b) { return pl("stack", b) + " of " + many<T>(); } }; 395 396 // C++0x 397 398 template <typename T> struct type_desc_t<std::shared_ptr<T> >: consonant 399 { static std::string s (bool b) { return "shared " + type_desc<T*>(b); } }; 400 401 template <typename T, size_t N> struct type_desc_t<std::array<T, N> >: Vowel 402 { static std::string s (bool b) { return pl("array", b) + " of " + count<T>(N); } }; 403 404 template<size_t N> struct num_vowel { enum { vowel = N == 8 || N == 18 }; }; 405 406 template <typename... T> struct type_desc_t<std::tuple<T...>> { 407 static std::string s (bool b) { 408 std::vector<std::string> v; list_desc<T...>::s(v); 409 return std::to_string(sizeof...(T)) + "-" + pl("tuple", b) + " of " + commas_and(v.begin(), v.end()); 410 } 411 enum { vowel = num_vowel<sizeof...(T)>::vowel }; 412 }; 413 414 #ifdef _UNIQUE_PTR_H 415 template <typename T> struct type_desc_t<std::unique_ptr<T>>: consonant 416 { static std::string s (bool b) { return "unique " + type_desc<T*>(b); } }; 417 #endif 418 419 template <typename T> struct type_desc_t<std::reference_wrapper<T>>: consonant 420 { static std::string s (bool b) { return "reference wrapped " + type_desc_t<T>::s(b); } }; 421 422 template <typename T, typename U, typename H, typename P, typename A> struct type_desc_t<std::unordered_map<T, U, H, P, A>>: consonant 423 { static std::string s (bool b) { return pl("unordered map", b) + " from " + many<T>() + " to " + many<U>(); } }; 424 425 template <typename T, typename U, typename H, typename P, typename A> struct type_desc_t<std::unordered_multimap<T, U, H, P, A>>: consonant 426 { static std::string s (bool b) { return pl("unordered multi-map", b) + " from " + many<T>() + " to " + many<U>(); } }; 427 428 template <typename T, typename H, typename P, typename A> 429 struct type_desc_t<std::unordered_set<T, H, P, A>>: consonant 430 { static std::string s (bool b) { return pl("unordered set", b) + " of " + many<T>(); } }; 431 432 template <typename T, typename H, typename P, typename A> struct type_desc_t<std::unordered_multiset<T, H, P, A>>: consonant 433 { static std::string s (bool b) { return pl("unordered multi-set", b) + " of " + many<T>(); } }; 434 435} // textual_type_descriptions 436#endif 437 438enum expr_cat { lvalue, xvalue, prvalue }; 439 440inline char const * to_string(expr_cat c) 441{ 442 switch (c) 443 { 444 case lvalue: return "lvalue"; 445 case xvalue: return "xvalue"; 446 case prvalue: return "prvalue"; 447 default: return "?"; 448 } 449} 450 451template <typename Ch, typename Tr> 452basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, expr_cat c) 453{ 454 return o << to_string(c); 455} 456 457template<typename T> 458expr_cat expression_category() 459{ 460 if (std::is_lvalue_reference<T>::value) return lvalue; 461 if (std::is_rvalue_reference<T>::value) return xvalue; 462 return prvalue; 463} 464 465template <typename T> std::string type_desc (bool const plural) 466{ return textual_type_descriptions::type_desc_t<T>::s(plural); } 467 468template <typename> struct type_desc_tag {}; 469template <typename> struct type_tag {}; 470template <typename> struct etype_tag {}; 471template <typename> struct etype_desc_tag {}; 472 473struct adl_hint {}; 474 475template <typename Ch, typename Tr, typename T> 476basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, adl_hint(type_desc_tag<T>)) 477{ return o << type_desc<T>(); } 478 479template <typename Ch, typename Tr, typename T> 480basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, adl_hint(type_tag<T>)) 481{ return o << type<T>(); } 482 483template <typename Ch, typename Tr, typename T> 484basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, etype_tag<T>) 485{ return o << expression_category<T>() << ' ' << type<typename std::remove_reference<T>::type>(); } 486 487template <typename Ch, typename Tr, typename T> 488basic_ostream<Ch, Tr> & operator<<(basic_ostream<Ch, Tr> & o, etype_desc_tag<T>) 489{ return o << expression_category<T>() << ' ' << type_desc<typename std::remove_reference<T>::type>(); } 490 491} // type_strings_detail 492 493template <typename T> type_strings_detail::adl_hint 494 TYPE_DESC(type_strings_detail::type_desc_tag<T>) { return type_strings_detail::adl_hint(); } 495template <typename T> type_strings_detail::adl_hint 496 TYPE(type_strings_detail::type_tag<T>) { return type_strings_detail::adl_hint(); } 497 498#define TYPE(...) ::type_strings_detail::etype_tag<decltype(void(), (__VA_ARGS__))>{} 499#define TYPE_DESC(...) ::type_strings_detail::etype_desc_tag<decltype(void(), (__VA_ARGS__))>{} 500 501#endif // header guard