Search code examples
c++stringtemplatesc++14template-meta-programming

How do I concatenate strings repeatedly at compile time?


I wrote the following code for using string in compile time:

template<char... Chars>
struct CnstString {
    template<char... Aped>
    using push_back = CnstString<Chars..., Aped...>;
    constexpr static char value[] = {Chars...};
};

It's fine when I use it like this:

#include <iostream>
using str = CnstString<'H', 'e', 'l', 'l', 'o', '\0'>;
int main()
{
    std::cout << str << std::endl;
    return 0; // it will output "Hello"
}

What I want to do is use a recursive template struct, splice a string given by the above code at compile time, and append a '\0' to the array at the recursive end point.

The reason I wrote this code is because I want to concatenate a same string multiple times. Obviously I can't concatenate the same string together like "Hello""Hello".

The recursive template struct is shown below:

template<int N, typename Str>
struct RepeatStr {
    using ret = RepeatStr<N-1, Str>; // this is an error, I don't know how to pass parameters
};
template<typename Str>
struct RepeatStr<-1, Str> {
    using ret = Str:: template push_back<'\0'>;
};

However, I found that I didn't know how to append characters to itself using its characters in Str.

Is there something wrong? Or it's just impossible to do in compile time?


Solution

  • If possible, I would change definition of CnstString to place '\0' only in value

    template<char... Chars>
    struct CnstString {
        constexpr static char value[] = {Chars..., '\0'};
    };
    

    Then provide operator + (which would allow Fold expression (C++17) later)

    template <char... Cs1, char...Cs2>
    CnstString<Cs1..., Cs2...> operator +(CnstString<Cs1...>, CnstString<Cs2...>) { return {}; }
    

    Finally, you RepeatStr<N, Str> which would have a type for Str{} + .. + Str{}:

    template<int N, typename Str>
    struct RepeatStr {
        // C++20
        using ret = decltype([]<std::size_t... Is>(std::index_sequence<Is...>){
            return ((static_cast<void>(Is), Str{}) + ...);
        }(std::make_index_sequence<N>()));
    };
    

    Demo

    As you are limited to C++14, replacing fold expression of C++17 by "recursive" function call:

    template <typename T>
    auto sum_impl(T t)
    {
        return t;
    }
    
    template <typename T, typename... Ts>
    auto sum_impl(T t, Ts... ts)
    {
        return t + sum_impl(ts...);
    }
    

    And replace the template lambda(C++20) by regular template function:

    template <typename Str, std::size_t... Is>
    auto repeat_impl(std::index_sequence<Is...>)
    {
        return sum_impl((static_cast<void>(Is), Str{})...);
    }
    
    template<int N, typename Str>
    struct RepeatStr {
        using ret = decltype(repeat_impl<Str>(std::make_index_sequence<N>()));
    };
    

    Demo