Генерация строки простого формата во время компиляции

Я пытаюсь сгенерировать простую строку формата для fmt во время компиляции, но не могу понять конкатенацию строк. Я ограничен С++ 14.

Я хотел бы иметь возможность генерировать строку формата для N элементов, чтобы ее можно было использовать следующим образом: auto my_string = fmt::format(format_string_struct<2>::format_string, "Item", "key1", "value1", "key2", "value2");

Строка сгенерированного формата будет выглядеть так: "{} {}={} {}={}", в результате чего получится отформатированная строка "Item key1=value1 key2=value2". Основой строки формата будет "{}", и она добавляет " {}={}" для каждого N.

Я пытаюсь использовать рекурсивные шаблоны, но не могу заставить все работать. Я получил некоторый код от Объединить строки времени компиляции в шаблоне во время компиляции? для объединения строк (хотя я получаю ошибку undefined reference to `concat_impl<&base_format_string_struct::format_string, std::integer_sequence<int, 0, 1>, &format_string_parameter_struct::parameter_string, std::integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7> >::value')

template<int...I> using is      = std::integer_sequence<int,I...>;
template<int N>   using make_is = std::make_integer_sequence<int,N>;

constexpr auto size(const char*s) { int i = 0; while(*s!=0){++i;++s;} return i; }

template<const char*, typename, const char*, typename>
struct concat_impl;

template<const char* S1, int... I1, const char* S2, int... I2>
struct concat_impl<S1, is<I1...>, S2, is<I2...>> {
    static constexpr const char value[]
    {
        S1[I1]..., S2[I2]..., 0
    };
};

template<const char* S1, const char* S2>
constexpr auto concat {
    concat_impl<S1, make_is<size(S1)>, S2, make_is<size(S2)>>::value
};

struct base_format_string_struct {
    static constexpr const char format_string[] = "{}";    
};

struct format_string_parameter_struct {
    static constexpr const char parameter_string[] = " {}=\"{}\"";
};

template <int arg_count, const char[]... params>
struct format_string_struct:
    public format_string_struct<arg_count - 1, format_string_parameter_struct, params...> {
};

template <int arg_count>
struct format_string_struct:
    public format_string_struct<arg_count - 1, format_string_parameter_struct> {
};

template <const char[]... params>
struct format_string_struct<0> {
  static const char* format_string() {
    return concat<base_format_string_struct, params...>;
  }
};


Это не та ошибка, которую я получаю. Укажите минимальный воспроизводимый пример

Ted Lyngmo 06.04.2022 18:59

Да, ошибка, которую я перечислил, была связана с упрощенной попыткой просто использовать шаблон concat<>, который я получил с другой страницы SO. Я не привязан к этому решению конкатенации строк. Мой вопрос здесь не в том, чтобы найти исправление для одной ошибки, а в том, чтобы найти полезное решение для создания всей строки, которая мне нужна.

Rob W. 06.04.2022 20:03

Проблема в том, что код, который вы показываете, и ошибка, о которой вы говорите, не совпадают, поэтому мы не знаем, с чего начать. Либо измените код, чтобы мы получили то же сообщение об ошибке, либо покажите фактическое сообщение об ошибке для кода, который вы показали. Смешивать их редко бывает хорошей идеей.

Ted Lyngmo 06.04.2022 20:07

Код, который я представил, должен был показать общее представление о том, чего я пытаюсь достичь. Ошибка, которую я выделил, должна была проиллюстрировать трудности, с которыми я столкнулся с кодом concat<>. Не стесняйтесь игнорировать это, если это сбивает вас с толку. Jarod42 предоставил решение, основанное на моем коде ниже, но, к сожалению, для этого требуется более новый компилятор, чем я могу использовать в этой ситуации.

Rob W. 06.04.2022 20:50
Получение данных из формы с помощью JavaScript - краткое руководство
Получение данных из формы с помощью JavaScript - краткое руководство
Получить данные из формы с помощью JS очень просто: вы запрашиваете элемент формы, передаете его конструктору new FormData() и, наконец, получаете...
Пользовательские правила валидации в Laravel
Пользовательские правила валидации в Laravel
Если вы хотите создать свое собственное правило валидации, Laravel предоставляет возможность сделать это. Создайте правило с помощью следующей...
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
1
4
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У вас есть несколько опечаток (и concat ограничен двумя аргументами).

template<int...I> using is      = std::integer_sequence<int,I...>;
template<int N>   using make_is = std::make_integer_sequence<int,N>;

constexpr auto size(const char*s) { int i = 0; while(*s!=0){++i;++s;} return i; }

template<const char*, typename, const char*, typename>
struct concat_impl;

template<const char* S1, int... I1, const char* S2, int... I2>
struct concat_impl<S1, is<I1...>, S2, is<I2...>> {
    static constexpr const char value[]
    {
        S1[I1]..., S2[I2]..., 0
    };
};

template<const char* S1, const char* S2, const char*... Ss>
struct concat
{
    constexpr static const char* value = concat_impl<S1, make_is<size(S1)>, concat<S2, Ss...>::value, make_is<size(concat<S2, Ss...>::value)>>::value;
};

template<const char* S1, const char* S2>
struct concat<S1, S2>
{
    constexpr static const char* value = concat_impl<S1, make_is<size(S1)>, S2, make_is<size(S2)>>::value;
};

struct base_format_string_struct {
    static constexpr const char format_string[] = "{}";    
};

struct format_string_parameter_struct {
    static constexpr const char parameter_string[] = " {}=\"{}\"";
};

template <int arg_count, const char*... params>
struct format_string_struct:
    public format_string_struct<arg_count - 1, format_string_parameter_struct::parameter_string, params...> {
};

template <int arg_count>
struct format_string_struct<arg_count>:
    public format_string_struct<arg_count - 1, format_string_parameter_struct::parameter_string> {
};

template <const char*... params>
struct format_string_struct<0, params...> {
  static const char* format_string() {
    return concat<base_format_string_struct::format_string, params...>::value;
  }
};

Демо

Но я бы, вероятно, пошел с функциями constexpr:

#if 1 // missing some constexpr for std::array/std::copy

template <typename T, std::size_t N>
struct my_array
{
    T data[N];
};

template <typename CIT, typename IT>
constexpr void copy(CIT begin, CIT end, IT dest)
{
    for (CIT it = begin; it != end; ++it)
    {
        *dest = *it;
        ++dest;
    }
}
#endif

template <std::size_t N>
constexpr my_array<char, 2 + 8 * N + 1> format_string_struct_impl()
{
    my_array<char, 2 + 8 * N + 1> res{};
    constexpr char init[] = "{}"; // size == 2
    constexpr char extra[] = R"( {}="{}")"; // size == 8
    copy(init, init + 2, res.data);
    for (std::size_t i = 0; i != N; ++i) {
        copy(extra, extra + 8, res.data + 2 + i * 8);
    }
    return res;
}

template <std::size_t N>
constexpr my_array<char, 2 + 8 * N + 1> format_string_struct()
{
    // Ensure the computation is done compile time.
    constexpr auto res = format_string_struct_impl<N>();
    return res;
}

Демо

К сожалению, у меня это не работает, так как для этого требуется С++ > 14.

Rob W. 06.04.2022 20:00

В частности: <источник>:23:145: ошибка: 'concat<(& format_string_parameter_struct::parameter_string), (& format_string_parameter_struct::parameter_string)>::value' не является допустимым аргументом шаблона, поскольку 'concat<(& format_string_parameter_struct:: parameter_string), (& format_string_parameter_struct::parameter_string)>::value' — это переменная, а не адрес переменной

Rob W. 06.04.2022 20:06

@RobW.: Добавить альтернативу с функциями constexpr (сделано на C++14).

Jarod42 07.04.2022 13:17

Идеальный! Функция constexpr мне нравится намного больше, чем беспорядок с шаблонами, за которым я изначально гнался.

Rob W. 07.04.2022 14:59

Другие вопросы по теме