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

Я пытаюсь сгенерировать простую строку формата для 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
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
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

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