Вывести постоянную литеральную длину и количество параметров можно с помощью func(const char(&)[N], Args...)C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Вывести постоянную литеральную длину и количество параметров можно с помощью func(const char(&)[N], Args...)

Сообщение Anonymous »

Я делаю функцию, похожую на std::format, она должна принимать на вход константный строковый литерал и набор аргументов для подстановки.
Во время компиляции необходимо разобрать строку и подготовить массив с описанием параметров подстановки.
И для этого хотелось бы при компиляции в одном параметре получить и длину строки подстановки, и количество аргументов в виде констант времени компиляции.
Но я не может описать параметр таким образом, чтобы компилятор мог определить требуемый тип. Это работает, только если я дублирую строку подстановки дважды:

Код: Выделить всё

template struct const_lit;

template
struct const_lit {
enum {Count = N};
};

template
struct param_info {
// Information about a portion of the pattern - this is either a piece of the pattern of length `len`, starting from `from`, or an argument with index `from`.
struct portion {
portion() = default;
unsigned from: 16 = 0;
unsigned len: 15 = 0;
unsigned is_param: 1 = 0;
};
// A string of length PatternLen can have a maximum of 1 + (PatternLen - 2 * 2 / 3) portions (PatternLen includes 0)
// For example "-{}-{}-{}-" - one portion of one character at the beginning of the string and two portions for every three characters.
// PatternLen == 11, portions: [0, 1] [p0] [3, 1] [p1] [6, 1] [p2] [9, 1]
portion portions_[1 + (PatternLen - 2 * 2 / 3)];
unsigned actual_used_{}; // Here we will save how many portions actually turned out during pattern parsing

consteval param_info(const char(&pattern)[PatternLen]) {
// parse pattern, fill portions_ and set actual_used_
.....
}
};

template
constexpr auto subst(T&& dummy_for_deduce, const param_info& pattern, Args&&...args) {
/// We take the pre-calculated portions_ from the pattern and fill the result based on the information in them
......
}

#define SUBST(par) par, par

void test() {
// This works
subst(SUBST("test {2}, {1}"), 1, 2); // Expands into the construct -> subst("test {2}, {1}", "test {2}, {1}", 1, 2);
// Based on the first "test {2}, {1}" - Count = 14 is deduced, based on 1, 2 - int, int is deduced.
// Then all the types for the second parameter become known, and the second "test {1}, {2}" is converted to
// param_info
}
Можно ли добиться того же, но используя шаблон только один раз?
Дело в том, что мне нужно знать длину строки шаблона как константы компиляции, чтобы использовать ее для расчета максимального размера массива
порций информации о шаблоне. Одно из решений — просто игнорировать длину шаблона и либо заранее взять большой размер
массива:

Код: Выделить всё

    portion portions_[128];
что приводит к ненужной трате памяти или сосредоточению внимания на количестве аргументов:

Код: Выделить всё

    portion portions_[sizeof...(Args) * 2 + 1];
но это не будет работать с шаблонами типа "{1}-{1}-{1}-{1}".
Я сделал вариант с использованием шаблона один раз, как в std::format, где на этапе компиляции строка шаблона только проверяется на корректность,
а потом во время выполнения она каждый раз анализируется заново, но это проигрывает во времени выполнения примерно в два раза,
судя по бенчмаркам (последний и предыдущий тесты)

Подробнее здесь: https://stackoverflow.com/questions/798 ... -charn-arg
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»