powered by simpleCommunicator - 2.0.58     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / C++ [игнор отключен] [закрыт для гостей] / Вариативные шаблоны C++ и вариативные функции Си
55 сообщений из 55, показаны все 3 страниц
Вариативные шаблоны C++ и вариативные функции Си
    #39951365
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
По мотивам статьи вот здесь .

Если коротко, человек написал вариативный шаблон функции, которая есть быть обёрткой вокруг `std::printf()`. Вариативные аргументы шаблона передаются «as is» в `printf()`. Если отбросить нюансы семантики копирования и т.п. Ну...

Вопрос № 1: насколько это соответствует стандарту С++? Они бинарно совместимы?

Идём далее. Пример из его статьи:

Код: plaintext
1.
2.
3.
4.
5.
6.
template <typename ... Args>
void Print(char const * const format,
           Args const & ... args) noexcept
{
    printf(format, Argument(args)...);
}



Вопрос № 2: в какой последовательности согласно стандарту вызывается функция `Argument()` для вариативных шаблонных параметров? Это определено стандартом или нет?
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951416
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav
Вопрос № 1: насколько это соответствует стандарту С++? Они бинарно совместимы?

Соответствует.
Бинарная совместимость не требуется. Это два ортогональных механизма.
Распаковка пакета параметров оператором "..." - это просто синтаксический сахар и все работает также как если бы вы аргументы передавали в printf явным перечислением.
petrav
Вопрос № 2: в какой последовательности согласно стандарту вызывается функция `Argument()` для вариативных шаблонных параметров? Это определено стандартом или нет?

Порядок вычисления аргументов неопределен (IIRC, кроме некоторых исключений для перегруженных операторов в свежих версиях стандарта) независимо от того как подставляются аргументы, из вариативного пакета параметров или явным перечислением. А какая разница? Это не влияет на рабочесть примера.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951428
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
petrav
Вопрос № 1: насколько это соответствует стандарту С++? Они бинарно совместимы?

Соответствует.
Бинарная совместимость не требуется. Это два ортогональных механизма.
Распаковка пакета параметров оператором "..." - это просто синтаксический сахар и все работает также как если бы вы аргументы передавали в printf явным перечислением.

Окей, я Вас понял.

Anatoly Moskovsky
petrav
Вопрос № 2: в какой последовательности согласно стандарту вызывается функция `Argument()` для вариативных шаблонных параметров? Это определено стандартом или нет?

Порядок вычисления аргументов неопределен (IIRC, кроме некоторых исключений для перегруженных операторов в свежих версиях стандарта) независимо от того как подставляются аргументы, из вариативного пакета параметров или явным перечислением. А какая разница? Это не влияет на рабочесть примера.

А разница **большая**. Я хочу разработать обёртку вокруг `printf()` которая хотя бы на этапе исполнения проверяла строку формата и типы переданных аргументов. Лучше на этапе компиляции.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951431
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky,

Для этого мне нужно что бы функция `Argument()` вызывалась последовательно в вышеприведённом примере.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951458
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav
А разница **большая**. Я хочу разработать обёртку вокруг `printf()` которая хотя бы на этапе исполнения проверяла строку формата и типы переданных аргументов. Лучше на этапе компиляции.

Для этого мне нужно что бы функция `Argument()` вызывалась последовательно в вышеприведённом примере.

Зачем для этого последовательно вызывать какую-то функцию для каждого аргумента?
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951463
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
petrav
А разница **большая**. Я хочу разработать обёртку вокруг `printf()` которая хотя бы на этапе исполнения проверяла строку формата и типы переданных аргументов. Лучше на этапе компиляции.

Для этого мне нужно что бы функция `Argument()` вызывалась последовательно в вышеприведённом примере.

Зачем для этого последовательно вызывать какую-то функцию для каждого аргумента?

Ну идея такая. Мы перегружаем функцию `Argument()` для каждого стандартного типа, но одновременно передаём в `Argument()` парсер формата, который в своём состоянии хранит текущий указатель на символ строки формата и мы можем проверить соответствие типа в формате и того, что передано в вариативных шаблонных аргументах.

Для не стандартных типов срабатывает шаблонная перегрузка и там `static_assert`.


Но лучше конечно compile time.

PS: Да, придётся написать свой парсер формата `printf()`, но я думаю это макс на пару дней работы без написания тестов. Т.е. на коленке.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951485
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky,

Если у Вас есть какое-то лучшее решение в стиле:

Код: plaintext
1.
2.
3.
4.
for (auto const &curr: Parametrs)
{
    checkFormat(parser, curr);
}



Так Вы сообщите, плиз. Я не особо изучал С++17.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951498
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav,

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
template <typename ... Args>
bool validate_format(const char* fmt)
{
    // распарсить формат и проверить его соответствие типам Args 
    // например используя рекурсивное применение вспомогательного шаблона к Args
    return ...;
}


template <typename ... Args>
void my_print(const char* fmt, Args ... args)
{
    if (!validate_format<Args...>(fmt))
        throw std::logic_error("invalid format");
    printf(fmt, args...);
}



Это в рантайме.
В компайл-тайме тоже наверно как-то можно, лень думать.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951500
a guest
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
petrav
Но лучше конечно compile time.

PS: Да, придётся написать свой парсер формата `printf()`, но я думаю это макс на пару дней работы без написания тестов. Т.е. на коленке.
Код: plaintext
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat-error")
Проверка форматной строки в compile-time готова.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951504
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky

Это в рантайме.

Это замечательно, но...

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
template <typename ... Args>
bool validate_format(const char* fmt)
{
    // распарсить формат и проверить его соответствие типам Args 
    // например используя рекурсивное применение вспомогательного шаблона к Args
    return ...;
}


Я примерно понимаю о чём вы говорите. Наш рекурсивый шаблон зависит от типа первого параметра, он наследуется от самого себя, но предок зависит от типа второго параметра и т.д. Да? Но как это сделать?! Как пронавигировать по Args? Пожалуйста. :)

Anatoly Moskovsky

В компайл-тайме тоже наверно как-то можно, лень думать.

Ну мы тут ни куда же не торопимся. Лень, так лень. Подумаем через неделю.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951528
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav,

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
struct FmtArg
{
    // some type info
};

template <typename T>
bool validate_format_arg(FmtArg& fmt_arg)
{
    // check if T can be used with FmtArg
    return true;
}

// recursion terminator
template <size_t>
bool validate_format_args(std::vector<FmtArg>&)
{
    return true;
}

template <size_t ArgPos, typename T, typename ... Rest>
bool validate_format_args(std::vector<FmtArg>& parsed_fmt)
{
    if (!validate_format_arg<T>(parsed_fmt[ArgPos]))
        return false;
    return validate_format_args<ArgPos + 1, Rest...>;
}

std::vector<FmtArg> parse_format(const char* fmt)
{
    // implement real parse
    std::vector<FmtArg> rv;
    while (auto c = *fmt++) {
        if (c == '%')
            rv.push_back({});
    }
    return rv;
}

template <typename ... Args>
bool validate_format(const char* fmt)
{
    // распарсить формат и проверить его соответствие типам Args
    auto parsed_fmt = parse_format(fmt);
    if (parsed_fmt.size() != sizeof...(Args))
        return false;
    // например используя рекурсивное применение вспомогательного шаблона к Args
    return validate_format_args<0, Args...>(parsed_fmt);
}


template <typename ... Args>
void my_print(const char* fmt, Args ... args)
{
    if (!validate_format<Args...>(fmt))
        throw std::logic_error("invalid format");
    printf(fmt, args...);
}

int main()
{
    my_print("%i %s\n", 2, "a");
    return 0;
}
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951580
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav,

рекурсивные шаблоны - зло, особенно для компилятора, их место уже занял fold expression

в качестве примера https://www.qt.io/blog/efficient-qstring-concatenation-with-c17-fold-expressions
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951646
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
рекурсивные шаблоны - зло, особенно для компилятора, их место уже занял fold expression

Вообще-то fold expr не применим к типам.
Так что пока не добавят в стандарт итерацию по списку типов придется использовать рекурсивные шаблоны.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951658
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В принципе если еще дальше перейти в рантайм, то можно избавиться от рекурсивных шаблонов.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
struct FmtArg
{
    // some type info
};

bool compatible_formats(const FmtArg& a, const FmtArg& b)
{
    // check a is compatible with b
    return true;
}

template <typename T>
FmtArg make_format()
{
    // make FmtArg from T
    return FmtArg{};
}

std::vector<FmtArg> parse_format(const char* fmt)
{
    // implement real parse
    std::vector<FmtArg> rv;
    while (auto c = *fmt++) {
        if (c == '%')
            rv.push_back({});
    }
    return rv;
}

template <typename ... Args>
bool validate_format(const char* fmt)
{
    // распарсить формат и проверить его соответствие типам Args
    auto parsed_fmt = parse_format(fmt);
    if (parsed_fmt.size() != sizeof...(Args))
        return false;
    size_t i = 0;
    for (auto& arg_fmt: { make_format<Args>() ... }) {  // тут превращаем типы в значения
        if (!compatible_formats(parsed_fmt[i++], arg_fmt))
            return false;
    }
    return true;
}


template <typename ... Args>
void my_print(const char* fmt, Args ... args)
{
    if (!validate_format<Args...>(fmt))
        throw std::logic_error("invalid format");
    printf(fmt, args...);
}

int main()
{
    my_print("%i %s\n", 2, "a");
    return 0;
}



PS. Правда fold expressions и тут не нужны ))
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951659
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Какой смысл затаскивать в обсуждение Си? Это - контр-продуктивно вообще.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951687
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
Вообще-то fold expr не применим к типам.

прекрасно пременим, если их состав известен (при необходимости может быть расширен за счет добавления нужных специализаций).

я привел пример, в котором производится конкатенация строк, но кто мешает расширить состав обрабатываемых типов, раз уж тут пытаются сэмулировать работу printf, набор типов которой описывается форматной строкой, где каждому типу сопоставлена лексема?

[spoiler] Если примера недостаточно, то вот еще один,
YouTube Video
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951688
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
В принципе если еще дальше перейти в рантайм, то можно избавиться от рекурсивных шаблонов.

Ого... А говорили думать лень. Я правда просил только пример рекурсивного шаблона, но Ваш развёрнутый код очень познавателен. Спасибо!

Но уходить желательно в compile time. Если взять за основу Ваш первый прототип... То имхо, std::vector не нужен. Проходить по строке формата нужно одновременно с навигацией по типам. Оказались в перегрузке для double -- начали искать в строке формата `%f`. Тогда состояние парсера будет описываться одним указателем на char. И, наверное, можно будет сделать compile time.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951692
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Какой смысл затаскивать в обсуждение Си? Это - контр-продуктивно вообще.

Может printf() кривой, но он очень удобен и производителен. Тут у меня код программный... ему уже лет 30-ть. Тут вообще хардкор нереал. :) Но он работает...
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951695
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav,

на мой взгляд пройтись по типам в variadic pack'e - вообще не проблема, проблема будет в том, чтобы в компайл тайме обеспечить парсинг фоматной строки, если каждому типу в ней не будет отведено строго обозначенное кол-во символов, чего в реальном printf не было никогда.

Например, тип може быть задан с префиксом указывающем точность округления - %0.5f, и тогда это уже не будет %f для compile-time парсера.

Если же у вас форматная строка всегда четко вида %[type symbol], то можно попробовать в complle time ее разобрать на составляющие (я не пробовал, но если форматная строка - лексема, то думаю, что это реально). А типы сопоставить - это фигня.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951700
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
petrav,

на мой взгляд пройтись по типам в variadic pack'e - вообще не проблема, проблема будет в том, чтобы в компайл тайме обеспечить парсинг фоматной строки, если каждому типу в ней не будет отведено строго обозначенное кол-во символов, чего в реальном printf не было никогда.

Например, тип може быть задан с префиксом указывающем точность округления - %0.5f, и тогда это уже не будет %f для compile-time парсера.

Если же у вас форматная строка всегда четко вида %[type symbol], то можно попробовать в complle time ее разобрать на составляющие (я не пробовал, но если форматная строка - лексема, то думаю, что это реально). А типы сопоставить - это фигня.

Нет проблем написать парсер и %f и %0.5f и вообще всех вариантов. Это простой формат, это ж не регулярные выражения. По крайней мере я так думаю.

За ссылки спасибо, прочитаю.

Вот Вы бы написали бы свой прототип? Но не стоит задачи написать свою библиотеку ввода вывода. Нужно создать обёртку вокруг printf().
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951707
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Какой смысл затаскивать в обсуждение Си? Это - контр-продуктивно вообще.

Ещё меня привлекает идея расширить стандартный формат:
Код: plaintext
1.
std::printf("%5.3ang", angle); // Тут автоматически перевод радиан в углы.


Конечно, перед передачей в оригинальный printf() нужно и строку формата подкорректировать до стандартной. Но это уже в run time, конечно.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951725
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я думаю что тебе нужна Scala и ее implicit conversions.
Ты - постигнешь дзен и твоя душа придёт в состояние гармонии.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951728
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Очередной этап изысканий.

Cerebrum

Например, тип може быть задан с префиксом указывающем точность округления - %0.5f, и тогда это уже не будет %f для compile-time парсера.


Вроде работает. Есть конечно вероятность, что в определённый момент компилятор не выдержит сложности парсера. И я не уверен, что третий тестовый формат правильный. Но такой вариант вообще запретить нужно. Сузить стандартный формат, а потом расширить нестандартными форматами -- до задач предметной области.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
constexpr bool checkCharOrNo(char const *&frmt, char const ch)
{
    if (*frmt == ch)
    {
        ++frmt;
        return true;
    }
    return false;
}

constexpr bool checkDigitOrNo(char const *&frmt)
{
    while (*frmt >= '0' && *frmt <= '9')
    {
        ++frmt;
    }
    return true;
}

constexpr bool checkFloat(char const *frmt)
{
    while (*frmt != '\0')
    {
        if (checkCharOrNo(frmt, '%'))
        {
            checkDigitOrNo(frmt);
            if (checkCharOrNo(frmt, '.'))
            {
                checkDigitOrNo(frmt);
            }
            return *frmt == 'f';
        }
        ++frmt;
    }
    return false;
}

static_assert(checkFloat("Angle: %f."));
static_assert(checkFloat("Angle: %10f."));
static_assert(checkFloat("Angle: %10.f.")); // Запретить в своём парсере.
static_assert(checkFloat("Angle: %10.3f."));
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39951767
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
прекрасно пременим, если их состав известен (при необходимости может быть расширен за счет добавления нужных специализаций).

Не применим. Потому что в С++ нет бинарных операторов применимых к типам. А fold expression это применение операторов к списку сущностей. И поскольку есть только операторы применимые к значениям, то fold expression возможен только для пакета параметров, но не пакета типов этих параметров.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953477
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Господа, нам осталось решить одну проблему: как в одном вызове совместить и статическую проверку данных и динамическую печать данных? Желательно найти выход без макросов. Неужели выхода нет?! :-((( Вот псевдокод, он не компилируется:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
constexpr bool check(char const *str)
{
    return str != nullptr;
}

constexpr void print_f(char const *str)
{
    static_assert(check(str)); // Compile time.
    std::printf(str);          // Run time.
}


А вот жуткое решение на вариативных макросах:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
#if (DEBUG)
#define print_f(format, ...)                       \
    static_assert(checkArgs(format, __VA_ARGS__)); \
    printStr(format, __VA_ARGS__);
#elif (RELEASE)
// В Release хотим компилироваться без макросов. Для очистки совести.
#endif

template <size_t Size, typename... Parameters>
constexpr bool checkArgs(char const (&format)[Size],
                         Parameters const &... parameters)
{
    // В прототипе проверим длину формата и наличие нуля.
    char const *end = format;
    while (*end++ != '\0');
    size_t const len = end - format - 1;
    return len>0 && len<128;
}

// Тут начинается тихий ужас. В зависимости от конфигурации имя функции печати
// должно быть разным.
template <size_t Size, typename... Parameters>
constexpr void
#if (DEBUG)
printStr
#else
print_f
#endif
(char const (&format)[Size], Parameters const &... parameters)
{
    if (!checkArgs(format, parameters...)) throw -1; // Для Release.
    std::snprintf(buff, std::size(buff), format, parameters...);
}

void newTest()
{
    print_f("Angle: %0.3f. Mode: %s.", 12.8, "Prepare");
}



Заранее спасибо!
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953518
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav,

уже говорил и скажу еще раз, для тех кто в танке - юзай fold expression
моя идея представлена ниже (это только набросок, но он вполне пригоден для ее понимания и даже компилируется, проверил)
дальше доводить ее до ума придется тебе самому

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
#include <array>

template <size_t N>
using static_string = std::array<const char, N>;

template <size_t N, size_t ...Indexes>
constexpr static_string<N> make_static_string(const char (&str)[N], std::index_sequence<Indexes...>)
{
	return {str[Indexes] ..., '\0'};
}

template <size_t N, typename Indexes = std::make_index_sequence<N - 1>>
constexpr static_string<N> make_static_string(const char (&str)[N])
{
	return make_static_string(str, Indexes{});
}

template <typename T>
struct type_processor;

template <>
struct type_processor<float>
{
	template <size_t N>
	constexpr explicit type_processor(const static_string<N>& fmt, float fValue, char** ppOutBuff) : value(fValue), pp_out_buff(ppOutBuff)
	{
		// я не знаю (нет ни времени ни желания разбираться) как сделать проверку типа в форматной строке в compile time
		// после проверки форматная строка должна быть смещена на X позиций, чтобы следующая итерация проверяла следующий
		// тип в форматной строке

		static_assert(N >= 2 /*&& fmt[0] == '%' && fmt[1] == 'f'*/);
	}

	constexpr bool operator ()(void)
	{
		// TODO: конвертируем value в строку и записываем в pp_out_buff 
		
		// смещаем указатель pp_out_buff за блок записанных данных
		return true;
	}

	char**	pp_out_buff;
	float	value;
};

template <>
struct type_processor<int>
{
	template <size_t N>
	constexpr explicit type_processor(const static_string<N>& fmt, int nValue, char** ppOutBuff) :	value(nValue), pp_out_buff(ppOutBuff)
	{
		static_assert(N >= 2 /*&& fmt[0] == '%' && fmt[1] == 'f' && ppOutBuff != nullptr*/);
	}

	constexpr bool operator ()(void) 
	{
		// TODO: конвертируем value в строку и записываем в pp_out_buff
		
		// смещаем указатель pp_out_buff за блок записанных данных;
		return true;
	}

	char**	pp_out_buff;
	int		value;
};

template <>
struct type_processor<const char*>
{
	template <size_t N>
	constexpr explicit type_processor(const static_string<N>& fmt, const char* pValue, char** ppOutBuff) :	value(pValue), pp_out_buff(ppOutBuff)
	{
		static_assert(N >= 2 /*&& fmt[0] == '%' && fmt[1] == 'f' && ppOutBuff != nullptr*/);
	}

	constexpr bool operator ()(void) 
	{
		// TODO: копируем value в pp_out_buff 
		
		// смещаем указатель pp_out_buff за блок записанных данных
		return true;
	}

	char**		pp_out_buff;
	const char*	value;
};

template <size_t N, typename ...ArgsT>
constexpr bool my_printf(const static_string<N>& fmt, ArgsT ...vArgs)
{
	// TODO: организовать автоматический рассчет буфера под результирующую строку
	// 
	size_t szLength = 0;
	// (type_processor<ArgsT>(fmt, vArgs, (char**)&outBuff).calculate_size(szLength) && ...);

	char outBuff[256] = {0};
	return (type_processor<ArgsT>(fmt, vArgs, (char**)&outBuff)() && ...);
}

int main()
{
	constexpr auto fmt = make_static_string("%f %d %s");
	my_printf(fmt, 0.56f, 10, "some string");
	return 0;
}


если ты попробуешь передать в my_printf значение неизвестного type_processor типа, то на этапе компиляции уже получишь ор. Добавив специализаций type_processor можно покрыть все стандартные типы, которые должна жрать и преобразовывать к строке my_printf, таким образом, можно расширять набор ее типов по мере необходимости. Все что не входит в этот круг - идет лесом на этапе компиляции. Сможешь сделать static_assert проверку куска форматной строки на соответствие типу type_process - будешь молодец!

Одна из больших проблем (опять же, как я говорил ранее) - нерегулярность форматной строки. В моем примере все типы четко фиксируемые двумя символами %[symbol]. Как проанализировать форматную строку в компайл тайме с нерегулярной структурой я хз, но кто-то говорил выше, что это - ерунда, ну, тогда я уверен, все у вас получится

Вторая большая проблема - размер буфера под выходные данные. В своем примере его задал статически, но если получится все что написано выше, то размер можно посчитать таким же макаром через fold expression. Это можно сделать в тех же type_expression специализациях, добавив в них функции calculate_size или переопределив оператор ().

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

может это https://habr.com/ru/post/428846/
еще поможет или натолкнет на какие-то мысли по анализу форматной строки в compile time

PS. я не стал заморачиваться с std::decay_t, но тебе придется
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953525
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum,

Код: plaintext
1.
return (type_processor<ArgsT>(fmt, vArgs, (char**)&outBuff)() && ...);


Ну и где здесь fold-expression над типом? )))
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953529
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav
Господа, нам осталось решить одну проблему: как в одном вызове совместить и статическую проверку данных и динамическую печать данных? Желательно найти выход без макросов. Неужели выхода нет?! :-((( Вот псевдокод, он не компилируется:


Чтобы применить static_assert к выражению, все его составляющие должны быть известны на этапе компиляции.
В частности это означает что строка формата не может быть аргументом функции, а должна быть передана как аргумент шаблона.
Поэтому без макроса нельзя обойтись если вы хотите чтобы для юзера все выглядело как обычная функция.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953535
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
Ну и где здесь fold-expression над типом? )))

в type_processor'e
специализация выбирается на основе типа, нет специализации - нет компиляции.

ясен пень, что fold expression не сделает цепочку типов, это и не подразумевалось,
странно, что приходится это объяснять, в особенности после того, как я привел аж два примера иллюстрирующих мое высказывание
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953541
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
ясен пень, что fold expression не сделает цепочку типов, это и не подразумевалось,
странно, что приходится это объяснять, в особенности после того, как я привел аж два примера иллюстрирующих мое высказывание

Начнем с того что fold было приведено как замена рекурсивной обработки списка типов в компайл-тайм.
При этом все примеры работают не с типами и не в компайл-тайм.
Если мы вместо типов работаем со значениями, то все что тут делает fold можно сделать и обычным циклом )))
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953568
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
petrav,

уже говорил и скажу еще раз, для тех кто в танке - юзай fold expression
моя идея представлена ниже (это только набросок, но он вполне пригоден для ее понимания и даже компилируется, проверил)
дальше доводить ее до ума придется тебе самому

Спасибо за код. :) На уровне концепции я его понял. Но он не будет работать в compile time. И Вы делаете немного не то что Вас просят. :)

Строку формата в стиле "%21.567f" я уже разбирал compile-time выше в этой ветке. Только что я написал рекурсивный обход по типам тоже compile time. Осталось совместить.

И погуглить как строку формата сделать аргументом шаблона.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953594
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum

в type_processor'e
специализация выбирается на основе типа, нет специализации - нет компиляции.

Кстати! В Вашем коде есть очень большой изъян. Нет специализации, но есть приведение типов! Именно поэтому у вас получилось написать специализацию для const char *, хотя в Вашем примере аргумент "some string" -- это не const char * ! Это массив. Вот Ваш тестовый код:

Код: plaintext
1.
my_printf(fmt, 0.56f, 10, "some string");



Т.е. Вы с таким подходом можете написать специализацию для double, но забыть специализацию для char. И Ваш код, при передаче char, будет компилироваться, но не будет проверять соответствие типов аргументов и строки формата. Даже в run time! Мне так кажется.

Т.е. правильно говорит Anatoly Moskovsky, Вы работаете не с типами. Вы работаете со значениями.

Вот мне пришлось помучится что бы написать специализацию для аргумента (не формата) в стиле "some string". Потому что эти рекурсивные шаблоны чётко руководствуются типом, а не значением.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953596
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav
Вот мне пришлось помучится что бы написать специализацию для аргумента (не формата) в стиле "some string"

Cerebrum
PS. я не стал заморачиваться с std::decay_t, но тебе придется

откуда вы только беретесь такие со своим Московским...

Модератор: Вложение удалено.

Модератор: Вложение удалено.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953604
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
petrav
Вот мне пришлось помучится что бы написать специализацию для аргумента (не формата) в стиле "some string"

Cerebrum
PS. я не стал заморачиваться с std::decay_t, но тебе придется

откуда вы только беретесь такие со своим Московским...

Так я и не утверждаю, что разбираюсь в С++17. Но не очень понятно, что будет в Вашем примере, если мы определим перегрузку (явную специализацию шаблона) только для double, подставим %12f в виде формата, подставим char в runtime как аргумент, а потом всё это передадим в snprintf(). Передача в snprintf() это требование.

И причём тут std::decay...
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953609
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
petrav
Вот мне пришлось помучится что бы написать специализацию для аргумента (не формата) в стиле "some string"

Cerebrum
PS. я не стал заморачиваться с std::decay_t, но тебе придется

откуда вы только беретесь такие со своим Московским...

Мне кажется, Вы задействовали механизм преобразования типов аргументов. А по ТЗ нужно однозначно и чётко проверить соответствие типов аргументов и строки формата. В compile time. Ну или хотя бы в run time, но однозначно.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953610
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav
И Вы делаете немного не то что Вас просят. :)

Ну так всегда бывает, когда узнал новую фичу, и теперь суешь ее везде без разбору )))
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953611
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav
И причём тут std::decay...

std::decay автоматически выведет тип указателя из типа строкового литерала (без явного указания типов)
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953771
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
petrav
И причём тут std::decay...

std::decay автоматически выведет тип указателя из типа строкового литерала (без явного указания типов)

Да, я это понял... со второго раза, правда. Но лучше поздно, чем... Я думаю "std::decay" тут ненужен. Он сносит дополнительную информацию с типов, а это ни к чему, мы всё проверим compile time. Я тут проще и надёжнее вещь спрограммировал. И Вы знаете, все "static_assert" работают, достаточно инвертировать условие -- и тут же код перестаёт собираться. Если аргумент "12.8" переделать на "12.8F", то код тоже перестаёт собираться. Т.е. корректность максимальная.

Вот тестовый код:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
void newTest()
{
    double d = std::rand();
    char* s = "Jim";

    bool const res1 =
        print_f("Angle: %0.3f. Mode: %s. Name: %s.", d, "Prepare", s);
    bool const res2 =
        print_f("My float: %f.", 12.8);
    bool const res3 =
        print_f("Hello, world !");
}



А вот код обхода типов compile time, основанный на Вашем примере.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
template <size_t N>
constexpr void processArray(char const (&)[N])
{
    static_assert(N > 0); // Я знаю, что массив не может быть размера ноль.
}

template <typename T>
constexpr void processType()
{
    // Для аргументов типов "строковый литерал".
    static_assert(std::is_array_v<T>);
    // А вот тут уже значения, а не типы, но приведение типов не работает с
    // массивами. Ну, наверное, не работает. Скорее всего…
    processArray(T{});
}

template <>
constexpr void processType<char *>()
{
}

template <>
constexpr void processType<double>()
{
}

template <size_t N>
constexpr void validateFormatArgs()
{
    static_assert(N == 0);
}

template <size_t N, typename T, typename... Rest>
constexpr void validateFormatArgs()
{
    processType<T>();
    validateFormatArgs<N-1, Rest...>();
}

template <size_t Size, typename... Parameters>
constexpr bool checkArgs(char const (&format)[Size],
                         Parameters const &... parameters)
{
    // Compile time навигация по типам.
    validateFormatArgs<sizeof...(Parameters), Parameters...>();
}
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953938
a guest
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
petrav
Если аргумент "12.8" переделать на "12.8F", то код тоже перестаёт собираться. Т.е. корректность максимальная.
В каком смысле «максимальная корректность», если
Код: plaintext
printf("My float: %f.", 12.8f);
это ОК?
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953946
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
a guest
petrav
Если аргумент "12.8" переделать на "12.8F", то код тоже перестаёт собираться. Т.е. корректность максимальная.
В каком смысле «максимальная корректность», если
Код: plaintext
printf("My float: %f.", 12.8f);
это ОК?

В каком смысле ОК? В плане стандарта C++, да ОК. В плане однозначной проверки типов -- супер ОК. Не написана специализация для "float" и выключено приведение типов. Компиляция ломается. Это Супер ОК. Только что написал специализацию для "float" -- тут же начало компилироваться.

Или Вы про различие "%f", "%lf" и "%Lf" ?
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953955
a guest
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
petrav
a guest
пропущено...
В каком смысле «максимальная корректность», если
Код: plaintext
printf("My float: %f.", 12.8f);
это ОК?

В каком смысле ОК?
В том смысле, что это корректное использование printf.
Просто странно читать про «максимальную корректность» чекера аргументов, который должен разрешать всё то, что допустимо для обычного printf, а он отказывается компилировать.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953968
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
a guest
petrav
пропущено...

В каком смысле ОК?
В том смысле, что это корректное использование printf.

Под «максимальной корректностью» имелась в виду возможность однозначно в compile time детектировать тип аргумента. И запретить приведение типов. Т.е. под корректностью имелась возможность чётко увидеть тип. И никаких "std::decay".

a guest
Просто странно читать про «максимальную корректность» чекера аргументов, который должен разрешать всё то, что допустимо для обычного printf, а он отказывается компилировать.

Нет… Чекер, наоборот должен запрещать всякие бессмысленные изыски. Зачем "%d", если есть "%i" ? На сайте cppreference написано, что "%f" и "%lf" эквивалентны. А `%Lf` -- это "long double", которая с 2005-го года не 80-т бит, а соответствует "double". Кстати, зачем нам аргументы типа "float" ? Да по-запрещать всё к чертям. Я такие бардаки годами разбираю. И везде какие-то глюки и косяки.

Нам нужно сузить формат, а потом расширить его на печать, например, углов. С авто. преобразованием радиан в углы. И это тоже уменьшит количество косяков.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953969
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А может, всё-таки проще компилятор сменить? Что вы так к убогой студии привязались?..
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39953970
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov
А может, всё-таки проще компилятор сменить? Что вы так к убогой студии привязались?..

Я так понял что помимо printf надо будет реализовать нестандартные форматы, и смена компилятора тут не поможет.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954005
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да все претензии по форматной печати можно адресовать к конкретной библиотеке. Вот ее и ругайте.

Компиллятор-то тут причем?
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954039
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov

А может, всё-таки проще компилятор сменить? Что вы так к убогой студии привязались?..

Студия прекрасна. Я почитал документацию, да хорошая штука:

Код: plaintext
1.
2.
3.
4.
5.
6.
void my_printf(int x, int y, char const *format, ...)
    __attribute__((format(printf, 3, 4)));

void my_printf(int x, int y, char const *format, ...)
{
}



В Студии есть аналогичный атрибут: _Printf_format_string_. И там ещё куча атрибутов, другое дело что активация этого анализатора кода у меня сборку проекта замедляет в шесть раз. И начинают лезть предупреждения на сторонние библиотеки. Не знаю что делать с этим.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954062
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
Dimitry Sibiryakov
А может, всё-таки проще компилятор сменить? Что вы так к убогой студии привязались?..

Я так понял что помимо printf надо будет реализовать нестандартные форматы, и смена компилятора тут не поможет.

Да... вот только...

Код: plaintext
1.
2.
3.
4.
5.
// Можно было бы написать так с преобразованием радиан в углы.
print_f("%ang", 0.3);
// Но зачем это делать если можно написать так?
std::printf("%f", toAng(0.3));
// В обоих случаях нужно явно указать, что в данных радианы.


Собственная compile time проверка формата с преобразованием значений в run time имела бы смысл в таких случаях:

Код: plaintext
1.
2.
3.
4.
Angle<double, AngType::Radian> ang = {};
double angles[3] = {};
std::string str;
print_f("%ang %ang_arr %s", ang, angles, str);


Смутные сомнения об overengineering меня, конечно, терзали с самого начала...
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954066
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav
Смутные сомнения об overengineering меня, конечно, терзали с самого начала...

на мой взгляд для type rich кода следует использовать operator "" с собственными суффиксами для типов и перегрузку operator <<, а не измываться над printf, который уже много раз предавали анафеме
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954067
Фотография Cerebrum
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
petrav
Смутные сомнения об overengineering меня, конечно, терзали с самого начала...

на мой взгляд для type rich кода следует использовать operator "" с собственными суффиксами для типов и перегрузку operator <<, а не измываться над printf, который уже много раз предавали анафеме
тогда никаких проблем с разделением углов в радианах от углов в градусах не будет
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954106
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Cerebrum
petrav
Смутные сомнения об overengineering меня, конечно, терзали с самого начала...

на мой взгляд для type rich кода следует использовать operator "" с собственными суффиксами для типов и перегрузку operator <<, а не измываться над printf, который уже много раз предавали анафеме

Вы имеете в виду операторы в стиле "123.4_rad" ? Да, штука хорошая, но это уже про написание литералов, а не про печать данных.

Вообще, анафеме нужно предать библиотеку <iostream>. Совершено непонятно о чём думали люди которые это проектировали. Ведь были же все языковые инструменты, что бы разработать type safe форматирование строк в стиле "printf()" -- как это сделали в "QString" и "boost::format", например.

Впрочем, C++ это язык для создания велосипедов и костылей. Может они неосознанно так проектировали <iostream>, что бы пользователей спровоцировать к творчеству через шок. Кстати, Страуструп, собственно, и писал, что C++ проектируется так, что бы пользователи могли расширять язык своими типами. Видимо многоуважаемый Бьёрн передал эту мысль другим проектантам и они накосячили не осознанно.

А может и осознанно...
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954189
Фотография Anatoly Moskovsky
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav,

Посмотрите еще на тему форматирования

1 https://en.cppreference.com/w/cpp/utility/format
2 https://github.com/fmtlib/fmt (в т.ч. имплементация std::format)
3 https://github.com/mpark/format
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954392
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
petrav,

Посмотрите еще на тему форматирования

1 https://en.cppreference.com/w/cpp/utility/format
2 https://github.com/fmtlib/fmt (в т.ч. имплементация std::format)
3 https://github.com/mpark/format

Спасибо, завтра начну изучать. Кстати, я тут нашёл простейший приём, который решает все проблемы:

Код: plaintext
1.
2.
3.
4.
int printf_wrapper_(const char *format, ...);

#define printf_wrapper(...) \
(false || printf(__VA_ARGS__), printf_wrapper_(__VA_ARGS__))


Просто и ясно. Студия тут же начинает выдавать предупреждения о неправильности формата и аргуменов. Правда тут же ломается перегрузка по теме printf()/wprintf().
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954426
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
petrav

Код: sql
1.
2.
3.
4.
int printf_wrapper_(const char *format, ...);

#define printf_wrapper(...) \
(false || printf(__VA_ARGS__), printf_wrapper_(__VA_ARGS__))


Просто и ясно. Студия тут же начинает выдавать предупреждения о неправильности формата и аргуменов. Правда тут же ломается перегрузка по теме printf()/wprintf().

Кстати, вот я уверен, что на таком коде PVS Studio сейчас в позу встанет и начнёт сыпать предупреждениями.

Что ещё нужно сделать, что бы просто начать программировать на C++, а не прорываться сквозь строй косяков?
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954529
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
petrav,

Посмотрите еще на тему форматирования

1 https://en.cppreference.com/w/cpp/utility/format

Пока Студия 2019 не поддерживает C++ 20.

Anatoly Moskovsky
2 https://github.com/fmtlib/fmt (в т.ч. имплементация std::format)

В этой библиотеке compile time check не работает как нужно. Что-то проверяет, но вот это компилируется, хотя оно и неверно:

Код: plaintext
1.
std::string s = fmt::format(FMT_STRING("Hello, {0}!"), "world", 1.1);


Я уж не говорю о том, что "FMT_STRING" -- это макрос.


Библиотека уже три года не поддерживается, не думаю что там есть какое-то откровение.
...
Рейтинг: 0 / 0
Вариативные шаблоны C++ и вариативные функции Си
    #39954710
petrav
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Anatoly Moskovsky
petrav
Господа, нам осталось решить одну проблему: как в одном вызове совместить и статическую проверку данных и динамическую печать данных? Желательно найти выход без макросов. Неужели выхода нет?! :-((( Вот псевдокод, он не компилируется:


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

Это я понимаю, но как же строку передать как аргумент шаблона? Гуглинг не помог, есть решения в стиле Boost.MPL, но там такие костыли и жесть нереальная. Ну и макросы. Я не думаю, что это хороший дизайн. Т.е. резко превышен болевой порог в плане костылизма.

Если явно строку не поместить как глобальную константу, то, видимо, её и нельзя передать как аргумент шаблона. По крайней мере по человечески. В принципе можно отступить от стандартного printf() style. Например так:

Код: plaintext
1.
my_print_f<fmt("%f")>(1.2);


Но я думаю это невозможно.

Если Вы забыли, напомню. Предлагается что бы заработал такой код:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
constexpr bool check(char const *str)
{
    return str != nullptr;
}

constexpr void print_f(char const *str)
{
    static_assert(check(str)); // Compile time.
    std::printf(str);          // Run time.
}
...
Рейтинг: 0 / 0
55 сообщений из 55, показаны все 3 страниц
Форумы / C++ [игнор отключен] [закрыт для гостей] / Вариативные шаблоны C++ и вариативные функции Си
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]