LINUX.ORG.RU

fmt 10.0.0

 , ,


1

4

После восьми месяцев разработки состоялся выпуск 10.0.0 библиотеки форматирования данных fmt — быстрой и безопасной альтернативы stdio и iostreams для C++.

#include <fmt/color.h>

int main() {
    fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
           "Elapsed time: {0:.2f} seconds", 1.23);
}

Список изменений:

  • для форматирования чисел с плавающей точкой с заданной точностью взамен алгоритма Grisu используется алгоритм Dragonbox, что дало существенное улучшение производительности;
  • для форматирования чисел с плавающей точкой в шестнадцатеричном представлении вместо ранее использованного snprintf применяется собственная реализация;
  • улучшена поддержка модулей C++20;
  • format_as теперь поддерживает любой пользовательский тип, а не только перечисления:
#include <fmt/format.h>

struct floaty_mc_floatface {
  double value;
};

auto format_as(floaty_mc_floatface f) { return f.value; }

int main() {
  fmt::print("{:8}\n", floaty_mc_floatface{0.42}); // prints "    0.42"
}
  • для совместимости с std::format удалены устаревшие неявные преобразования для перечислений и преобразования к примитивным типам;
  • улучшена поддержка std::chrono:
#include <fmt/chrono.h>

int main() {
  // prints "    2023"
  fmt::print("{:>8%Y}\n", std::chrono::system_clock::now());
}

#include <fmt/chrono.h>

int main() {
  // prints 01.234567
  fmt::print("{:%S}\n", std::chrono::microseconds(1234567));
}
  • добавлена поддержка std::utc_time;
  • добавлена поддержка std::exception:
#include <fmt/std.h>
#include <vector>

int main() {
  // prints: "vector<bool>::_M_range_check: __n (which is 0) >= this->size() (which is 0)"
  try {
    std::vector<bool>().at(0);
  } catch(const std::exception& e) {
    fmt::print("{}", e);
  }
}
  • улучшена поддержка стандартных контейнеров:
#include <fmt/ranges.h>
#include <stack>
#include <vector>

int main() {
  auto s = std::stack<bool, std::vector<bool>>();
  for (auto b: {true, false, true}) s.push(b);
  fmt::print("{}\n", s); // prints [true, false, true]
}
  • добавлена поддержка std::optional;
  • в fmt::ptr добавлена поддержка unique_ptr с пользовательским деструктором;
  • улучшена обработка некорректного Юникода в путях файлов;
  • добавлена экспериментальная поддержка Юникодных разделителей цифр :
c++
auto loc = std::locale(
  std::locale(), new fmt::format_facet<std::locale>("’"));
auto s = fmt::format(loc, "{:L}", 1000);
  • для совместимости с basic_format_string добавлена fmt::basic_format_string::get();
  • для совместимости с C++23 добавлена функция println;
  • улучшения документации и тестов.

Множество других исправлений и улучшений.

>>> Подробности

★★★★★

Проверено: hobbit ()
Последнее исправление: hobbit (всего исправлений: 1)

быстрой и безопасной альтернативы stdio и iostreams для C++.

А те что небезопасны? Как могу быть стандартизированные пузатыми дядьками библиотеки небезопасны?
Это ровно так же как Chrome блокирует загрузку Skype из-за дырявости второго? Похоже на бред...

xwicked ★★☆
()
Ответ на: комментарий от trex6

Если используешь в своем коде Сишную либу напрямую. Можно поискать враппер, но если такового нет, то есть два варианта: писать враппер самому или вызывать Сишную либу из плюсов.

zx_gamer ★★★
()
Ответ на: комментарий от sadko4u

Хм, а что мешает использовать

format("arg1={0} arg2≠{2}, arg3={3}"_i18n, x, y, z)

, где _i18 выполняет всю нужную магию с выбором паттерна для нужного языка

?

AlexM ★★★★★
()
Последнее исправление: AlexM (всего исправлений: 1)
Ответ на: комментарий от mazdai

Ну, строго говоря, анализ строки - это хороший кандидат для constexpr.

Справляется же kotlin в то, чтобы преобразовать такую строку формата в plain old java

AlexM ★★★★★
()
Ответ на: комментарий от ox55ff

Конечно это было бы круто, но в общем случае это создавало вопросы и накладывало дополнительные требования, вносило разнобой, так например можно развить идею интерполяции и вычисления строк на пользовательские типы(ибо иначе они бы всё так же форматировались как сейчас, либо появлялось требование к тому, что все такие типы должны иметь перегруженный строковый оператор или спецфункцию, но это своб очередь плохо обобщалось и требовало введения системы глобального наследования и обеспечения согласования между классами и примитивными типами… Короче - плюсы это язык среднего уровня с определённым наследием и просто так такие вещи не впихать, да и если пихать, то способы бывают разные и сложно найти оптимальный по компромисам. С другой стороны текущий формат даже лучше, ибо если правильно понимаю, то внутри скобок можно развить идею и указывать номера параметров, в виду чего такие строки будут, при повторяемости аргументов, короче.

AKonia ★★★
()
Ответ на: комментарий от AlexM

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

AKonia ★★★
()
Последнее исправление: AKonia (всего исправлений: 1)

и безопасной

дальше можно и не читать

uwuwuu
()

А так выглядит форматирование в strf:

#include <strf/to_string.hpp>
#include <strf/locale.hpp> // strf::locale_numpunct

void numeric_punctuation()
{
    // German punctuation
    auto s = strf::to_string.with(strf::numpunct_de_DE) (!strf::fixed(1000000.5));
    assert(s == "1.000.000,5");

    // You have to use format function `strf::punct` or `.punct()`
    // or operator! to apply punctuation
    s = strf::to_string.with(strf::numpunct_de_DE)
        ( !strf::fixed(1000000.5), "  ", strf::fixed(1000000.5)
        , "  ", strf::punct(10000), "  ", 100000 );
    assert(s == "1.000.000,5  1000000.5  10.000  100000");

    // Extracting punctuation from locale
    if (setlocale(LC_NUMERIC, "as_IN")) {
        auto loc_punct = strf::locale_numpunct(); // provided by header <strf/locale.hpp>
        auto s_loc = strf::to_string.with(loc_punct) (*!strf::fixed(1e+16));
        assert(s_loc == "10,00,00,00,00,00,00,000.");
    }

    // Manually specifing a punctuation
    constexpr auto my_fancy_punct = strf::numpunct<10>(3)
        .thousands_sep(U'•')
        .decimal_point(U'⎖'); // U+2396
    auto s2 = strf::to_u8string.with(my_fancy_punct) (!strf::fixed(1000000.5));
    assert(s2 == u8"1•000•000⎖5");

    // With variable grouping
    constexpr auto my_fancy_punct_2 = strf::numpunct<10>(3, 2, 1)
        .thousands_sep(U'•')
        .decimal_point(U'⎖');
    auto s3 = strf::to_u8string.with(my_fancy_punct_2)
        (strf::punct(10000000.125));
    assert(s3 == u8"1•0•0•00•000⎖125");

    // Non-decimal bases
    constexpr auto my_hex_punct = strf::numpunct<16>(4).thousands_sep('\'');
    auto s4 = strf::to_string.with(my_hex_punct)(!strf::hex(0xFFFFFFF));
    assert(s4 == "fff'ffff");
}

void ranges()
{
    int array[] = {10, 20, 30, 40};

    // Basic
    auto str = strf::to_string( "--[", strf::range(array), "]--");
    assert(str == "--[10203040]--");


    // With separator
    str = strf::to_string( "--[", strf::separated_range(array, " / "), "]--");
    assert(str == "--[10 / 20 / 30 / 40]--");


    // With separator and formatting
    str = strf::to_string( "--["
                         , *strf::hex(strf::separated_range(array, " / ")).p(4)
                         , "]--");
    assert(str == "--[0x000a / 0x0014 / 0x001e / 0x0028]--");


    // Transforming the elements
    auto func = [](int x){ return strf::join('<', strf::pad0(x, 4), '>'); };

    str = strf::to_string(strf::range(array, func));
    assert(str == "<0010><0020><0030><0040>");

    str = strf::to_string(strf::separated_range(array, " ", func));
    assert(str == "<0010> <0020> <0030> <0040>");
}
dataman ★★★★★
() автор топика
Последнее исправление: dataman (всего исправлений: 1)
Ответ на: комментарий от AKonia

Собственно, единственное, что требуется от ядра языка - научиться формировать и передавать в constexpr-анализатор строкового литерала «контекст», что-то типа лямбдовского &. Всё остальное можно делать на уровне библиотеки.

Вон, умельцы на стэковерфлоу уже зовут snprinf на string_view в constexpr-ах :)

Таким способом опасения граждан с c++ на микроконтроллерах будут развеяны.

AlexM ★★★★★
()
Ответ на: комментарий от Logopeft

А весь парсинг строк для интерполяции можно произвести в момент компиляции.

Вот, +1. И это называется «метапрограммирование».

В принципе, и на нынешнем C++ можно запилить что-нить constexpr-овое чтобы сабжевая либа парсила формат-строку в buildtime. И может даже конструировала какой-нибудь template<class… Args>, эффективно конкатенирующий куски строки с аргументами. Но делает ли это сабжевая либа – лень смотреть. Если не делает, то – таки-говно.

Хотя сам я конечно предпочту полноценный source rewriting с помощью clang LibTooling, а не пердолиться с constexpr-убожествами.

dimgel ★★★★★
()

На какие только извращения не пойдут геронтофилы, чтобы Rust не учить

FishHook
()
Ответ на: комментарий от ox55ff

Для такого на плюсах придётся парсить строку и хз что делать с переменной, а это - долго. Ну или сделать надо какую-то макросню/метакостыли, чтобы из

auto errorMsg = "fuck";
print("error {$errorMsg}");
//получалось
print("error %s", errorMsg);

crutch_master ★★★★★
()
Последнее исправление: crutch_master (всего исправлений: 1)
Ответ на: комментарий от pathfinder

Завтра придумают ещё что-то, что будет лучше предыдущих решений. Будем каждый раз орать - «Аааааа, говно!»?

Да. Так победим.

crutch_master ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.