/*~ * Copyright (C) 2015, 2016 George Makrydakis * * The 'typestring' header is a single header C++ library for creating types * to use as type parameters in template instantiations, repository available * at https://github.com/irrequietus/typestring. Conceptually stemming from * own implementation of the same thing (but in a more complicated manner to * be revised) in 'clause': https://github.com/irrequietus/clause. * * File subject to the terms and conditions of the Mozilla Public License v 2.0. * If a copy of the MPLv2 license text was not distributed with this file, you * can obtain it at: http://mozilla.org/MPL/2.0/. */ #ifndef IRQUS_TYPESTRING_HH_ #define IRQUS_TYPESTRING_HH_ namespace irqus { /*~ * @desc A class 'storing' strings into distinct, reusable compile-time types that * can be used as type parameters in a template parameter list. * @tprm C... : char non-type parameter pack whose ordered sequence results * into a specific string. * @note Could have wrapped up everything in a single class, eventually will, * once some compilers fix their class scope lookups! I have added some * utility functions because asides being a fun little project, it is of * use in certain constructs related to template metaprogramming * nonetheless. */ template struct typestring final { private: static constexpr char const vals[sizeof...(C)+1] = { C...,'\0' }; static constexpr unsigned int sval = sizeof...(C); public: static constexpr char const * data() noexcept { return &vals[0]; } static constexpr unsigned int size() noexcept { return sval; }; static constexpr char const * cbegin() noexcept { return &vals[0]; } static constexpr char const * cend() noexcept { return &vals[sval]; } }; template constexpr char const typestring::vals[sizeof...(C)+1]; //*~ part 1: preparing the ground, because function templates are awesome. /*~ * @note While it is easy to resort to constexpr strings for use in constexpr * metaprogramming, what we want is to convert compile time string in situ * definitions into reusable, distinct types, for use in advanced template * metaprogramming techniques. We want such features because this kind of * metaprogramming constitutes a pure, non-strict, untyped functional * programming language with pattern matching where declarative semantics * can really shine. * * Currently, there is no feature in C++ that offers the opportunity to * use strings as type parameter types themselves, despite there are * several, different library implementations. This implementation is a * fast, short, single-header, stupid-proof solution that works with any * C++11 compliant compiler and up, with the resulting type being easily * reusable throughout the code. * * @usge Just include the header and enable -std=c++11 or -std=c++14 etc, use * like in the following example: * * typestring_is("Hello!") * * is essentially identical to the following template instantiation: * * irqus::typestring<'H', 'e', 'l', 'l', 'o', '!'> * * By passing -DUSE_TYPESTRING= during compilation, you can * set the maximum length of the 'typestring' from 1 to 1024 (2^0 to 2^10). * Although all preprocessor implementations tested are capable of far * more with this method, exceeding this limit may cause internal compiler * errors in most, with at times rather hilarious results. */ template constexpr char tygrab(char const(&c)[M]) noexcept { return c[N < M ? N : M-1]; } //*~ part2: Function template type signatures for type deduction purposes. In // other words, exploiting the functorial nature of parameter packs // while mixing them with an obvious catamorphism through pattern // matching galore (partial ordering in this case in C++ "parlance"). template auto typoke(typestring) // as is... -> typestring; template auto typoke(typestring, typestring<'\0'>, typestring...) -> typestring; template auto typoke(typestring, typestring, typestring...) -> decltype(typoke(typestring(), typestring()...)); template auto typeek(typestring) -> decltype(typoke(typestring()...)); template auto tycat_(typestring, typestring, X... x) -> decltype(tycat_(typestring(), x...)); template auto tycat_(typestring) -> typestring; /* * Some people actually using this header as is asked me to include * a typestring "cat" utility given that it is easy enough to implement. * I have added this functionality through the template alias below. For * the obvious implementation, nothing more to say. All T... must be * of course, "typestrings". */ template using tycat = decltype(tycat_(T()...)); } /* irqus */ //*~ part3: some necessary code generation using preprocessor metaprogramming! // There is functional nature in preprocessor metaprogramming as well. /*~ * @note Code generation block. Undoubtedly, the preprocessor implementations * of both clang++ and g++ are relatively competent in producing a * relatively adequate amount of boilerplate for implementing features * that the language itself will probably be having as features in a few * years. At times, like herein, the preprocessor is able to generate * boilerplate *extremely* fast, but over a certain limit the compiler is * incapable of compiling it. For the record, only certain versions of * g++ where capable of going beyond 4K, so I thought of going from base * 16 to base 2 for USE_TYPESTRING power base. For the record, it takes * a few milliseconds to generate boilerplate for several thousands worth * of "string" length through such an 'fmap' like procedure. */ /* 2^0 = 1 */ #define TYPESTRING1(n,x) irqus::tygrab<0x##n##0>(x) /* 2^1 = 2 */ #define TYPESTRING2(n,x) irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) /* 2^2 = 2 */ #define TYPESTRING4(n,x) \ irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) /* 2^3 = 8 */ #define TYPESTRING8(n,x) \ irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) \ , irqus::tygrab<0x##n##4>(x), irqus::tygrab<0x##n##5>(x) \ , irqus::tygrab<0x##n##6>(x), irqus::tygrab<0x##n##7>(x) /* 2^4 = 16 */ #define TYPESTRING16(n,x) \ irqus::tygrab<0x##n##0>(x), irqus::tygrab<0x##n##1>(x) \ , irqus::tygrab<0x##n##2>(x), irqus::tygrab<0x##n##3>(x) \ , irqus::tygrab<0x##n##4>(x), irqus::tygrab<0x##n##5>(x) \ , irqus::tygrab<0x##n##6>(x), irqus::tygrab<0x##n##7>(x) \ , irqus::tygrab<0x##n##8>(x), irqus::tygrab<0x##n##9>(x) \ , irqus::tygrab<0x##n##A>(x), irqus::tygrab<0x##n##B>(x) \ , irqus::tygrab<0x##n##C>(x), irqus::tygrab<0x##n##D>(x) \ , irqus::tygrab<0x##n##E>(x), irqus::tygrab<0x##n##F>(x) /* 2^5 = 32 */ #define TYPESTRING32(n,x) \ TYPESTRING16(n##0,x),TYPESTRING16(n##1,x) /* 2^6 = 64 */ #define TYPESTRING64(n,x) \ TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ , TYPESTRING16(n##3,x) /* 2^7 = 128 */ #define TYPESTRING128(n,x) \ TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ , TYPESTRING16(n##3,x), TYPESTRING16(n##4,x), TYPESTRING16(n##5,x) \ , TYPESTRING16(n##6,x), TYPESTRING16(n##7,x) /* 2^8 = 256 */ #define TYPESTRING256(n,x) \ TYPESTRING16(n##0,x), TYPESTRING16(n##1,x), TYPESTRING16(n##2,x) \ , TYPESTRING16(n##3,x), TYPESTRING16(n##4,x), TYPESTRING16(n##5,x) \ , TYPESTRING16(n##6,x), TYPESTRING16(n##7,x), TYPESTRING16(n##8,x) \ , TYPESTRING16(n##9,x), TYPESTRING16(n##A,x), TYPESTRING16(n##B,x) \ , TYPESTRING16(n##C,x), TYPESTRING16(n##D,x), TYPESTRING16(n##E,x) \ , TYPESTRING16(n##F,x) /* 2^9 = 512 */ #define TYPESTRING512(n,x) \ TYPESTRING256(n##0,x), TYPESTRING256(n##1,x) /* 2^10 = 1024 */ #define TYPESTRING1024(n,x) \ TYPESTRING256(n##0,x), TYPESTRING256(n##1,x), TYPESTRING256(n##2,x) \ , TYPESTRING128(n##3,x), TYPESTRING16(n##38,x), TYPESTRING16(n##39,x) \ , TYPESTRING16(n##3A,x), TYPESTRING16(n##3B,x), TYPESTRING16(n##3C,x) \ , TYPESTRING16(n##3D,x), TYPESTRING16(n##3E,x), TYPESTRING16(n##3F,x) //*~ part4 : Let's give some logic with a -DUSE_TYPESTRING flag! #ifdef USE_TYPESTRING #if USE_TYPESTRING == 0 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 1 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 2 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 3 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 4 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 5 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 6 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 7 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 8 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 9 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING == 10 #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING > 10 #warning !!!: custom typestring length exceeded allowed (1024) !!! #warning !!!: all typestrings to default maximum typestring length of 64 !!! #warning !!!: you can use -DUSE_TYPESTRING= to set length !!! #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #elif USE_TYPESTRING < 0 #warning !!!: You used USE_TYPESTRING with a negative size specified !!! #warning !!!: all typestrings to default maximum typestring length of 64 !!! #warning !!!: you can use -DUSE_TYPESTRING= to set length !!! #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #endif #else #define typestring_is(x) \ decltype(irqus::typeek(irqus::typestring())) #endif #endif /* IRQUS_TYPESTRING_HH_ */