Ответ 1
Кажется, что ответ - нет, нет библиотеки С++, которая делает это, а программисты на С++, по-видимому, даже не видят необходимости в ней, основываясь на полученных комментариях. Мне придется снова написать свой собственный.
Мне интересно, есть ли библиотека типа Boost Format, но которая поддерживает именованные параметры, а не позиционные. Это обычная идиома, например. Python, где у вас есть контекст для форматирования строк, который может или не может использовать все доступные аргументы, например
mouse_state = {}
mouse_state['button'] = 0
mouse_state['x'] = 50
mouse_state['y'] = 30
#...
"You clicked %(button)s at %(x)d,%(y)d." % mouse_state
"Targeting %(x)d, %(y)d." % mouse_state
Существуют ли библиотеки, предлагающие функциональность этих двух последних строк? Я ожидаю, что он предложит API что-то вроде:
PrintFMap(string format, map<string, string> args);
В Googling я нашел много библиотек, предлагающих варианты позиционных параметров, но ни один из них не поддерживает названные. В идеале библиотека имеет несколько зависимостей, поэтому я могу легко ее поместить в свой код. С++ не будет столь же идиоматичным для сбора именованных аргументов, но, вероятно, кто-то там подумал об этом больше, чем я.
Важное значение имеет производительность, в частности, я хочу, чтобы сокращения памяти (всегда сложные на С++), так как это может запускаться на устройствах без виртуальной памяти. Но даже медленный, чтобы начать с, вероятно, будет быстрее, чем писать его с нуля.
Кажется, что ответ - нет, нет библиотеки С++, которая делает это, а программисты на С++, по-видимому, даже не видят необходимости в ней, основываясь на полученных комментариях. Мне придется снова написать свой собственный.
библиотека fmt поддерживает именованные аргументы:
print("You clicked {button} at {x},{y}.",
arg("button", "b1"), arg("x", 50), arg("y", 30));
И в качестве синтаксического сахара вы можете даже (ab) использовать пользовательские литералы для передачи аргументов:
print("You clicked {button} at {x},{y}.",
"button"_a="b1", "x"_a=50, "y"_a=30);
Для краткости пространство имен fmt
опущено в приведенных выше примерах.
Отказ от ответственности: я являюсь автором этой библиотеки.
Я всегда критиковал С++ I/O (особенно форматирование), потому что, на мой взгляд, это шаг назад в отношении C. Форматы должны быть динамичными и, в частности, имеют смысл для загрузки их из внешнего ресурса в виде файла или параметра.
Я никогда не пробовал, прежде чем на самом деле реализовать альтернативу, и ваш вопрос заставил меня попытаться инвестировать некоторые выходные в эту идею.
Уверенная проблема была более сложной, чем я думал (например, просто целочисленная процедура форматирования - 200+ строк), но я думаю, что этот подход (строки динамического формата) более полезен.
Вы можете загрузить мой эксперимент из эту ссылку (это только файл .h) и тестовую программу из эта ссылка (тест, вероятно, не правильный термин, я использовал его, чтобы увидеть, удалось ли мне скомпилировать).
Ниже приведен пример
#include "format.h"
#include <iostream>
using format::FormatString;
using format::FormatDict;
int main()
{
std::cout << FormatString("The answer is %{x}") % FormatDict()("x", 42);
return 0;
}
Он отличается от подхода boost.format, потому что использует именованные параметры и потому что
слова формата и формат словаря должны быть построены отдельно (и для
пример прошел). Также я считаю, что параметры форматирования должны быть частью
string (например, printf
), а не в коде.
FormatDict
использует трюк для разумного соблюдения синтаксиса:
FormatDict fd;
fd("x", 12)
("y", 3.141592654)
("z", "A string");
FormatString
вместо этого просто анализируется с помощью const std::string&
(я решил подготовить строки форматирования, но более медленный, но, вероятно, приемлемый подход будет просто передавать строку и каждый раз переписывать ее).
Форматирование может быть расширено для пользовательских типов, специализируясь на шаблоне функции преобразования; например
struct P2d
{
int x, y;
P2d(int x, int y)
: x(x), y(y)
{
}
};
namespace format {
template<>
std::string toString<P2d>(const P2d& p, const std::string& parms)
{
return FormatString("P2d(%{x}; %{y})") % FormatDict()
("x", p.x)
("y", p.y);
}
}
после этого экземпляр P2d
можно просто поместить в словарь форматирования.
Также возможно передать параметры функции форматирования, разместив их между %
и {
.
В настоящее время я только реализовал целочисленную специализацию форматирования, которая поддерживает
Я также добавил несколько ярлыков для обычных случаев, например
"%08x{hexdata}"
- шестнадцатеричное число с 8 цифрами, заполненными "0".
"%026/2,8:{bindata}"
- 24-битное двоичное число (как требуется "/2"
) с разделителем цифр ":"
каждые 8 бит (как требуется ",8:"
).
Обратите внимание, что код - это всего лишь идея, и на данный момент я просто предотвратил копии, когда, вероятно, разумно разрешить хранить как форматированные строки, так и словари (для словарей важно, однако, дать возможность избежать копирования объекта только потому, что его нужно добавить к FormatDict
, а в то время как IMO это возможно, это также то, что поднимает нетривиальные проблемы о временах жизни).
Я сделал несколько изменений в первоначальном подходе:
Я создал проект github для него, с ускорением лицензирования.
Хорошо, я также добавлю свой собственный ответ, но не знаю, что я знаю (или закодировал) такую библиотеку, но чтобы ответить на бит "сохранить разряд памяти".
Как всегда, я могу представить себе компромисс между скоростью и памятью.
С одной стороны, вы можете разобрать "Just In Time":
class Formater:
def __init__(self, format): self._string = format
def compute(self):
for k,v in context:
while self.__contains(k):
left, variable, right = self.__extract(k)
self._string = left + self.__replace(variable, v) + right
Таким образом, вы не держите "проанализированную" структуру под рукой, и, надеюсь, большую часть времени вы просто вставляете новые данные на место (в отличие от Python, строки С++ не являются неизменяемыми).
Однако это далеко не эффективно...
С другой стороны, вы можете построить полностью построенное дерево, представляющее проанализированный формат. У вас будет несколько классов типа: Constant
, String
, Integer
, Real
и т.д. И, возможно, некоторые подклассы/декораторы, а также для самого форматирования.
Я думаю, однако, что самый эффективный подход состоял бы в том, чтобы иметь какое-то сочетание двух.
Constant
, Variable
Loki::AssocVector
).Вот вы: вы закончили с двумя динамически распределенными массивами (в основном). Если вы хотите, чтобы один и тот же ключ повторялся несколько раз, просто используйте std::vector<size_t>
в качестве значения индекса: хорошие реализации не должны выделять какую-либо память динамически для векторов малого размера (VС++ 2010 не менее 16 байт ценность данных).
При оценке самого контекста найдите экземпляры. Затем вы разбираете форматировщик "как раз вовремя", проверяете его как текущий тип значения, с которым его можно заменить, и обрабатывайте формат.
Плюсы и минусы: - Just In Time: вы снова и снова просматриваете строку - One Parse: требуется много выделенных классов, возможно, много распределений, но формат проверяется на входе. Как и Boost, его можно использовать повторно. - Смешивание: более эффективно, особенно если вы не заменяете некоторые значения (допускаете какое-то "нулевое" значение), но отсрочка разбора формата задерживает сообщение об ошибках.
Лично я пошел бы за схемой One Parse
, пытаясь сохранить распределения с помощью boost::variant
и шаблона стратегии как можно больше.
Учитывая, что Python сам написан на C, и что форматирование является такой часто используемой функцией, вы можете (игнорируя проблемы с копированием) разорвать соответствующий код из интерпретатора python и портировать его для использования STL-карт, а не Питоны native dicts.
Я написал библиотеку для этого кукольника, проверьте его на GitHub.
Взносы - хорошие результаты.