Регистрация | Вход 
Просмотр статьи
Введение в Boost

В ряде случаев Boost позволяет значительно увеличить производительность программиста C++. Наверное, каждому си-плюc-плюснику следует знать о ней, как и об STL. В статье предлагает обзор наиболее часто используемых (мною) средств Boost.

Как-то на RSDN я прочитал, что Boost "драматически" ускоряет разработку. Вот-таки да. Для целого ряда рутинных и не очень задач библиотека Boost просто незаменима. Скажем, вроде бы такая простая задача, как сравнение строк без учета регистров, в С++ вырастает в настоящую проблему: необходимо сначала определить новый std::char_traits, выполняющий сравнение строк без учета регистра, а затем создать на его базе новый класс istring, экземпляры которого будут сравниваться без учета регистра. Угу, а если те же строки надо сравнить с учетом регистра? Это что преобразовывать снова в std::string? В общем, аут полный. Можно еще воспользоваться одной из функций стандартной библиотеки, принимающими char *, завершающимися символом NULL. Стоит ли напоминать, что экземпляры string не обязаны завершаться на NULL, и могут содержать несколько этих символов. В общем, более-менее универсальное решение видится только в использовании функции std::equals и нужного функтора. Ну, а если нужно распрасить CSV-файл? Тоже приятного мало. Поиск запятых и кавычек после этого будет сниться во сне. А затем еще необходимо конвертировать строки в int и double, а вдруг эти строки содержат ведущие или хвостовые пробелы? Ох, грехи мои тяжкие. Конечно, эти задачи решаются в C++, но достаточно муторно все это, а в том же C# существуют функции String.Trim() и Convert.ToDouble().

Что есть Boost

Итак, что такое библиотека Boost. Ее история начинается с того времени, когда принимали стандарт С++. В какой-то момент, для форсирования принятия стандарта, было принято решение не включать в С++ новых возможностей (разгрести бы то, что уже добавили). В качестве к дополнению к стандарту был организован набор библиотек Boost (не оставлять же в свободном плавании превосходные библиотеки). Принципы добавления новых библиотек в Boost довольно просты: сообщество Boost должно одобрить идею (ее необходимость и полезность) новой библиотеки и реализацию оной. Дело это довольно небыстрое, сообщество общается посредством новостных групп.

Библиотека (ну, или набор библиотек – что не суть важно) Boost расширяет и дополняет STL.

Скачать Boost вы можете по адресу: http://www.boost.org/. Там же есть и инструкции по установке (см. Getting Started).

Примеры использования Boost

Перебор элементов коллекции

Для доступа к элементам коллекции библиотека STL предлагает использовать итераторы.

В следующем примере, просто создается и заполняется vector<int>, а затем все элементы вектора выводятся на консоль:

#include <vector>
#include <iostream>

void stl_foreach()
{
	std::vector<int> lstInt;
	for(int i = 0; i < 10; ++i)
	{
		lstInt.push_back(i);
	}

	std::vector<int>::iterator pos = lstInt.begin();
	for(; pos != lstInt.end(); ++pos)
	{
		std::cout << *pos << std::endl;
	}
}

Макрос BOOST_FOREACH предлагает абстрагироваться от концепции итераторов и перебрать элементы коллекции в стиле оператора foreach C#:

#include <vector>
#include <iostream>

#include <boost/foreach.hpp>

void boost_foreach()
{
	std::vector<int> lstInt;
	for(int i = 0; i < 10; ++i)
	{
		lstInt.push_back(i);
	}

	BOOST_FOREACH(int i, lstInt)
	{
		std::cout << i << std::endl;
	}
}

В ряде случаев такой подход более удобен и просто сокращает время на набор. В общем, не самый бесполезный макрос.

Форматированный вывод

Вновь вернемся к C#. Тем, кто знаком с .NET не понаслышке, наверняка знакомы методы Console.WriteLine() и String.Format(). Позволяющие создавать форматированный вывод, да и просто создавать форматированные строки:

using System;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("{0}+{1}={2}", 5, 6, 5+6);
    }
}

Библиотека Boost предлагает класс boost::format с аналогичными возможностями форматирования:

#include <iostream>

#include <boost/format.hpp>

void format1()
{
	std::cout << boost::format("%1%+%2%=%3%") % 5 % 6 % (5+6) << std::endl;
}

Более сложный пример форматирования:

//простой пример форматирования
void format2()
{
	std::cout << boost::format("%|1$7|+%|2$7|=%|3$+7|") % 5 % 6 % (5+6) << std::endl;
	std::cout << boost::format("%|7|+%|7|=%|+7|") % 5 % 6 % (5+6) << std::endl;
}

Обе строки выводят:

>       5+      6=       +11

Также возможно форматирование в стиле printf:

//форматирование в стиле printf
void format3()
{
	std::cout << boost::format("%7d+%7d=%+7d") % 5 % 6 % (5+6) << std::endl;
	std::cout << boost::format("%1$7d+%2$7d=%3$+7d") % 5 % 6 % (5+6) << std::endl;
}

Вывод такой же как в предыдущем примере.

Для преобразования объекта boost::format в std::string предназначена функция boost::str:

void format4()
{
	boost::format formatter("%1%+%2%=%3%");
	std::string str = boost::str(formatter % 5 % 6 % (5+6));
	std::cout << str << std::endl;
}

В целом, boost::format весьма гибкое и мощное средство. И для более полного использования его возможностей я рекомендую изучить документацию по нему.

Строковые операции.

Библиотека Boost string_algo предлагает не самый маленький набор строковых операций, среди них поиск вхождения подстрок, в том числе с использованием регулярных выражение, операции сравнения с учетом и без учета регистра, обрезка ведущих и хвостовых пробелов и много других операций. Для использования библиотеки необходимо включить заголовочный файл boost/algorithm/string.hpp. К сожалению, строковые алгоритмы этой библиотеки находятся в пространстве имен boost::algorithm – поэтому без использования директивы using не обойтись. В следующем примере рассматриваются функции сравнения строк с учетом регистра (boost::algorithm::equals) и без учета регистра (boost::algorithm::iequals), а также функция, обрезающая хвостовые и ведущие пробелы (boost::algoritm::trim) – для удобства используется ее аналог boost::algorithm::trim_copy, который вместо модифицирования строки, возвращает измененную копию строки:

#include <string>
#include <iostream>

#include <boost/algorithm/string.hpp>

void string1()
{
	using boost::algorithm::equals;
	using boost::algorithm::iequals;
	using boost::algorithm::trim_copy;

	using std::cout;
	using std::endl;

	std::string str_ABC = "ABC";
	std::string str_abc = "abc";
	cout << std::boolalpha;
	cout << equals(str_ABC, str_abc) << endl;	//false
	cout << iequals(str_ABC, str_abc) << endl;	//true

	std::string str_abc_spaces("  abc   ");
	cout << iequals(str_ABC, str_abc_spaces) << endl;		//false
	cout << iequals(str_ABC, trim_copy(str_abc_spaces)) << endl;	//true
}

Конвертирование строк

Задача конвертирования строк в double, int и т.п. встречается достаточно часто. Библиотека Boost предлагает для этих целей шаблон lexical_cast<>. Использование его элементарно и вряд ли вызовет какие-либо вопросы:

#include <iostream>

#include <boost/lexical_cast.hpp>

void cast1()
{
	using boost::lexical_cast;
	unsigned k = lexical_cast<unsigned>("101");
	std::cout << k << std::endl; 
	double db = lexical_cast<double>("12.3");
	std::cout << db << std::endl;
	try
	{
		float h = lexical_cast<float>("xy12");
	}
	catch(boost::bad_lexical_cast &)
	{
		std::cerr << "Unable to convert \"xy12\" to float" << std::endl;
	}
}

Вывод функции следующий:

101
12.3
Unable to convert "xy12" to float

Как вы уже догадались в случае неудачи boost::lexical_cast<> генерит исключение boost::bad_lexical_cast.

Разбор текста

А напоследок самый интересный пример, парсинг CSV-файла с помощью boost::tokenizer<>. Как видно из названия boost::tokenizer разбивает переданную строку на токены, при этом разделители задаются с помощью первого параметра шаблона. В нашем случае мы будет использовать предопреленный класс boost::escaped_list_separator<>, предназначенный для разбора текста в формате CSV. Для задания иных наборов разделителей обратите внимание на boost::char_separator<>.

Итак, у нас есть CSV-файл test.csv с несколькими записями:

1,"Andrey",200.5
4,"Michael",300
78,"Peter",12.567

 

Задача: распарсить файл и вывести результат на консоль.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/tokenizer.hpp>

void csv_parser(const std::string &file_in)
{
	typedef boost::tokenizer<boost::escaped_list_separator<char> > CsvTokenizer;
	using boost::lexical_cast;
	using boost::algorithm::trim_copy;

	struct Record
	{
		unsigned Index;
		std::string Name;
		double Value;
	};

	std::string line;
	std::ifstream ifs(file_in.c_str());
	std::vector<Record> recs;
	while(getline(ifs, line))
	{
		CsvTokenizer token(line);
		CsvTokenizer::iterator pos = token.begin();
		Record rec;
		rec.Index = lexical_cast<unsigned>(trim_copy(*pos));
		++pos;
		rec.Name = *pos;
		++pos;
		rec.Value = lexical_cast<double>(trim_copy(*pos));
		recs.push_back(rec);
	}

	BOOST_FOREACH(const Record &rec, recs)
	{
		std::cout << boost::format("%1%, %2%, %3%") % rec.Index % rec.Name % rec.Value << std::endl;
	}
}

 

В приведенном выше коде tokenizer получает очередную строку CSV-файла и парсит ее (CsvTokenizer token(line)), а затем возвращает итератор (CsvTokenizer::iterator pos = token.begin()) перечисляющий токены распарсенной строки. Все остальное рассматривалось выше и, думаю, не требует объяснений.

Заключение

Приведенные выше несколько примеров позволят оценить полезность буста и научиться использовать часть его библиотек. Это неплохо для начала использования библиотек Boost, но никак не позволяет претендовать на звание эксперта в нем :-). Boost поставляется вместе с очень неплохой документацией по нему, поэтому недостающие знания можно будет легко восполнить при необходимости.

Александр Игнатьев
http://www.vestace.ru/
18.05.2008


Автор: Alexander
Дата публикации: 08.06.2008
Число просмотров: 2676

Возврат


Copyright 2007-2009 by Alexander Ignatyev