2

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
康桓瑋
  • 33,481
  • 5
  • 40
  • 90
MikimotoH
  • 393
  • 1
  • 4
  • 16
  • Simply put: return type (array's size) cannot depend on the argument. `sv.length()` in `-> std::array` is illegal. I am not sure how, but maybe `consteval` can make it work. – R2RT Apr 14 '23 at 12:31

2 Answers2

4

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

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • But at first, how can I convert string (maybe char[]) to `struct char_sequence` ? Should I do it manually? – MikimotoH Apr 18 '23 at 09:46
  • I know 2 non manual ways, UDL with gnu extension or MACRO, see [a-compile-time-evaluated-char-n](https://stackoverflow.com/questions/72520679/a-compile-time-evaluated-char-n/72520959#72520959) for UDL solution and link to MACRO. – Jarod42 Apr 18 '23 at 12:16
0

The following code might solve your problem. I use std::integer_sequence and constexpr lambda tricks to generate a compile-time char array consisting of '{}{}{}...'. Pay attention that both the char array and the string_view should be declared static so that they're generated during compilation.

#include <array>
#include <format>
#include <iostream>
#include <string_view>

template <size_t _Cnt>
consteval auto make_format_string_placeholders() -> std::array<char, _Cnt * 2>
{
    return []<size_t... _Idx>(std::integer_sequence<size_t, _Idx...> &&) {
        return std::array{ (_Idx % 2 == 0 ? '{' : '}')... };
    }(std::make_index_sequence<_Cnt * 2>{});
}

template <size_t _Cnt>
struct format_string_placeholders {
    static constexpr auto arr = make_format_string_placeholders<_Cnt>();
    static constexpr auto str = std::string_view{ std::data(arr), _Cnt * 2 };
};

void print(const auto &...args)
{
    std::cout << std::format(format_string_placeholders<sizeof...(args)>::str, args...) << std::endl;
}

Although this might be a bit off-topic, I'd like to say that a simpler solution might be using fold expressions.

void print(const auto &...args)
{
    (std::cout << ... << args) << std::endl;
}

You might run the code here. See line 303-309 of the assembly, which proves that '{}{}{}...' is a compile-time char array.