Search code examples
c++c++20constexprfmtstring-view

How to implement constexpr string_view multiplication with a number


I want to implement string_view multiplied by a number like python ("{}"*8) so that on fmt::format is simpler to express how many "{}" in format string. But the following code:

inline constexpr auto operator*(const std::string_view& sv, size_t times) -> std::array<char, sv.length() * times> {
  std::array<char, sv.length() * times> result;
  constexpr char* data = result.data();
  for(int i=0; i<times; ++i){
    for(int j=0; j<sv.length(); ++j, ++data){
      *data = sv.data()[j];
    }
  }
  return result;
}

The compiler error message is

/home/miki/dumpefs_cpp/include/fs/f3s_spec.h:96:114: error: template argument 2 is invalid
   96 | inline constexpr auto operator*(const std::string_view& sv, size_t times) -> std::array<char, sv.length() * times> {
      |                                                                                                                  ^
/home/miki/dumpefs_cpp/include/fs/f3s_spec.h:96:114: error: template argument 2 is invalid
/home/miki/dumpefs_cpp/include/fs/f3s_spec.h:96:114: error: template argument 2 is invalid
/home/miki/dumpefs_cpp/include/fs/f3s_spec.h:96:114: error: template argument 2 is invalid
/home/miki/dumpefs_cpp/include/fs/f3s_spec.h:96:78: error: invalid template-id
   96 | inline constexpr auto operator*(const std::string_view& sv, size_t times) -> std::array<char, sv.length() * times> {
      |                                                                              ^~~
/home/miki/dumpefs_cpp/include/fs/f3s_spec.h:96:97: error: use of parameter outside function body before ‘.’ token
   96 | inline constexpr auto operator*(const std::string_view& sv, size_t times) -> std::array<char, sv.length() * times> {
      |                                                                                                 ^
/home/miki/dumpefs_cpp/include/fs/f3s_spec.h:96:114: error: use of parameter outside function body before ‘>’ token
   96 | inline constexpr auto operator*(const std::string_view& sv, size_t times) -> std::array<char, sv.length() * times> {
      |                                                                                                                  ^
/home/miki/dumpefs_cpp/include/fs/f3s_spec.h:96:23: error: deduced class type ‘array’ in function return type
   96 | inline constexpr auto operator*(const std::string_view& sv, size_t times) -> std::array<char, sv.length() * times> {
      |                       ^~~~~~~~
In file included from /usr/include/c++/11/tuple:39,
                 from /usr/include/c++/11/bits/unique_ptr.h:37,
                 from /usr/include/c++/11/memory:76,
                 from /home/miki/dumpefs_cpp/spdlog-1.11.0/include/spdlog/fmt/bundled/format.h:40,
                 from /home/miki/dumpefs_cpp/spdlog-1.11.0/include/spdlog/fmt/bundled/compile.h:11,
                 from /home/miki/dumpefs_cpp/include/fs/f3s_spec.h:23,
                 from /home/miki/dumpefs_cpp/src/dumpefs.cpp:15:
/usr/include/c++/11/array:95:12: note: ‘template<class _Tp, long unsigned int _Nm> struct std::array’ declared here
   95 |     struct array
      |            ^~~~~

Looks like char pointer in constexpr is not allowed, but how to do char copy? And, when even on function return type std::array<char, sv.length() * times>, the argument 2 is not allowed even they are const?

Below is my c++ compiler:

  • C++ Compiler Path: /usr/bin/g++11
  • C++ Compiler Version: 11.3.0
  • C++ Compiler Options: -fdiagnostics-color=always -g -O0

Solution

  • Function parameters are not constexpr. so sv.length() * times cannot be used to define array size.

    You need C++20 to allow to return std::string in constexpr function (even if you cannot have constexpr std::string):

    constexpr auto operator*(const std::string_view& sv, size_t times)
    {
      std::string result;
      for(std::size_t i=0; i<times; ++i){
        result += sv;
      }
      return result;
    }
    

    Alternative would be to pass char sequence:

    template <char... cs>
    struct char_sequence
    {
        static constexpr char str[sizeof...(cs) + 1] = {cs..., '\0'};
    };
    
    template <char... cs1, char... cs2>
    constexpr auto operator+(char_sequence<cs1...>, char_sequence<cs2...>)
    {
        return char_sequence<cs1..., cs2...>{};
    }
    
    template <std::size_t N, char... cs>
    constexpr auto repeat(char_sequence<cs...>)
    {
        using seq = char_sequence<cs...>;
        return []<std::size_t... Is>(std::index_sequence<Is...>){
            return ((static_cast<void>(Is), seq{}) + ...);
        }(std::make_index_sequence<N>());
    }
    

    Demo