Здравствуйте, so5team, Вы писали:
R>>Как частный случай — форматтер, определенный для базового класса автоматом будет работать и для всех производных, для которых не предоставлена своя собственая версия форматтера;
S>Это вроде как и сейчас в fmtlib возможно (полагаю, что и в std::format). Пример прямо из документации к fmtlib:
Это понятно, что выкрутиться можно. Но если в будущем появляется еще и тип C, также производный от A, и вы попытаетесь написать для него форматтер, похожий на тот, который уже написали для A, вы тут же почувствуете неудобство — этого нельзя будет сделать, не модифицировав специализацию форматтера для A, потому что обе специализации окажутся равноправны и создадут коллизию. Этого не произошло бы с функциями.
P.S. И виртуальность вы напрасно сюда внесли, имхо. Я изначально вел речь про обычное невиртуальное использование форматирования и виртуальность здесь вносит только лишний повод для побочных дискуссий.
Здравствуйте, so5team, Вы писали:
R>>Ну, я изложил свой взгляд на предмет и надеялся, что словесного описания будет достаточно. S>Так дьявол-то в деталях. А без деталей получается как в анекдоте "мыши, станьте ежиками!" R>>На примеры сейчас времени не хватает, сорри. S>Вряд ли в этом вопросе кто-то куда-то торопится.
Да, вспомнил еще один аргумент в пользу функций — стандартная библиотека без особого труда могла бы разрешить пользователям писать кастомные форматтеры для своих классов как в форме свободных (дружественных) функций, так и в форме функций-членов.
Здравствуйте, rg45, Вы писали:
R>Но если в будущем появляется еще и тип C, также производный от A, и вы попытаетесь написать для него форматтер, похожий на тот, который уже написали для A, вы тут же почувствуете неудобство — этого нельзя будет сделать, не модифицировав специализацию форматтера для A, потому что обе специализации окажутся равноправны и создадут коллизию. Этого не произошло бы с функциями.
Это вообще зыбкая почва для споров. Если мы оперируем традиционным полиморфизмом, то надобность в отдельном форматтере для C вызывает вопросы. Если мы используем наследование не для полиморфизма, а для переиспользования функционала из базового класса в производных, то вопросы вызывает форматтер для A, который может форматировать и экземпляры классов-наследников.
В общем, возвращаемся в исходную точку: без примеров кода и реальных юз-кейсов все это теоритизирование. Типа можно было бы и лучше, но зачем и какой ценой хз.
Не пытаясь защищать fmtlib и std::format (мне процедура написания собственных специализаций formatter-а кажется неудобной) все же есть ощущение, что в процессе эволюции fmtlib и прохождение через комитет наверняка высказывались самые разные идеи, а на практике выжила имеющаяся комбинация возможностей + сложность реализации + накладные расходы.
R>P.S. И виртуальность вы напрасно сюда внесли, имхо.
Пример из документации к fmtlib, я его просто скопипастил. Все претензии к Зверовичу.
Здравствуйте, so5team, Вы писали:
S>Это вообще зыбкая почва для споров. Если мы оперируем традиционным полиморфизмом, то надобность в отдельном форматтере для C вызывает вопросы.
Да, только этих вопросов даже не возникло бы в случае использования функций. Может, для начала стоит ответить на вопрос, почему вместо фунций необходимо использовать шаблон класса? Имхо, прикладная библиотека не должна диктовать разработчику вопросы дизайна. Тем более в области "зыбких почв"
Здравствуйте, rg45, Вы писали:
S>>Это вообще зыбкая почва для споров. Если мы оперируем традиционным полиморфизмом, то надобность в отдельном форматтере для C вызывает вопросы.
R>Да только этих вопросов даже не возникло бы в случае использования функций.
Простите мне мой французский, но тут явно неравные условия. Есть конкретная fmtlib, которую можно пощупать и, поскольку она есть и опыт ее использования накоплен, то можно запросто обосрать либо ее всю, либо отдельные ее части.
А вот некий гипотетический дизайн на функциях он же идеален, в нем нет просчетов. Как можно обосрать то, чего нет?
R>Может, для начала стоит ответить на вопрос, почему вместо фунций необходимо использовать шаблон класса?
Были бы функции, можно было бы ответить. Но их нет.
У fmt::formatter, например, может быть состояние. Т.е. в процессе работы fmt::formatter::parse можно что-то сохранить/вычислить, что затем будет использовано в fmt::formatter::format. Причем наличие/отсутствие состояния совершенно прозрачно для самого fmt::format/print.
Тогда как функции stateless, и если из parse нужно что-то передать в format, то здесь уже нужно озадачиваться тем, чтобы функции были парными. Условно:
И для меня не очевидно, что свободные функции в массовом применении в таком случае будут проще в использовании, чем специализации fmt::formatter.
R>Имхо, прикладная библиотека не должна диктовать разработчику вопросы дизайна.
Простите, но это уже демагогия. Любой существующий инструмент будет накладывать на пользователя какие-то ограничения. И с этим придется считать, возможно, и на уровне дизайна.
Здравствуйте, so5team, Вы писали:
S>Простите мне мой французский, но тут явно неравные условия. Есть конкретная fmtlib, которую можно пощупать и, поскольку она есть и опыт ее использования накоплен, то можно запросто обосрать либо ее всю, либо отдельные ее части. S>А вот некий гипотетический дизайн на функциях он же идеален, в нем нет просчетов. Как можно обосрать то, чего нет?
То есть, если я вижу какие-то конкретные минусы какой-то конкретной библиотеки, я не имею права об этом рассуждать до тех пор, пока не предоставлю рабочую альтернативу? Не жестковата ли постановка вопроса?
Здравствуйте, so5team, Вы писали:
S>Тогда как функции stateless, и если из parse нужно что-то передать в format, то здесь уже нужно озадачиваться тем, чтобы функции были парными. Условно: S>И для меня не очевидно, что свободные функции в массовом применении в таком случае будут проще в использовании, чем специализации fmt::formatter.
А для меня это полностью очевидно. В случае с функциями, для стандартной библиотеки вообще не составит труда поддержать передачу состояния, причем сделать это опционально — поддержать stateless/statеfull тем же самым путем, как осуществить пооддержку свободных функций, так и функций-членов. Да, это дополнительные телодвижения, но они делаются один раз внутри стандартной библиотеки. С точки зрения пользователя все получается удобно и комфортно. Да нужно будет обеспечивать консистентность пользовательских parse и формат, но для этого совсем не обязательно делать из всегда парными. И это гораздо удобнее, чем карячить специализации классов с конфликтами, разрывами неймспейсов и мозгов.
Здравствуйте, rg45, Вы писали: R>Так о том и речь, что их нет, а вместо функций — шаблон класса std::formatter. А если бы std::format для кастомизации использовал бы не шаблон класса со специализациями, а неквалифицированный вызов функции с каким-то предопределенным именем, это могло бы дать ряд преимуществ...
Наверное, я ошибаюсь, но все-таки спрошу. А нет ли опасности, что перегрузки этой функции — форматтера, объявленные после определения функции std::format, окажутся вне области её видимости и не будут вызываться? Или есть способ как-то просто обойти эту проблему?
Здравствуйте, Went, Вы писали:
W>Наверное, я ошибаюсь, но все-таки спрошу. А нет ли опасности, что перегрузки этой функции — форматтера, объявленные после определения функции std::format, окажутся вне области её видимости и не будут вызываться? Или есть способ как-то просто обойти эту проблему?
Только в том случае у тебя возникли проблемы со встроенными типами. С типами, определенными пользователем, таких проблем не будет, ADL исправно будет работать. Равно как и для типов, в которых пользовательские типы фигурируют в качестве шаблонных параметров (std::vector<MyClass> и т.п.). Ну если только не косячить с неймспейсами, разумеется.
Здравствуйте, Went, Вы писали:
W>Наверное, я ошибаюсь, но все-таки спрошу. А нет ли опасности, что перегрузки этой функции — форматтера, объявленные после определения функции std::format, окажутся вне области её видимости и не будут вызываться? Или есть способ как-то просто обойти эту проблему?
Сейчас также все не сахар. Форматер в виде специализации класса не вызывается и всё, пока ты правильно не подберёшь синтаксис перегрузки. С шаблонными классами это бывает не очень просто. Вот пример у меня в коде:
Здравствуйте, rg45, Вы писали:
R>То есть, если я вижу какие-то конкретные минусы какой-то конкретной библиотеки, я не имею права об этом рассуждать до тех пор, пока не предоставлю рабочую альтернативу? Не жестковата ли постановка вопроса?
Только такая постановка вопроса позволяет вести конструктивный разговор, сорри.
Просто сказать, что дизайн со специализацией formatter -- говно, а те, кто его стандартизировал, сделали это неподумавши, -- это лишь выпустить пар в свисток. Ибо нет никакого способа исправить ситуацию.
То, что можно сделать лучше, чем в fmt/std::format, с точки зрения теории, не подлежит сомнению. Но интересна не теория, а практика: как это будет выглядеть, сколько будет стоить.
Здравствуйте, so5team, Вы писали:
S>Только мне кажется, что "два" противоречит "раз"?
Зачем нужно две функции, в том числе в текущей реализации fmt, понятно. Функция форматирования работает на этапе компиляции, т.е. она constexpr и выдает ошибки там же.
На самом деде не проблема сlелать static_assert с выдачей ошибки в случае отсутствия одной из функций. Увидели тип, какой-то функции нет — тут же static_assert.
S>Разрый неймспейса -- да, серьезное неудобство. Регулярно на это чертыхаюсь.
Разрыв неймспейса, это как раз специализация форматтер-а в пространстве имен std. Со свободными функциями это не нужно. Описываете функцию в своём же пространстве имен, ADL её найдет сам.
Здравствуйте, Videoman, Вы писали:
S>>Только мне кажется, что "два" противоречит "раз"?
V>Зачем нужно две функции, в том числе в текущей реализации fmt, понятно. Функция форматирования работает на этапе компиляции, т.е. она constexpr и выдает ошибки там же. V>На самом деде не проблема сlелать static_assert с выдачей ошибки в случае отсутствия одной из функций. Увидели тип, какой-то функции нет — тут же static_assert.
Э... А зачем вы это все написали? Это типа доказательство удобства иметь две свободные функции, которые пользователю нужно держать согласованными?
Здравствуйте, so5team, Вы писали:
R>>С точки зрения пользователя все получается удобно и комфортно.
S>Раз.
R>>Да нужно будет обеспечивать консистентность пользовательских parse и формат,
S>Два.
S>Только мне кажется, что "два" противоречит "раз"?
А по-моему, совсем нет, если требуется состояние, то его нужно вернуть из parse, и получить в format параметром, если не нужно, значит ничего этого не нужно. Что может быть проще и естественнее?
S>Разрый неймспейса -- да, серьезное неудобство. Регулярно на это чертыхаюсь.
Так о чем и речь. Одно это перевешивает все остальное.
S>Примеры конфликтов можно?
И важно то, что проблема не сводится к одному лишь наследованию и не решается отказом от него. Я уверен многим из присутствующих приходилось разруливать самые разнообразные SFINAE-коллизии в виду того, что для одного типа подходят более одной специализации. Для функций этот вопрос во многих случаях реашется проще, благодаря механизму выбора кандидата, обеспечивающего лучшее соответствие типов формальных и фактических параметров. А при использовании SFINAE при специализации классов, фильтры нужно "полировать" при расширении, чтоб исключить накладки. Да я уверен, что вы и сами знаете об этой проблеме.
Здравствуйте, rg45, Вы писали:
S>>Только мне кажется, что "два" противоречит "раз"?
R>А по-моему, совсем нет, если требуется состояние, то его нужно вернуть из parse, и получить в format параметром, если не нужно, значит ничего этого не нужно. Что может быть проще и естественнее?
Как раз подход с классом, у которого есть методы. Хотим, делаем состояние. Хотим не делаем. Хотим, делаем в зависимости от defines и фазы Луны. При этом прототипы самих методов не меняются.
При этом, в моей практике, при специализации fmt::formatter можно наследоваться от уже существующего fmt::formatter и переопределять только один из методов.
А в случае функций нужно писать две функции. И при этом их еще и модифицировать, если вдруг выяснилось, что состояние (не) нужно.
S>>Разрый неймспейса -- да, серьезное неудобство. Регулярно на это чертыхаюсь.
R>Так о чем и речь. Одно это перевешивает все остальное.
Э... Ну, ОК.
S>>Примеры конфликтов можно?
R>Об одном случае я уже упоминал выше
, вот эти две специализации одновременно могут подходить для одних и тех же типов:
Т.е. пример а) искусственный и b) спорный.
Videoman привел свой пример из той же оперы. Но есть у меня ощущение, что там что-то человек сам перемудрил. Впрочем, могу ошибаться, чтобы убедиться в этом нужно глубоко в его код погрузиться.
Здравствуйте, so5team, Вы писали:
S>Простите, общие слова не интересны. Конкретные примеры -- да, абстрактные рассуждения о том, как у кого-то где-то что-то -- нет.
А чего вы решаете за всех что интересно, что нет? Это ж форум, а не личная переписка.