Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert a number to a string literal with constexpr [duplicate]

Tags:

c++

c++11

I'm looking for a way to convert a number to a string literal at compile time. It should look something like this:

template <unsigned num>
struct num_to_string {
    constexpr static char value[] = /* ... magic goes here ... */;
};

So that num_to_string<5>::value is equal to "5" or {'5', '\0'}.

This can be useful for generating a string at compile time from a number that is the result of some other constexpr number calculations.

Also note that I'm only interested in unsigned numbers since that should be easier to deal with. Bonus points for the signed version :)

EDIT: Note that this is similar to C++ convert integer to string at compile time, but not the same. Here I explicitly want something that uses constexpr and not macros to aid in generic programming.

like image 814
syvex Avatar asked Jun 02 '14 16:06

syvex


1 Answers

Variadic templates to the rescue. :)

namespace detail
{
    template<unsigned... digits>
    struct to_chars { static const char value[]; };

    template<unsigned... digits>
    constexpr char to_chars<digits...>::value[] = {('0' + digits)..., 0};

    template<unsigned rem, unsigned... digits>
    struct explode : explode<rem / 10, rem % 10, digits...> {};

    template<unsigned... digits>
    struct explode<0, digits...> : to_chars<digits...> {};
}

template<unsigned num>
struct num_to_string : detail::explode<num> {};

As always, here's a live example on Coliru showing usage and the (relevant) generated assembly.


It's straightforward to adapt this approach to support negative numbers as well. Here's a more generic form that requires the user to input the integer's type:

namespace detail
{
    template<uint8_t... digits> struct positive_to_chars { static const char value[]; };
    template<uint8_t... digits> constexpr char positive_to_chars<digits...>::value[] = {('0' + digits)..., 0};

    template<uint8_t... digits> struct negative_to_chars { static const char value[]; };
    template<uint8_t... digits> constexpr char negative_to_chars<digits...>::value[] = {'-', ('0' + digits)..., 0};

    template<bool neg, uint8_t... digits>
    struct to_chars : positive_to_chars<digits...> {};

    template<uint8_t... digits>
    struct to_chars<true, digits...> : negative_to_chars<digits...> {};

    template<bool neg, uintmax_t rem, uint8_t... digits>
    struct explode : explode<neg, rem / 10, rem % 10, digits...> {};

    template<bool neg, uint8_t... digits>
    struct explode<neg, 0, digits...> : to_chars<neg, digits...> {};

    template<typename T>
    constexpr uintmax_t cabs(T num) { return (num < 0) ? -num : num; }
}

template<typename Integer, Integer num>
struct string_from : detail::explode<(num < 0), detail::cabs(num)> {};

Its usage is like:

string_from<signed, -1>::value

as demonstrated in the live example on Coliru.

like image 129
tclamb Avatar answered Nov 04 '22 06:11

tclamb