c++stringtemplatesboostboost-mpl

C++ convert integer to string at compile time


I want to do something like this:

template<int N>
char* foo() {
  // return a compile-time string containing N, equivalent to doing
  // ostringstream ostr; 
  // ostr << N;
  // return ostr.str().c_str();
}

It seems like the boost MPL library might allow this but I couldn't really figure out how to use it to accomplish this. Is this possible?


Solution

  • This can be done using C++14 without any external dependencies. The key addition to the standard is the ability to have non-trivial constexpr constructors, allowing the functionality to be contained within a simple class.

    Given an integer template parameter, the constructor can carry out the integer-to-string conversion. This is stored in a member character buffer whose size is determined by an additional constexpr function. Then, a user-defined conversion provides access to the buffer:

    #include <cstdint>
    
    template<std::intmax_t N>
    class to_string_t {
    
        constexpr static auto buflen() noexcept {
            unsigned int len = N > 0 ? 1 : 2;
            for (auto n = N; n; len++, n /= 10);
            return len;
        }
    
        char buf[buflen()] = {};
    
    public:
        constexpr to_string_t() noexcept {
            auto ptr = buf + buflen();
            *--ptr = '\0';
    
            if (N != 0) {
                for (auto n = N; n; n /= 10)
                    *--ptr = "0123456789"[(N < 0 ? -1 : 1) * (n % 10)];
                if (N < 0)
                    *--ptr = '-';
            } else {
                buf[0] = '0';
            }
        }
    
        constexpr operator const char *() const { return buf; }
    };
    

    Finally, a variable template (another C++14 addition) simplifies the syntax:

    template<std::intmax_t N>
    constexpr to_string_t<N> to_string;
    
    puts(to_string<62017>); // prints "62017"
    

    The functionality can be extended to support other bases (e.g. hexadecimal), wide character types, and the common container interface; I've packed this all into a single header and put it on GitHub at tcsullivan/constexpr-to-string.

    With C++20, this can also be extended to support floating-point numbers. A container type is needed for the floating-point literal, which previously could not be a template parameter. See the f_to_string.hpp header in the GitHub repo for the implementation.