Я пытаюсь сгенерировать простую строку формата для 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...>;
}
};
Да, ошибка, которую я перечислил, была связана с упрощенной попыткой просто использовать шаблон concat<>, который я получил с другой страницы SO. Я не привязан к этому решению конкатенации строк. Мой вопрос здесь не в том, чтобы найти исправление для одной ошибки, а в том, чтобы найти полезное решение для создания всей строки, которая мне нужна.
Проблема в том, что код, который вы показываете, и ошибка, о которой вы говорите, не совпадают, поэтому мы не знаем, с чего начать. Либо измените код, чтобы мы получили то же сообщение об ошибке, либо покажите фактическое сообщение об ошибке для кода, который вы показали. Смешивать их редко бывает хорошей идеей.
Код, который я представил, должен был показать общее представление о том, чего я пытаюсь достичь. Ошибка, которую я выделил, должна была проиллюстрировать трудности, с которыми я столкнулся с кодом concat<>. Не стесняйтесь игнорировать это, если это сбивает вас с толку. Jarod42 предоставил решение, основанное на моем коде ниже, но, к сожалению, для этого требуется более новый компилятор, чем я могу использовать в этой ситуации.
У вас есть несколько опечаток (и 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.
В частности: <источник>: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' — это переменная, а не адрес переменной
@RobW.: Добавить альтернативу с функциями constexpr
(сделано на C++14).
Идеальный! Функция constexpr мне нравится намного больше, чем беспорядок с шаблонами, за которым я изначально гнался.
Это не та ошибка, которую я получаю. Укажите минимальный воспроизводимый пример