Search code examples
c++c++11constexprstrcatitoa

How to generate a constexpr version string from three integers (or perhaps a git/SVN commit/rev. string)?


Say I have

constexpr const std::uint8_t major = 1;
constexpr const std::uint8_t minor = 10;
constexpr const std::uint8_t bugfix = 0;

and I want

constexpr const char* version_string(){ ... }

to return the equivalent of "1.10.0" in this example, how would I do it?

I assume I'll need both of these, in constexpr:

  • integer to string conversion
  • string concatenation

The problem is purely academic, and I see little to no use to actually have it constexpr other than "it's possible". I just can't see how this would pan out. I'm willing to accept C++1y solutions that work on GCC 4.9 and Clang 3.4/3.5.

I believe I have found nearly what I seek on some Japanese blogs:

I will see what I can do with these, and perhaps answer this self-declared interesting question myself when I'm satisfied with the result.


Solution

  • Here is a C++11 solution. It uses class templates with char... parameter pack to simulate strings:

    #include <iostream>
    #include <type_traits>
    
    template <char... symbols>
    struct String
    {
        static constexpr char value[] = {symbols...};
    };
    
    template <char... symbols>
    constexpr char String<symbols...>::value[];
    
    template <typename, typename>
    struct Concat;
    
    template <char... symbols1, char... symbols2>
    struct Concat<String<symbols1...>, String<symbols2...>>
    {
        using type = String<symbols1..., symbols2...>;
    };
    
    template <typename...>
    struct Concatenate;
    
    template <typename S, typename... Strings>
    struct Concatenate<S, Strings...>
    {
        using type = typename Concat<S, typename Concatenate<Strings...>::type>::type;
    };
    
    template <>
    struct Concatenate<>
    {
        using type = String<>;
    };
    
    template <std::size_t N>
    struct NumberToString
    {
        using type = typename Concat
            <
                typename std::conditional<(N >= 10), typename NumberToString<N / 10>::type, String<>>::type,
                String<'0' + N % 10>
            >::type;
    };
    
    template <>
    struct NumberToString<0>
    {
        using type = String<'0'>;
    };
    
    constexpr const std::uint8_t major = 1;
    constexpr const std::uint8_t minor = 10;
    constexpr const std::uint8_t bugfix = 0;
    
    using VersionString = Concatenate
        <
            NumberToString<major>::type,
            String<'.'>,
            NumberToString<minor>::type,
            String<'.'>,
            NumberToString<bugfix>::type
        >::type;
    
    constexpr const char* version_string = VersionString::value;
    
    int main()
    {
        std::cout << version_string << std::endl;
    }
    

    See live example.