Search code examples
c++templatesc-preprocessorstring-literals

How do I generate an integer from a string literal at compile-time?


In C++, is it possible to generate an integer from a string literal using only compile-time facilities ?

For instance, if all we have is the literal "6", is there some way to use it as a template argument, like std::array<GET_INTEGER("6")> a; ?

I know about constexpr-based techniques, such as :

template <int N> constexpr char get_char(const char s[N], int n) {
  return s[n];
}

However constexpr isn't ready yet on most compilers, so I'm looking for solutions using probably macros and TMP.

It's just for experimentation, so crazy ideas are welcome.


Solution

  • Apparently gcc allows "abcd"[3] be interpreted as 'd', which allows this to work (at least on g++-4.6 and 4.7):

    #include <boost/preprocessor/repetition/enum.hpp>
    
    template <const char... characters>
    struct GetIntegerTemplate;
    
    template <const char head, const char... rest>
    struct GetIntegerTemplate<head, rest...>
    {
        typedef GetIntegerTemplate<rest...> Prev;
        enum
        {
            power = Prev::power * 10,
            value = (head - '0') * Prev::power + Prev::value
        };
    };
    
    template <>
    struct GetIntegerTemplate<>
    {
        enum
        {
            power = 1,
            value = 0
        };
    };
    
    #define GET_NTH_CHARACTER(z, n, data) data[n]
    #define GET_INTEGER(length, the_string) GetIntegerTemplate<BOOST_PP_ENUM(length, GET_NTH_CHARACTER, the_string)>::value
    
    int main()
    {
        static_assert(GET_INTEGER(7, "1234567") == 1234567, "oops");
    }
    

    But it won't compile on clang, which says "non-type template argument of type 'const char' is not an integral constant expression".


    What it really does is to break down the string literal "1234567" into a list of character literals '1', '2', '3', '4', '5', '6', '7'. The instantiation

    GetIntegerTemplate<'1', '2', '3', '4', '5', '6', '7'>::value
    

    is then invoked to turn the list into an integer 1234567. The string → char literal step may involve non-standard behavior which may not work outside of g++ (i.e. worse than constexpr ☺), but that GetIntegerTemplate<...>::value is portable.