Comments 32
Есть большущий минус, если захочется локализовать приложение, то такой вариант никак не подойдет. Для этого и придумано семейство printf, с другой стороны С++ не позволяет указать порядок. А вот boost::format это делает с легкостью, многие почему-то боятся boost, но данная библиотека очень сильно экономит время при разработке ПО.
+7
Классно! Спасибо за наводку на boost::format.
#include <boost/format.hpp>
class MakeString2 {
public:
MakeString2(const char* fmt): m_fmt(fmt) {}
template<class T>
MakeString2& operator<< (const T& arg) {
m_fmt % arg;
return *this;
}
operator std::string() const {
return m_fmt.str();
}
protected:
boost::format m_fmt;
};
void func(int id, const std::string& data1, const std::string& data2)
{
throw std::runtime_error(MakeString2("Operation with id = %1% failed, because data1 (%2%) is incompatible with data2 (%3%)") << id << data1 << data2);
}
+1
Более того, аналогичная описанной в статье обёртка над std::stringstream находится где-то глубоко внутри boost::lexical_cast.
+1
Могут возникнуть проблемы при форматированном выводе дробных чисел, указателей и т.д.
Имхо, printf более конкретен.
Имхо, printf более конкретен.
0
В С++ не бум-бум, но недавно увидел, что с помощью этой библиотеки удачно делаь строку на подстроку через делитель. Может кто-либо подсказать более изящнее решение на отсутствие таких стандартных функций как split; explode etc.?
0
boost::tokenizer или boost::split из набора строковых алгоритмов, недостающих в C++ (boost string algo): www.boost.org/doc/libs/1_47_0/doc/html/string_algo.html
Первый мне не понравился, в нем нельзя разделители предикатом задавать (мне нужно было ::std::is_punct), а он умеет только массив символов принимаь.
Со вторым все ок.
Первый мне не понравился, в нем нельзя разделители предикатом задавать (мне нужно было ::std::is_punct), а он умеет только массив символов принимаь.
Со вторым все ок.
+1
При работе с вектором, например, очень удобно можно использовать ostream_iterator
0
Года 3 назад, тоже такую же обертку где-то у себя в проектах делал. Проблем не возникло.
0
Use boost::lexical_cast, Luke!
Он на порядок(!) быстрее стрингстрима за счет того, что не требует создания тяжелого объекта. Если не учитывать создание объекта, то он все равно быстрее, хотя уже на десятки процентов.
Сравнение внизу страницы: www.boost.org/doc/libs/1_47_0/libs/conversion/lexical_cast.htm
Он на порядок(!) быстрее стрингстрима за счет того, что не требует создания тяжелого объекта. Если не учитывать создание объекта, то он все равно быстрее, хотя уже на десятки процентов.
Сравнение внизу страницы: www.boost.org/doc/libs/1_47_0/libs/conversion/lexical_cast.htm
+1
Месяцем раньше и я бы вас расцеловал :)
0
Вот сэкономьте поцелуи, пробегитесь за пару дней по документации boost, хотя бы просто по общему описанию всех библиотек, чтобы знать когда куда копать.
Не тратьте поцелуи зря)
Не тратьте поцелуи зря)
+2
А есть ли в boost что-нибудь похожее на MakeString или MakeString2?
0
boost::lexical_cast(«123»);
boost::lexical_cast(123);
boost::lexical_cast(123);
0
А как использовать boost::lexical_cast для форматирования строки?
Будьте добры, перепишите мой пример с выбрасыванием исключения с использованием boost::lexical_cast.
Будьте добры, перепишите мой пример с выбрасыванием исключения с использованием boost::lexical_cast.
0
void func(int id, const std::string& data1, const std::string& data2)
{
throw std::runtime_error("Operation with id = " + boost::lexical_cast<std::string>(id) + " failed, because data1 (" + data1 + ") is incompatible with data2 (" + data2 + ")");
}
0
Угу. Только нужно первый строковый операнд ещё принудительно привести к std::string, т.к. для const char* не определен оператор "+".
Но я впрочем, не об этом. Положим, data1 и data2 имеют сложный тип (пусть это будут экземпляры классов A и B). Тогда имеем:
Это сработает, если для A и B определены операторы вывода в поток (<<).
А теперь сравните то же самое с использованием предлагаемого класса MakeString:
По-моему, получилось значительно компактнее, разве нет?
Я просто хочу подчеркнуть, что целью данного топика было представить класс MakeString() в контексте его использования для форматирования строк, а не использование std::stringstream для преобразования типов.
MakeString() позволяет форматировать строку, как если бы она была потоком.
MakeString — 'inplace' конвертер из std::ostream в std::string. Вот в чем суть. Наверное, стоит добавить эти разъяснения в топик.
Но я впрочем, не об этом. Положим, data1 и data2 имеют сложный тип (пусть это будут экземпляры классов A и B). Тогда имеем:
void func(int id, const A& data1, const B& data2)
{
throw std::runtime_error(std::string("Operation with id = ") + boost::lexical_cast<std::string>(id) + " failed, because data1 (" + boost::lexical_cast<std::string>(data1) + ") is incompatible with data2 (" + boost::lexical_cast<std::string>(data2) + ")");
}
Это сработает, если для A и B определены операторы вывода в поток (<<).
А теперь сравните то же самое с использованием предлагаемого класса MakeString:
void func(int id, const A& data1, const B& data2)
{
throw std::runtime_error(MakeString() << "Operation with id = " << id << " failed, because data1 (" << data1 << ") is incompatible with data2 (" << data2 << ")");
}
По-моему, получилось значительно компактнее, разве нет?
Я просто хочу подчеркнуть, что целью данного топика было представить класс MakeString() в контексте его использования для форматирования строк, а не использование std::stringstream для преобразования типов.
MakeString() позволяет форматировать строку, как если бы она была потоком.
MakeString — 'inplace' конвертер из std::ostream в std::string. Вот в чем суть. Наверное, стоит добавить эти разъяснения в топик.
0
Угу. Только нужно первый строковый операнд ещё принудительно привести к std::string, т.к. для const char* не определен оператор "+".Не нужно, я же проверил на компилябельность перед отправкой комментария.
Ну а так, для составления сложных строк уже придуман упомянутый выше boost::format, его можно научить и с пользовательскими типами работать.
0
Не нужно, я же проверил на компилябельность перед отправкой комментария.
А я поленился :)
Ну а так, для составления сложных строк уже придуман упомянутый выше boost::format, его можно научить и с пользовательскими типами работать.
Беда в том, что boost::format, как выяснилось, тоже не умеет автоматически преобразовываться к строке:
std::cout << boost::format("%2% - %1%") % 1 % 2 << std::endl; // работает
...
boost::format fmt("%2% - %1%");
fmt % 1 % 2;
std::string str = fmt.str() // работает
...
boost::format fmt("%2% - %1%");
std::string str = fmt % 1 % 2; // не работает
...
std::string str = boost::format("%2% - %1%") % 1 % 2; // не работает
...
throw std::runtime_error(boost::format("%2% - %1%") % 1 % 2); // не работает
...
std::string str = MakeString2("%2% - %1%") << 1 << 2; // работает (MakeString2 определен в моем комментарии выше)
0
Да, классы хорошие. С их помощью очень удобно делать логгер, который понимает любые типы, которые умеют сериализоваться в std::ostream
Например так:
Например так:
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
class LogMessage
{
public:
~LogMessage()
{
cout << msg << endl;
}
static LogMessage log()
{
return LogMessage();
}
template<class T>
LogMessage& operator<<(const T& obj)
{
std::ostringstream ostr;
ostr << obj;
if(!msg.empty())
msg+=" ";
msg+=ostr.str();
return *this;
}
private:
LogMessage(){};
std::string msg;
};
LogMessage log()
{
return LogMessage::log();
}
struct Custom
{
Custom():x(33), name("some_obj"){}
int x;
std::string name;
};
std::ostream& operator<< (std::ostream& ostr, const Custom& obj)
{
return ostr << "x="<<obj.x<<", name: " << obj.name;
}
int main()
{
log() << "hey" << 33;
Custom customObject;
customObject.x = 37;
log() << 3.4 << " and " << customObject;
}
0
Боже, зачем вы каждый раз создаете ::std::ostringstream, тем более, если это метод, а не свободная функция?
Ну сделайте вы его членом, да очищайте через ostream.str(::std::string()).
Тому, кто скажет, что это преждевременная оптимизация, я отвечу, что не сделать этого — преждевременная пессимизация, так как перенести 1 строку легко, а искать почему с логами тормозит, а без них нет профилировщиком — бесполезная трата времени получится.
Ну сделайте вы его членом, да очищайте через ostream.str(::std::string()).
Тому, кто скажет, что это преждевременная оптимизация, я отвечу, что не сделать этого — преждевременная пессимизация, так как перенести 1 строку легко, а искать почему с логами тормозит, а без них нет профилировщиком — бесполезная трата времени получится.
+2
Есть принцип — создавать объекты в наименьшей области видимости. Это говорит компилятору о том, что где нужно, и дальше он сам уже может оптимизировать.
Насчет данного примера — логи тормозят в момент вывода на консоль или в момент записи в лог файл. Поэтому иногда приходится делать их асинхронными, но вот создание временных объекто не тормозило ни разу.
PS. Если вам ну так сильно хочется оптимизровать код — то можно сразу же заменить код фукнции на boost::lexical_cast — он в разы быстрее, и точней отражает сущность нужного нам преобразования.
Насчет данного примера — логи тормозят в момент вывода на консоль или в момент записи в лог файл. Поэтому иногда приходится делать их асинхронными, но вот создание временных объекто не тормозило ни разу.
PS. Если вам ну так сильно хочется оптимизровать код — то можно сразу же заменить код фукнции на boost::lexical_cast — он в разы быстрее, и точней отражает сущность нужного нам преобразования.
-1
Если вы так боитесь преждевременной пессимизации в связи с созданием временных переменных, то предлагаю при старте программы выделять массив данных, и потом все переменные использовать как элементы этого массива.
-1
Вы можете шуть сколько угодно, но я натыкался на горло, когда создание стрингстрима тормозило. Конкретно он — тяжелый объект.
Касательно компилятора — оптимизаторы значительно умнее, чем вы, вероятно, думаете, и по CFG спокойно можно понять, где что используется и без областей видимостей, не говорите глупостей.
Касательно компилятора — оптимизаторы значительно умнее, чем вы, вероятно, думаете, и по CFG спокойно можно понять, где что используется и без областей видимостей, не говорите глупостей.
0
в качестве альтернативы хитрожопому классу с неявным преобразованием к std::string могу предложить хитрожопый макрос, не использования ради, а забавы для.
использование:
#define STR(WHAT) ({std::stringstream e;e<<WHAT;e.str();})
использование:
throw runtime_error(STR("2+2="<<5));
+1
А такая же статья только форматированный ввод есть?
0
Sign up to leave a comment.
Articles
Change theme settings
std::stringstream и форматирование строк