Здравствуйте, Videoman, Вы писали:
S>>Конструктивная критика должна, как минимум, идти в https://stdcpp.ru/ V>Искренне считаю, что то, что вы предлагаете, постоянно сидеть на форуме С++ и мониторить все предложения, анализировать их и проверять — не возможно на практике.
Где я такое предлагал?
V>Ну посетовал немного, это запрещено на RSDN?
Вы назвали свое сетование конструктивной критикой.
На практике же лично я не понял ни что вас не устраивало (т.е. в чем именно у вас была проблема), ни как вы смогли решить свою проблему, ни как эта проблема решалась в вашей библиотеке (т.е. не увидел никаких реальных альтернатив подходу std::format/fmt::format).
Так что конструктива в ваших сообщениях в этой теме лично я не вижу. Если что-то таковое есть, то могли бы вы показать пальцем?
S>>std::format взялся не на пустом месте. Много лет в OpenSource была fmtlib, автор которой потратил туеву хучу времени и сил на то, чтобы выложить это в OpenSource, снабдить документацией, среагировать на огромное количество issues, принять кучу pull-request-ов, выслушать в свой адрес всякое-разное. А потом еще и принять участие в формировании предложения в стандарт, и пройти вместе с другими соавторами через процесс включения в стандарт. V>Знаем знаем как всё там не на пустом месте. Ребята из Microsoft запилили библиотеку для своих личных нужд и пропихнули её в стандарт, т.к. могли.
Мы все еще говорим про fmtlib и std::format?
V>Но зачем так яростно его защищать, даже если там есть очевидные недостатки
Можно хотя бы одну цитату из моих слов, из которой бы следовало, что я защищаю std::format (или какое-либо из технических решений, примененных в std::format)?
Здравствуйте, Videoman, Вы писали:
V>Для остальных классов можно хотя бы дать возможность не подключать заголовки стандартных форматеров, а использовать свои.
Простите, а это как?
Вот, допустим, есть специализация std::formatter<std::time_t>. Вы не подключаете заголовок, в котором эта специализация определена, а делаете собственную специализацию std::formatter<std::time_t> в своем .cpp-файле, но в пространстве имен std.
Здравствуйте, so5team, Вы писали:
S>Вот, допустим, есть специализация std::formatter<std::time_t>. Вы не подключаете заголовок, в котором эта специализация определена, а делаете собственную специализацию std::formatter<std::time_t> в своем .cpp-файле, но в пространстве имен std.
S>Я правильно вас понял?
Нет. Если ввести какой-нибудь формальный шаблонный параметр, который будет контролировать область поиска, то в случае реализация форматеров на функциях, ADL не увидит их и начнет искать в заданном вами найспейсе.
Здравствуйте, Videoman, Вы писали:
V>Нет. Если ввести какой-нибудь формальный шаблонный параметр, который будет контролировать область поиска, то в случае реализация форматеров на функциях, ADL не увидит их и начнет искать в заданном вами найспейсе.
Очень не хватает примеров. Мне до сих пор не понятно, что значит "реализация форматеров на функциях".
Здравствуйте, Videoman, Вы писали:
S>>Очень не хватает примеров. Мне до сих пор не понятно, что значит "реализация форматеров на функциях".
V>Посмотрите доклад Полухина
И библиотека у вас секретная, даже фрагмента оттуда увидеть нельзя.
И вы настолько заняты, что минимальный пример продемонстрировать не можете.
Ну OK, пересмотрю доклад и буду гадать, а как Videoman это все сам понял.
Здравствуйте, so5team, Вы писали:
S>И библиотека у вас секретная, даже фрагмента оттуда увидеть нельзя. S>И вы настолько заняты, что минимальный пример продемонстрировать не можете.
S>Ну OK, пересмотрю доклад и буду гадать, а как Videoman это все сам понял.
S>Конструктивно, однако.
Ну чо вы такой душный, а?! Ну никакого позитива. Отдельной библиотеки нет, есть огромная библиотека-платформа для всего. Работает под Linux и Windows. Отодрать от туда именно форматирование, не могу, т.к. писалось оно еще с 2008-года, т.к. тогда нужна была поддержка не только std строк, но и MFC/ATL — строк и т.д. Код там не для показа, честно. Я по мере сил выкидываю от туда всё, что появляется в новых стандартах С++, изолирую законченные части и выкладываю в опен сорс, например: slimcpplib — вот sutfcpplib — и вот
Форматирование в виде отдельной библиотеки не появится, т.к. стандартное, по счастливой случайности совпадет с моим собственным и меня почти полностью устраивать. Нужно только переписать форматеры, с чем я сейчас и борюсь. Но похожий подход у меня используется не только там, но и при сериализации, например. Точно так же, как это делает Полухин. Очень удобно и гибко получается. В двух словах:
ADL ищет pack сначала в текущем пространстве имен, потом с пространстве имен custom_class, а если не найдет то в пространстве имен archive_t — моем стандартном. Эти три пространсва имен могут быть разными. Отсюда и гибкость.
int a = ...;
std::time_t b = ...;
print(std::cout, "Value {} at {}", a, b);
если я хочу time_t форматировать не так, как это делает некий задействованный мной print (пусть он будет стандартным)?
Здесь же есть только форматная строка (которая парсится не мной) и ссылки на значения, типы которых могут вообще не иметь отношения к моей библиотеке. Что-то мне не видно где здесь вообще форматеры для print-а указать можно.
Здравствуйте, so5team, Вы писали:
S>Как это перенести на вот такой способ записи: S>
S>int a = ...;
S>std::time_t b = ...;
S>print(std::cout, "Value {} at {}", a, b);
S>
S>если я хочу time_t форматировать не так, как это делает некий задействованный мной print (пусть он будет стандартным)?
S>Здесь же есть только форматная строка (которая парсится не мной) и ссылки на значения, типы которых могут вообще не иметь отношения к моей библиотеке. Что-то мне не видно где здесь вообще форматеры для print-а указать можно.
Могу рассуждать только гипотетически. Допустим мы можем добавить более низкоуровневую функцию print_as:
namespace my_namespace
{
struct my_trait{};
}
namespace std
{
std::print_as(std::cout, std::format_as<my_namespace::my_trait>(), "Value {} at {}", a, b);
}
Где-то в потрохах мы делегируем std::format_as<my_trait>() до каждого форматера, который является функцией и получает его в качестве аргумента. Тогда если отключены стандартные форматеры, ADL будет искать во всех остальных местах, в том числе в my_namespace. А std::print для удобства пусть вызывает что-то типа: std::print_as(std::cout, std::format_as<std::format_traits>, "Value {} at {}", a, b);
Здравствуйте, so5team, Вы писали: S>Мы все еще говорим про fmtlib и std::format? S>Можно хотя бы одну цитату из моих слов, из которой бы следовало, что я защищаю std::format (или какое-либо из технических решений, примененных в std::format)?
Выглядит что защищаете, потому-что не хотите признать что некоторые вещи нельзя сделать, а некоторые непонятно как делать в теперь уже отлитой в камне парадигме. И
прямо докладывает, что особо сильно не думали, увидели хорошую, я не спорю, библиотеку, она им понравилась и они её прямо почти один в один перенесли в стандарт. Предложение было от Microsoft и они же её первыми заимплементили для VC.
Здравствуйте, Videoman, Вы писали:
V>Где-то в потрохах мы делегируем std::format_as<my_trait>() до каждого форматера, который является функцией и получает его в качестве аргумента. Тогда если отключены стандартные форматеры, ADL будет искать во всех остальных местах, в том числе в my_namespace. А std::print для удобства пусть вызывает что-то типа: std::print_as(std::cout, std::format_as<std::format_traits>, "Value {} at {}", a, b);
И что мешает внести такое предложение? Хотя бы вот сюда: https://stdcpp.ru/proposals/
Если людей это заинтересует, то они доведут это до формального предложения комитету.
Меня в таком подходе, например, сильно смущает такой момент: допустим, мы линкуем два объектника. В одном используются вызовы std::print (которые трансформируются в std::print_as(..., std::format_as<std::format_traits>, ...)), а в другом вызовы std::print_as(.., std::format_as<my_traits>, ...). Поскольку std::print_as шаблонная, то компилятор сгенерирует и std::print_as для std::format_traits, и для my_traits. Т.е. мы получим две копии практически одного и того же кода. На ровном месте, по сути.
Здравствуйте, Videoman, Вы писали:
S>>Мы все еще говорим про fmtlib и std::format?
S>>Можно хотя бы одну цитату из моих слов, из которой бы следовало, что я защищаю std::format (или какое-либо из технических решений, примененных в std::format)?
V>Выглядит что защищаете
Т.е. цитат нет, но выглядит?
V>потому-что не хотите признать что некоторые вещи нельзя сделать, а некоторые непонятно как делать в теперь уже отлитой в камне парадигме.
Откуда следует что я что-то признаю или не признаю? С цитатами, плз.
V>прямо докладывает, что особо сильно не думали, увидели хорошую, я не спорю, библиотеку, она им понравилась и они её прямо почти один в один перенесли в стандарт. Предложение было от Microsoft и они же её первыми заимплементили для VC.
Т.е. роль Microsoft-а лишь в том, что они взяли на себя труд провести fmtlib через процедуру стандартизации. Но про это обязательно нужно высказаться вот в таком духе:
> Ребята из Microsoft запилили библиотеку для своих личных нужд и пропихнули её в стандарт, т.к. могли.
Добавить сюда еще и:
> А я говорил, что люди принимающие стандарты, по ходу вообще не думают, последнее время.
Здравствуйте, so5team, Вы писали:
S>И что мешает внести такое предложение? Хотя бы вот сюда: https://stdcpp.ru/proposals/ S>Если людей это заинтересует, то они доведут это до формального предложения комитету.
Теоретически ничего не мешает. Практически, я только неделю как погрузился в std::format и пока изучаю исходники и занят своей работой. Что бы оформить предложение, нужно время. Время появиться, обязательно сделаю, тем более если вы считаете, что это имеет смысл и что это вообще кто-то читает.
Подход с функциями хорош еще и тем, что работают стандартные преобразования. Т.е. можно делать форматеры, которые не обязательно жестко совпадают с типом, а являются, например, базовым классом для подмножества наследников или преобразуются к типу. Короче весь богатый арсенал который доступен при перегрузках функций. При желании, с помощью SFINAE можно и жестко зашить тип. При специализациях классов, мы всё это теряем и приходится сразу обращаться к SFINAE. (кстати, может быть разработчики хотели обратного, кто знает)
S>Меня в таком подходе, например, сильно смущает такой момент: допустим, мы линкуем два объектника. В одном используются вызовы std::print (которые трансформируются в std::print_as(..., std::format_as<std::format_traits>, ...)), а в другом вызовы std::print_as(.., std::format_as<my_traits>, ...). Поскольку std::print_as шаблонная, то компилятор сгенерирует и std::print_as для std::format_traits, и для my_traits. Т.е. мы получим две копии практически одного и того же кода. На ровном месте, по сути.
Ну это стандартные издержки шаблонов, можно с легкостью нагенерировать кучу машинного кода. Зато быстро ! Нужно будет подумать как это оптимизировать. Например, поскольку аргумент формальный, его можно передавать как параметр шаблона и только в конце инстанцировать при передаче в сам formatter. Такое компилятору будет проще оптимизировать?
Здравствуйте, Sm0ke, Вы писали:
S>Заведите второй параметр шаблона с именем traits_t.
Боюсь, это не тот случай, когда это осуществимо. Стартовое сообщение хоть и сформулировано для общего случая, но по дальнейшему обсуждению ясно, что речь идет об std::formatter, primary template которого объявлен в стандартной библиотеке.
Здравствуйте, so5team, Вы писали:
S>Я вот хоть убей не вижу где же здесь функции.
Так о том и речь, что их нет, а вместо функций — шаблон класса std::formatter. А если бы std::format для кастомизации использовал бы не шаблон класса со специализациями, а неквалифицированный вызов функции с каким-то предопределенным именем, это могло бы дать ряд преимуществ:
Пользователь мог бы определять кастомные форматтеры в том же пространстве имен, что и пользовательские типы. Это удобнее писать и удобнее читать, потому что не нужно рвать пространства имен или выносить определения в какие-то отдельные места;
При определении кастомных форматтеров пользователь мог бы использовать дополнительные параметры по умолчанию — как в списке шаблонных параметров, так и в списке формальных параметров функции, что дает возможность использования SFINAE и вообще дает большую гибкость в тех случаях, когда кастомизацию нужно сделать не для одного конкретного типа, а для какого-нибудь сеймейства типов, объединенных каким-то признаком;
Как частный случай — форматтер, определенный для базового класса автоматом будет работать и для всех производных, для которых не предоставлена своя собственая версия форматтера;
Используя квалифицированные вызовы, пользователь мог бы внутри кастомго форматтера повторно использовать форматтеры из других пространств имен, что дает возможность декорирования форматтеров.
Если что-то и упустил, по-моему, этого списка уже достаточно.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, so5team, Вы писали:
S>>Я вот хоть убей не вижу где же здесь функции.
R>Так о том и речь, что их нет, а вместо функций — шаблон класса std::formatter. А если бы std::format для кастомизации использовал бы не шаблон класса со специализациями, а неквалифицированный вызов функции с каким-то предопределенным именем, это могло бы дать ряд преимуществ:
Вынужден повторить то, что уже писал: очень не хватает примеров. У вас одна картина в голове, у Videoman вторая, у меня третья. Все это можно было бы привести к общему знаменателю несколькими примерами, но вот примеров-то никто и не приводит
Здравствуйте, so5team, Вы писали:
S>Вынужден повторить то, что уже писал: очень не хватает примеров. У вас одна картина в голове, у Videoman вторая, у меня третья. Все это можно было бы привести к общему знаменателю несколькими примерами, но вот примеров-то никто и не приводит
Ну, я изложил свой взгляд на предмет и надеялся, что словесного описания будет достаточно. На примеры сейчас времени не хватает, сорри.
Здравствуйте, rg45, Вы писали:
R>Как частный случай — форматтер, определенный для базового класса автоматом будет работать и для всех производных, для которых не предоставлена своя собственая версия форматтера;
Это вроде как и сейчас в fmtlib возможно (полагаю, что и в std::format). Пример прямо из документации к fmtlib:
#include <type_traits>
#include <fmt/format.h>
struct A {
virtual ~A() {}
virtual std::string name() const { return"A"; }
};
struct B : A {
virtual std::string name() const { return"B"; }
};
template <typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> {
template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) const {
return fmt::formatter<std::string>::format(a.name(), ctx);
}
};
int main() {
B b;
A& a = b;
fmt::print("{}", a); // prints "B"
}