Как стать автором
Обновить

Комментарии 32

Да, какие-то проверки есть, и даже форматную строку оно проверяет! Но такой простой проверки, как количество аргументов нет.

auto foo(auto&&... args) {
  return std::format("{1} {6}", args...);
}

Если вам хочется дополнительно проверить, что количество аргументов совпадает с количеством {}, то сделайте

template<typename... Types>
struct my_fmt_string : std::format_string<Types...> {
  consteval my_fmt_string() { // additional checks  }
};
std::string myformat(my_fmt_string) {
...
}



Спасибо, дельный совет! К сожалению, наличие std::format_stringя увидел только после обновления студии до самой свежей. А до этого у меня УХ как подгорало...

Таким трюком можно полностью избавиться от макросов (почти, если не считать совместимость с несвежими VS и clang-ом)

. Причём gcc (на скрине выше) генерирует самый приличный код из «большой троицы» (не считая сотен функций в бинарнике и ещё десятки импортированных).

кажется это типичная ошибка при работе с гцц - писать бенчмарк в мейне. Гцц по одной им известной причине по другому оптимизирует мейн нежели остальные функции

Нет, не хорошо, создаётся лишняя строка которая только потом выводится в cout, вместо этого лучше использовать format_to + ostreambuf_iterator

https://godbolt.org/z/xen6cT4h1

Мы ещё и форматную строку будем каждый раз в рантайме парсить.

А вот это проблема, но решаемая, нужно чтобы реализации использовали факт проверенности строки на компиляции(кажется пока это закрывается спецификацией которая чётко говорит, что format должен вызывать vformat)

Спасибо, про main познавательно. Про последний пункт — да, надеемся.

Нет, не хорошо

У вас ссылка непосещённая ;)

кажется пока это закрывается спецификацией которая чётко говорит, что format должен вызывать vformat

Не "должен вызывать", а эквивалентен (цитата: "Effects: Equivalent to:
return vformat(fmt, make_format_args(args...));"
). Поскольку vformat — тоже стандартная функция с известным поведением, менять реализацию format можно как угодно до тех пор пока результат не меняется.

Плюс мы получаем проверки формата во время компиляции, и даже можем дополнительно написать свои.

Если это уже есть и в printf, то зачем городить огород? Чтобы строка формата как в Python, автоматический вывод типа или еще что-то?

  1. этого нет в printf

  2. printf это сишный вариадик который вносит рантайм оверхед(помимо прочих проблем)

  3. не кастомизируем под свои типы(даже с расширениями), не переносим(нужно писать макросы в формат строке) и тд

этого нет в printf

Нет чего, проверки форматов во время компиляции? А как же __attribute__ ((format(printf, 1, 2))); Если этого нет в компиляторе MS, то этого нет именно там, но не у printf, для которой проверка формата есть.

printf это сишный вариадик который вносит рантайм оверхед(помимо прочих проблем)

Который проверен десятилетиями и изучены всего подводные камни? И чем это плохо? А насчет оверхеда, я специально для embedded использую только printf, потому что с оверхедом при парсинге строки формата справляется самый простой микроконтроллер, а вот лишние 200-300 кБайт ПЗУ только для того, чтобы подключить форматированные ввод/вывод потоков это очень серьезный оверхед, который съедает в первую очередь деньги, т.к. заставляет использовать более старшие (и более дорогие) версии кристаллов.

не кастомизируем под свои типы(даже с расширениями), не переносим(нужно писать макросы в формат строке) и тд

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

Я не стараюсь вас убедить использовать старый и проверенный годами printf. Хотите использовать новомодный std::format_string, пожалуйста, это ваше право. Просто для себя я не увидел в нем ни одного ключевого преимущества, который бы заставил на него перейти.

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

А чтобы не писать макросы, можно определить собственную функцию с форматироваными аргументами как у printf, так же можно сделать и в предыдущем случае.

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

конвертируешь свой тип данных в строку и выводишь как строку

неэффективно

ввод/вывод потоков это очень серьезный оверхед

потоки это другое, не связано с std::format. Они действительно добавляют в бинарь своего, да и в целом неэффективны. Но формат к ним никак не относится, std::print, который внутри использует std::format, можно использовать вместе с FILE* и каким нибудь stdout

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

Сделай static_cast<int> для аргумента и использую единую форматную строку для всех платформ. Я реально не понимаю, зачем тут макросы?

неэффективно

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

потоки это другое, не связано с std::format....

Но это не ответ на вопрос, зачем прикручивать лишнюю библиотеку и менять работающее годами решение на другое, если у нового него нет серьезных преимуществ?

Сделай static_cast<int>

дополнительное поле для ошибок, оверхеда, кривого кода. Интересно как выводить unsigned если кастовать всё к инту... А как выводить __int128? Ну и конечно любые свои типы, какая-нибудь точка своя в геометрии. Ну или ренж таких точек. В общем придётся на каждый случай писать кучу бойлерплейта с возможностью ошибиться оочень много раз

хотя свой формат в строку все равно придется конвертировать перед выводом.

не придётся, вместо того чтобы создавать строку и выводить просто выводишь поля друг за другом, например

если у нового него нет серьезных преимуществ?

они есть, если вам не очевидны минусы printf, то тут ничем не могу помочь

Я ответил ответил на ваше возражение

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

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

просто выводишь поля друг за другом, например

С такой же легкостью можно и в printf, поэтому опять возражение мимо.

они есть, если вам не очевидны минусы printf, то тут ничем не могу помочь

Это не вопрос веры или суслика (ты его не видишь, а он есть), а реальных фактов, если вы конечно в теме, но вы так и не смогли привести ни одного (в отличии от @atd).

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

придётся выдумывать каждый раз как это сделать

С такой же легкостью можно и в printf, поэтому опять возражение мимо.

нет,
1. придётся много раз вызывать printf

2. каждый раз придётся повторять код, нельзя вынести это в форматтер

если вы конечно в теме, но вы так и не смогли привести ни одного 

я привёл, вы просто не считаете это проблемами по непонятной причине

вот, перепишите на printf

void log(auto&& x) {
  std::print("{}", x);
}

Не нужно выдумывать, достаточно решать текущие задачи не замахиваясь на глобальные.

Сделать свой форматтер, если не хочется повторяться.

    va_list args;
    va_start(args, format);
    vsnprintf(buffer, LOG_MAX_BUFFER_SIZE, format, args);
    va_end(args);

Я вижу более простые решения для приведенных вами примеров, поэтому и не считаю их серьезными аргументами для замена проверенного и работающего решения на что-то новое.

вот, перепишите на printf

Не буду. Но не потому, что не хочу или не могу, а не вижу смысла в такой конструкции логирования. Я считаю, что разработчик должен понимать, что он выводит в лог и в каком виде (чтобы это не было 1Гб двоичных данных). И с этой точки зрения printf с проверкой типов аргументов для форматной строки является почти идеальным решением.

Не буду. Но не потому, что не хочу или не могу,

могли бы сразу сказать, что вам единственное что нужно это хоть как нибудь вывести через 3 костыля int(и не дай бог это int32_t), а остальное не нужно, не имеет смысла и так далее

Ни в коем случае.
Наоборот, я очень активно пользуюсь отладочным выводом, причем реализовано это кроссплатформенно и единообразно сразу для всех платформ (Linux, Windows, FreeRTOS на x86, x64, Cortex, GigaDevice) без изменений исходников и возможностью переназвачения вывода на экран, в файл, UART или даже во Flash память микроконтроллера для посмертного CoreDump

Сделай static_cast<int> для аргумента и использую единую форматную строку для всех платформ. Я реально не понимаю, зачем тут макросы?

А если не влезет?


Да и даже если вы можете гарантировать что влезет — в чём вообще смысл использовать какой-нибудь int_least32_t если вы можете гарантировать что оно влезает в int? А если никогда int_least32_t не использовать — зачем его вообще добавляли?


Наличие в языке int_least32_t и отсутствие поддержки его вывода — это абсурд, который требуется исправить.

А насчет оверхеда, я специально для embedded использую только printf

Ого, как времена изменились! Раньше сам printf в ембеде считался преступлением, и использовались отдельные форматтеры (иногда даже самописные), чтобы сэкономить 20кб принтфа (ещё и стэка неизвестно сколько он сожрёт). А теперь это «без оверхеда» уже считается? Oo

Но с тем, что в ембеде std::format пока не нужен — соглашусь. Хотя и принтф тоже там не нужен )

Я не знаю, когда и у кого он считался преступлением, так как это один из самых простых и всегда работающих выводов при отладке, который работает даже без программатора. Тем более, можно отключать не нужные форматы вывода (например числа с плавающей запятой).

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

Я не знаю, когда и у кого он считался преступлением

Во времена pic16f84a.

Ваша основная (и справедливая) претензия — к iostream, но его использовать совсем не обязательно, вы можете сделать (как показано в статье):

char buff[BUFSIZE];
std::format_to_n(buff, BUFSIZE...)

Или вообще взять `format_to` и писать сразу в UARTn->DR.

можно отключать не нужные форматы вывода (например числа с плавающей запятой).

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

Спасибо, это действительно реальный аргумент за использование std::format

Я вот лично не очень понимаю все эти лишние проверки


    std::cout << std::format( load_string(ID_HELLO,lang), arg1, arg2 );

И что в таком случае надо проверять?


И как вообще выравнивать строки влево в право, если ширина в юникоде/utf8 очень не очевидная величина.


А что насчет шаблонов текста то же ничего ни фильтров, ни именованных полей?


В общем очень прорывная технология, аж уши закладывает. /s

И что в таком случае надо проверять?

что строка известна на этапе компиляции для начала

А что насчет шаблонов текста

а про это целый параграф нытья в статье есть

В общем очень прорывная технология

это С++, детка, так и живём...

И что в таком случае надо проверять?

Очевидно же, что если вы загружаете строку из файла локализации — во время компиляции ничего вы не проверите. И вообще этот код не скомпилируется, вам надо std::vformat использовать.


В общем очень прорывная технология, аж уши закладывает.

Для плюсов — ещё как прорывная. 20 лет копилось, и сейчас вот прорвалось.

Да, какие-то проверки есть, и даже форматную строку оно проверяет! Но такой простой проверки, как количество аргументов нет.

Это сделано сознательно, так как можно написать std::format("{1} {1} {0} {0}", 9, 6)

Так можно, там ещё много что можно. Но вот этот случай тоже вполне проверяемый.

А constexpr kv-контейнеры уже завезли под валидацию такого?

нет, но они и не требуются

Честно говоря, форматирование как в питоне может и пожить в питоне, хотелось бы наконец уже работу с сетью из коробки, а не выбирать из окружающих библиотек

Во многих статьях упоминается, что это форматирование «почти как в питоне». Только данное «почти» слишком «почти».

Скорее всего имеются ввиду не f-строки, а .format(), который работает также

Попался ещё способ сделать "дзиньк" японской бензопиле
std::string hello = "hello";
std::wstring message = std::format(L"{}", hello);

Здесь вываливается сразу куча ошибок и все эти ошибки ведут внутрь кишков самого format, вместо того чтобы сообщить о том что format не умеет смешивать string c wstring и указать на место где я это попытался смешать.
Во всяком случае в MSVC 2022 так, в других компиляторах не проверял.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории