Ввод и вывод информации — критически важная задача, без выполнения которой любая программа становится бесполезной. В C++ для решения данной задачи традиционно применяются потоки ввода-вывода, которые реализованы в стандартной библиотеке IOStream.
Плюсами такого подхода являются:
— универсальность: не важно, с чем связан поток, — ввод-вывод с консоли, файла, сокета, процесса происходит одинаково;
— прозрачность: программисту не нужно явно указывать тип вводимого или выводимого объекта;
— расширяемость: программист может добавить поддержку ввода-вывода в поток для любого своего объекта, просто перегрузив соответствующие операторы.
В библиотеке IOStream есть также класс
Он позволяет делать весьма забавные вещи, например, осуществлять преобразование типов:
Кроме того, этот класс можно использовать для форматирования сложных строк, например:
Понятно, что в данном случае использование
Получается, вам необходим промежуточный объект
C++ порой винят за его чрезмерную сложность и избыточность, которые, однако, в ряде случаев позволяют создавать весьма изящные конструкции. Вот и сейчас, шаблоны и возможность перегрузки операторов помогли мне создать обёртку над
А вот и сам
Работает это очень просто. С одной стороны, в классе
Быть может, кому-то эта обёртка окажется полезной. Буду рад услышать комментарии, предложения и пожелания.
Плюсами такого подхода являются:
— универсальность: не важно, с чем связан поток, — ввод-вывод с консоли, файла, сокета, процесса происходит одинаково;
— прозрачность: программисту не нужно явно указывать тип вводимого или выводимого объекта;
— расширяемость: программист может добавить поддержку ввода-вывода в поток для любого своего объекта, просто перегрузив соответствующие операторы.
В библиотеке IOStream есть также класс
stringstream
, который позволяет связать поток ввода-вывода со строкой в памяти. Всё, что выводится в такой поток, добавляется в конец строки; всё, что считыватся из потока — извлекается из начала строки.Он позволяет делать весьма забавные вещи, например, осуществлять преобразование типов:
#include <sstream>
#include <iostream>
int main(int argc, char* argv[])
{
std::stringstream ss;
ss << "22";
int k = 0;
ss >> k;
std::cout << k << std::endl;
return 0;
}
Кроме того, этот класс можно использовать для форматирования сложных строк, например:
void func(int id, const std::string& data1, const std::string& data2)
{
std::stringstream ss;
ss << "Operation with id = " << id << " failed, because data1 (" << data1 << ") is incompatible with data2 (" << data2 << ")";
std::cerr << ss.str();
}
Понятно, что в данном случае использование
stringstream
излишне, так как сообщение можно было выводить напрямую в cerr
. Но что если вы хотите вывести сообщение не в стандартный поток, а использовать, скажем, функцию syslog()
для вывода сообщения в системный журнал? Или, скажем, сгенерировать исключение, содержащее данную строку как пояснение:void func(int id, const std::string& data1, const std::string& data2)
{
std::stringstream ss;
ss << "Operation with id = " << id << " failed, because data1 (" << data1 << ") is incompatible with data2 (" << data2 << ")";
throw std::runtime_error(ss.str());
}
Получается, вам необходим промежуточный объект
stringstream
, в котором вы сначала формируете строку, а затем получаете её с помощью метода str()
. Согласитесь, громоздко?C++ порой винят за его чрезмерную сложность и избыточность, которые, однако, в ряде случаев позволяют создавать весьма изящные конструкции. Вот и сейчас, шаблоны и возможность перегрузки операторов помогли мне создать обёртку над
stringstream
, которая позволяет форматировать строку в любом месте кода без дополнительных переменных как если бы я просто выводил данные в поток:void func(int id, const std::string& data1, const std::string& data2)
{
throw std::runtime_error(MakeString() << "Operation with id = " << id << " failed, because data1 (" << data1 << ") is incompatible with data2 (" << data2 << ")");
}
А вот и сам
MakeString
:class MakeString {
public:
template<class T>
MakeString& operator<< (const T& arg) {
m_stream << arg;
return *this;
}
operator std::string() const {
return m_stream.str();
}
protected:
std::stringstream m_stream;
};
Работает это очень просто. С одной стороны, в классе
MakeString
перегружен оператор вывода (<<), который принимает в качестве аргумента константную ссылку на объект любого типа, тут же выводит этот объект в свой внутренний stringstream
и возвращает ссылку на себя. С другой стороны, перегружен оператор преобразования к строке, который возвращает строку, сформированную stringstream
'ом.Быть может, кому-то эта обёртка окажется полезной. Буду рад услышать комментарии, предложения и пожелания.