Показаны сообщения с ярлыком boost. Показать все сообщения
Показаны сообщения с ярлыком boost. Показать все сообщения

1 апр. 2009 г.

Использование boost::spirit

В данной статье я расскажу как написать свой парсер ini-файлов на C++. За основу возьмём контекстно-свободную грамматику, построенную в предыдущей статье "Создаём парсер для ini-файлов. Теория". Для построения парсера будет использоваться библиотека Boost Spirit, которая позволяет строить свои собственные парсеры комбинируя готовые примитивные парсеры при помощи парсерных комбинаторов.

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

Грамматика


Для начала вспомним какую грамматику для ini-фалов мы построили в предыдущей статье:
inidata = spaces, {section} .
section = "[", ident, "]", stringSpaces, "\n", {entry} .
entry = ident, stringSpaces, "=", stringSpaces, value, "\n", spaces .
ident = identChar, {identChar} .
identChar = letter | digit | "_" | "." | "," | ":" | "(" | ")" | "{" | "}" | "-" | "#" | "@" | "&" | "*" | "|" .
value = {not "\n"} .
stringSpaces = {" " | "\t"} .
spaces = {" " | "\t" | "\n" | "\r"} .

Её описание нам скоро понадобится.

C++ и Boost Spirit



Начните с установки boost (можно взять на официальном сайте или поискать готовые пакеты для вашей OS). Собирать boost не требуется, так как весь Spirit живёт в хедерах. Процесс установки для разных систем может быть различным, поэтому я не буду его здесь описывать.

Я постараюсь подробно описать процесс создания парсера на С++. При этом я не буду особенно думать о производительности, так как это не является целью данной статьи.

Начнём с подключения необходимых хедеров.
1 #include <fstream>
2 #include <functional>
3 #include <numeric>
4 #include <list>
5 #include <vector>
6 #include <string>
7
8 #include <boost/spirit.hpp>
9 #include <boost/algorithm/string.hpp>
10
11 using namespace std;
12 using namespace boost::spirit;

Кроме хедера самого Spirit я включил библиотеку строчных алгоритмов из boost-а (буду использовать функцию trim). Конструкция using namespace не всегда является хорошей практикой, но здесь для краткости я себе это позволю.

Определим типы данных: запись — это пара «ключ — значение», секция — это пара «ключ — список записей», все данные ini-файла — это список секций.
14 typedef pair<string, string> Entry;
15 typedef list<Entry > Entries;
16 typedef pair<string, Entries> Section;
17 typedef list<Section> IniData;

Кроме типов данных нам потребуются обработчики событий, которые будут вызываться в тот момент, когда парсер разберёт очередной нетерминал.
19 struct add_section
20 {
21 add_section( IniData & data ) : data_(data) {}
22
23 void operator()(char const* p, char const* q) const
24 {
25 string s(p,q);
26 boost::algorithm::trim(s);
27 data_.push_back( Section( s, Entries() ) );
28 }
29
30 IniData & data_;
31 };
32
33 struct add_key
34 {
35 add_key( IniData & data ) : data_(data) {}
36
37 void operator()(char const* p, char const* q) const
38 {
39 string s(p,q);
40 boost::algorithm::trim(s);
41 data_.back().second.push_back( Entry( s, string() ) );
42 }
43
44 IniData & data_;
45 };
46
47 struct add_value
48 {
49 add_value( IniData & data ) : data_(data) {}
50
51 void operator()(char const* p, char const* q) const
52 {
53 data_.back().second.back().second.assign(p, q);
54 }
55
56 IniData & data_;
57 };


Обработчики событий представляют собой функторы, которые принимают на вход кусок строки (через два указателя).
Функтор add_section будет вызываться в тот момент, когда парсер распознает очередную секцию. В качестве параметра add_section получит имя этой секции. Функтор add_key будет вызван в тот момент, когда парсер распознает имя нового параметра. Функтор add_value будет вызван в тот момент, когда парсер распознает значение параметра. При помощи этих функторов организуется последовательное заполнение IniData: сначала добавляется пустая секция (add_section), потом в эту секцию кладется Entry с незаполненным значением (add_key), а потом это значение заполняется (add_value).

Теперь будем переносить грамматику из нотации Бэкуса-Наура в C++. Для этого создаётся специальный класс inidata_parser.
59 struct inidata_parser : public grammar<inidata_parser>
60 {
61 inidata_parser(IniData & data) : data_(data) {}
62
63 template <typename ScannerT>
64 struct definition
65 {
66 rule<ScannerT> inidata, section, entry, ident, value, stringSpaces, spaces;
67
68 rule<ScannerT> const& start() const { return inidata; }
69
70 definition(inidata_parser const& self)
71 {
72 inidata = *section;
73
74 section = ch_p('[')
75 >> ident[add_section(self.data_)]
76 >> ch_p(']')
77 >> stringSpaces
78 >> ch_p('\n')
79 >> spaces
80 >> *(entry);
81
82 entry = ident[add_key(self.data_)]
83 >> stringSpaces
84 >> ch_p('=')
85 >> stringSpaces
86 >> value[add_value(self.data_)]
87 >> spaces;
88
89
90 ident = +(alnum_p | chset<>("-_.,:(){}#@&*|") );
91
92 value = *(~ch_p('\n'));
93
94 stringSpaces = *blank_p;
95
96 spaces = *space_p;
97 }
98
99 };
100
101 IniData & data_;
102 };

Этот класс инкапсулирует в себе всю грамматику. Разберёмся поподробнее. В строке 59 мы видим, что парсер наследуется от шаблонного класса grammar, используя crtp, — это необходимo для правильной работы Spirit-а. Парсер принимает в конструкторе ссылку на незаполненную IniData и сохраняет её (61). Внутри парсера нужно определить шаблонную структуру definition (63-64). У структуры definitionесть члены данных типа rule — это парсеры для каждого из нетерминалов нашей грамматики в форме Бэкуса-Наура (66). Необходимо определить функцию-член start, которая будет возвращать ссылку на главный нетерминал — inidata (68).

В конструкторе definition мы описываем грамматику. Грамматика переписывается на C++ почти дословно. inidata состоит из нескольких секций (72) — это выражается звёздочкой (как замыкание Клини, но звёздочка слева). Секция начинается с квадратной скобки — для этого используется встроенный парсер ch_p, который парсит один символ. Вместо запятой из нотации Бэкуса-Наура используется оператор >>. В квадратных скобках после выражения пишется функтор-обработчик события (75, 82, 86). Символ "+" слева означает «хотя бы один», а "~" означает отрицание. alnum_p — встроенный парсер для букв и цифр. chset<> соответствует любому символу из строки (важно, что минус идёт первым, иначе он воспринимается как знак интервала, вроде «a-z»). blank_p соответствует пробельному символу в строке (пробел или табуляция), space_p соответствует любому пробельному символу (в т.ч. и переводу строки и возврату каретки).

Отметим, что нетерминалы ident и identChar удалось слить в один благодаря оператору "+" — в нотации Бэкуса-Наура это было невозможно, т.к. там отсутствует подобное обозначение.

С грамматикой всё. Осталось научиться удалять комментарии и искать значение в IniData.
Для удаления комментариев нам потребуется специальный функтор.
104 struct is_comment{ bool operator()( string const& s ) const { return s[] == '\n' || s[] == ';'; } };

Теперь напишем функцию поиска в IniData.
106 struct first_is
107 {
108 first_is(std::string const& s) : s_(s) {}
109
110 template< class Pair >
111 bool operator()(Pair const& p) const { return p.first == s_; }
112
113 string const& s_;
114 };
115
116 bool find_value( IniData const& ini, string const& s, string const& p, string & res )
117 {
118 IniData::const_iterator sit = find_if(ini.begin(), ini.end(), first_is(s));
119 if (sit == ini.end())
120 return false;
121
122 Entries::const_iterator it = find_if(sit->second.begin(), sit->second.end(), first_is(p));
123 if (it == sit->second.end())
124 return false;
125
126 res = it->second;
127 return true;
128 }

Вместо функтора first_is можно применить boost::bind, но я решил не мешать всё в одну кучу. С поиском всё просто: сначала в списке ищем секцию по имени, потом в списке записей секции ищем параметр по имени, и, если всё нашлось, то возвращаем значение параметра через параметр-ссылку.

Осталось написать main.
130 int main( int argc, char** argv)
131 {
132 if ( argc != 4 )
133 {
134 cout << "Usage: " << argv[] << " <file.ini> <section> <parameter>" << endl;
135 return ;
136 }
137
138 ifstream in(argv[1]);
139 if( !in )
140 {
141 cout << "Can't open file \"" << argv[1] << '\"' << endl;
142 return 1;
143 }
144
145 vector< string > lns;
146
147 std::string s;
148 while( !in.eof() )
149 {
150 std::getline( in, s );
151 boost::algorithm::trim(s);
152 lns.push_back( s+='\n' );
153 }
154 lns.erase( remove_if(lns.begin(), lns.end(), is_comment()), lns.end());
155 string text = accumulate( lns.begin(), lns.end(), string() );
156
157 IniData data;
158 inidata_parser parser(data); // Our parser
159 BOOST_SPIRIT_DEBUG_NODE(parser);
160
161 parse_info<> info = parse(text.c_str(), parser, nothing_p);
162 if (!info.hit)
163 {
164 cout << "Parse error\n";
165 return 1;
166 }
167
168 string res;
169 if (find_value(data, argv[2], argv[3], res))
170 cout << res;
171 else
172 cout << "Can't find requested parameter";
173 cout << endl;
174 }


Строки 132-136 — проверяем параметры программы: если их не 4, то выводим usage. Если с параметрами всё ок, то открываем файл (138-143). Если и с файлом всё нормально, то создаём массив строк lns (145) и считываем в него весь файл (147-153). После этого удаляем оттуда комментарии, используя припасённый функтор is_comment (154). В заключение склеиваем все строчки в одну (155).

В строчках 157-159 создаётся и инициализируется парсер. Теперь запускаем парсер — для этого используется функция parse, которая принимает на вход сам текст, парсер и специальный парсер для пропускаемых символов (скажем, мы хотели бы пропускать все пробелы). В нашем случае парсер для пропускаемых символов будет пустым — nothing_p (т.е. ничего не парсящий). Результатом функции parse является структура parse_info<>. Нас интересует булево поле hit этой структуры, которое истинное, если не произошло ошибок. В строчках 162-166 мы сообщаем, если произошла ошибка. Осталось только найти параметр, заданный в командной строке и вывести его значение (168-173).

Теперь код полностью написан. Компилируем его и запускаем на тестовом примере.
$ g++ ini.cpp -o ini_cpp

$ ./ini_cpp /usr/lib/firefox-3.0.5/application.ini App ID
{ec8030f7-c20a-464f-9b0e-13a3a9e97384}

$ ./ini_cpp /usr/lib/firefox-3.0.5/application.ini App IDD
Can't find requested parameter


Надеюсь, что данная статья поможет вам написать свой собственный парсер =)


P.S.: Наглым образом спёрто отсюда: Создаём парсер для ini-файлов на C++

Использование boost::datetime

  • Gregorian
  • Posix Time
  • Local Time

Сейчас наверно сложно найти человека, который не столкнулся бы с необходимостью использования времени в своих целях, а точнее его цифрового представления. Цели могут быть совершенно разными, от простых замеров интервалов времени, в которые выполняется некий участок кода(совершенно не верный метод профайлинга, между прочим, но сейчас не об этом), до поддержки полноценных календарей в своих приложениях, которые должны быть актуальны для любого человека на земном шаре. Все эти манипуляции можно выполнить с помощью довольно аскетичной и простой в использовании, но в тоже время мощной библиотеки Boost.Date_Time. В библиотеке учитываются: високосный год, високосная секунда, летнее время и т.д
Разберемся с некоторой терминологией, используемой в библиотеке:
В библиотеке существую три глобальных типа разделения времени:
  1. Time Point — некая точка во временном континууме, например дата Вашего рождения
  2. Time Duration — интервал времени, не привязанный ни к какой точке временного континуума
  3. Time Interval — интервал времени, привязанный к некой точке во временном континууме

Так же необходимо помнить о разрешающей способности каждого метода получения времени, разрешающая способность(Resolution) показывает — какая минимальная единица времени может быть использована при анализе имеющегося у вас объекта времени. Т.е это не что иное, как степень точности полученной временной точки. Наконец, перейдем к непосредственному знакомству с возможностями библиотеки.

boost::gregorian


Все примеры, используемые в данной статье подразумевает использование using namespace boost::<соответствующий_namespace> в начале каждого компилируемого юнита.
Данный компонент представляет нам Грегорианский календарь и все мыслимые возможности работы с ним. Он в свою очередь делится на следующие компоненты:

Разберем назначение каждой из них:

boost::gregorian::date — предназначен для простого хранения даты – точки во временном континууме. Поддерживает создание даты из строки определенного формата, а также посредством класса boost::date_time::day_clock, основанного на стандартном механизме С\С++ связанным с time_t
Помимо хранения даты(которая не может быть изменена у объекта, кроме как посредством operator=) у класса есть методы* для получения конкретных частей даты(year(), month(), day() etc.), преобразования внутреннего представления даты в строку, определенного формата(to_simple_string(),to_iso_string(),to_iso_extended_string()) и конвертации в(и обратно) стандартную С\С++ структуру tm(to_tm(),date_from_tm())

*- я не привожу описание каждой функции, более того я не буду, приводит полный список имеющихся функций, их список Вы можете посмотреть в ссылках, соответствующих конкретному классу. Функций довольно много и они довольно просты в использовании, так же я опускаю наличие параметров функции, если считаю это незначительным в данный момент.
Пример:
date xGPWStart(1941, Jun, 22);
date xNowdays = day_clock::local_day();
std::cout << "The Great Patriotic War was started in " << xGPWStart << std::endl;
std::cout << "And the current data is " << xNowdays;


* This source code was highlighted with Source Code Highlighter.


Вывод:
The Great Patriotic War was started in 1941-Jun-22
And the current data is 2009-Jul-26

boost::gregorian::date_duration — служит для подсчета дней, используя boost::gregorian::date для вычислений.
Для удобства работы с date_duration существуют три класса: months_duration, years_duration и weeks_duration(так же есть typedef’ы на эти типы, представленные для удобства: months, years и weeks соответственно), которые могут быть прибавлены или вычтены из date_duration чтобы получить желаемый результат. Существует подводный камень, связанный с этими тремя классами. Если использовать их в своих вычислениях, то Вы можете получить результат, который Вы не ожидаете. Приведу пример:
date xSomeDay(1999, Jan, 28);
date xDayInNextMonth;
std::cout << "That's right: " << ( xDayInNextMonth = xSomeDay + months(1) ) << std::endl;
std::cout << "And that's not: " << xDayInNextMonth + months(1);

* This source code was highlighted with Source Code Highlighter.

That's right: 1999-Feb-28
And that's not: 1999-Mar-31

Такое поведение обусловлено особенностью класса months_duration, который будет всегда использовать конец месяца в арифметических операциях, если изначальный объект указывал на одно из возможных чисел, которыми оканчиваются месяцы(28, 29, 30, 31). Будьте внимательны при использовании данного типа, кстати month_iterator лишен этого недостатка(преимущества?), но о нем поговорим позже.

boost::gregorian::date_period — класс представлен для удобного представления интервала между двумя датами, может быть использован для определения вхождения определенной даты во временной интервал(contains()), пересечения интервалов(intersects(), intersection()), смежности дат(is_adjacent()) и определения отношения расположения одной даты относительно другой(is_after(), is_before()). Кроме того, существуют методы для комбинации интервалов(merge(), span()) и их изменения(shift(), expand()). Важно помнить, что последний дегь в периоде не входит в весь период, т.е в периоде 1-Jan-1999\10-Jan-1999 последним днем будет 9 января, а не 10.
Пример:
date_period xGPWDuration( date(1941, Jun, 22), date(1945, May, 9) );
date_period xStalinLifeYears( date(1878, Dec, 18), date(1953, Mar, 6) ); date_period xJukovsIncorrectLifeYears( date(1896, Dec, 6), date(1974, Jun, 14) );
std::cout << "The Great Patriotic War duration is " << xGPWDuration << std::endl;
std::cout << "Was the GPW inside the Stalin's life years? " << std::boolalpha << xStalinLifeYears.contains(xGPWDuration) << std::endl;
std::cout << "Jukov's incorrect life years is " << xJukovsIncorrectLifeYears << std::endl;
xJukovsIncorrectLifeYears.expand( days(5) );
std::cout << "Jukov's correct life years is " << xJukovsIncorrectLifeYears << std::endl;
//Last day isn't included in the interval
date_period xFirstPeriod( date(1999, Jan, 1), date(1999, Jan, 10) );
date_period xSecondPeriod( date(1999, Jan, 10), date(1999, Jan, 12) );
std::cout << "Does these periods intersect? " << std::boolalpha << xFirstPeriod.intersects(xSecondPeriod) << std::endl;

* This source code was highlighted with Source Code Highlighter.

Вывод:
The Great Patriotic War duration is [1941-Jun-22/1945-May-08]
Was the GPW inside the Stalin's life years? true
Jukov's incorrect life years is [1896-Dec-06/1974-Jun-13]
Jukov's correct life years is [1896-Dec-01/1974-Jun-18]
Does these periods intersect? false

boost::gregorian::date_iterator — как должно быть понятно из названия – это типичный итератор, предназначенный для “движения” по датам. date_iterator сам по себе не интересен, т.к является абстрактным классом, более интересны его классы-наследники: day_iterator, week_iterator, month_iterator, year_iterator.
В качестве примера используем пример из date_duration, в котором мы получали некорректную дату (из-за подводных камней с месяцами). Как я и упоминал раньше, в date_iterator подобных проблем нет:
month_iterator xSomeDay(date(1999, Jan, 28));
std::cout << "That's right: " << *++xSomeDay << std::endl;
std::cout << "And that's too!: " << *++xSomeDay;

* This source code was highlighted with Source Code Highlighter.

That's right: 1999-Feb-28
And that's too!: 1999-Mar-28


Алгоритмы по работе с датами — набор разнообразных классов и функций, для различных манипуляций над датами. Каждый класс имеет метод get_data() который позволяет получить дату, сгенерированную данным классом. Классы предоставляют нам следующий функционал:
  • Получить первый, последний или произвольный день для заданного месяца и недели(first_day_of_the_week_in_the_month(), last_day_of_the_week_in_the_month(), nth_day_of_the_week_in_the_month). День недели для поиска задается.
  • Задать частичную дату(без указания года)(partial_date()). А потом получать полную дату, с помощью get_data()
  • Вычислить первый день недели до или после заданной даты(first_day_of_the_week_before(), first_day_of_the_week_after()). День недели для вычисления задается

Функции предоставляют следующий функционал:
  • Вычислить кол-во дней начиная от текущей даты, до следующего или предыдущего, заданного, дня недели(days_until_weekday(), days_before_week_day()).
  • Сгенерировать дату, которая будет является датой следующего или предыдущего, заданного, дня недели. Новая дата будет сгенерирована, относительно предварительно заданной даты.

Примеры:
last_day_of_the_week_in_month xLastFriday(Friday, Jul);
partial_date xJunTen(10, Jun);
std::cout << "What is the date of the last friday in the July 2009? " << xLastFriday.get_date(2009) << std::endl;
std::cout << "Just dusplay 10 Jun of 2009 " << xJunTen.get_date(2009) << std::endl;
std::cout << "How much days from now till next friday? " << days_until_weekday( day_clock::local_day(), greg_weekday(Friday) )<< std::endl;

* This source code was highlighted with Source Code Highlighter.

Вывод:
What is the date of the last friday in the July 2009? 2009-Jul-31
Just dusplay 10 Jun of 2009 2009-Jun-10
How much days from now till next friday? 5

boost::gregorian::gregorian_calendar — предоставляет полезный набор статических функций для работы с датами.
Вместо описания функций, приведу пример их использования(функции просты и их название говорит само за себя):
std::cout << "What the day of the GPW begining? " << DayToString( gregorian_calendar::day_of_week( gregorian_calendar::ymd_type(1941, Jun, 22) ) ) << std::endl;
std::cout << "And what is the number of this day frome the epoch start? " << gregorian_calendar::day_number( gregorian_calendar::ymd_type(1941, Jun, 22) ) << std::endl;
std::cout << "And what is the number of this day frome the epoch start? " << gregorian_calendar::day_number( gregorian_calendar::ymd_type(1400, Jan, 1) ) << std::endl;
std::cout << "What is the last day in the February 1941? " << gregorian_calendar::end_of_month_day(1941, Feb) << std::endl;
std::cout << "What is the date of the 3333333 day from the epoch start? " << date( gregorian_calendar::from_day_number(3333333) ) << std::endl;
std::cout << "Is the 2004 year a leap year? " << std::boolalpha << gregorian_calendar::is_leap_year(2004) << std::endl;

* This source code was highlighted with Source Code Highlighter.

Вывод:
What the day of the GPW begining? Sunday
And what is the number of this day frome the epoch start? 2430168
And what is the number of this day frome the epoch start? 2232400
What is the last day in the February 1941? 28
What is the date of the 3333333 day from the epoch start? 4414-Apr-03
Is the 2004 year a leap year? true

Эмпирическим путем было получено, что для функций day_number() и from_day_number() минимальными значениями являются 1400-Jan-1 и 2232400 соответственно. Если пытаться использовать дату ранее 1400-Jan-1, то получим исключение. То же справедливо и для количества дней.

boost::posix_time


Данный компонент предоставляет нам удобный метод работы с точками во времени, но в отличие от boost::gregorian boost::posix_time предоставляет возможность работы с временными точками более низкого разрешения (вплоть до наносекунд), высокая часть разрешения(дата) реализуется с помощью boost::gregorian. Компонент особенно удобен для задач, в которых необходима высокая точность получения времени(например строка записи в лог файле). Он делится на следующие части:

Разберем назначение каждой части:

boost::posix_time::ptime — представляет собой точку во временном континууме. Очень похожа на boost::gregorian:date но с разрешением до микросекунд. При создании экземпляра класса только по gregorian:date, “низкоразрешающая” часть устанавливается в полночь(все нули).
Пример использования:
ptime xTime(date(1961, Apr, 12), hours(9) + minutes(7));
std::cout << "Did you know that Gagrin said \"Poehali\" at " << xTime << "\n";
ptime xTimeStr( time_from_string("1961-04-12 09.07.00.0000") );
std::cout << "And the same time point constructed from a string: " << xTimeStr << "\n";
std::cout << "Current time with second resolution: " << second_clock::local_time() << "\nAnd with microsecond:" << microsec_clock::local_time();

* This source code was highlighted with Source Code Highlighter.

Вывод:
Did you know that Gagrin said «Poehali» at 1961-Apr-12 09:07:00
And the same time point constructed from a string: 1961-Apr-12 09:07:00
Current time with second resolution: 2009-Jul-29 16:41:51
And with microsecond:2009-Jul-29 16:41:51.087000


boost::posix_time::time_duration — представляет собой длительность во времени, которая не привязана к конкретной дате. Максимальное разрешение длительности ограничено наносекундами, если библиотека собрана с макросом BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG, и микросекундами, по умолчанию. Из объекта может быть получена информация о количестве секунд\микросекунд\миллисекунд\наносекунд(при соответствующей сборке) которые содержатся в текущей временной длительности.
Пример:
time_duration xTime(1,2,3);
std::cout << "Print time: " << xTime << "\n";
std::cout << "Print increased time: " << xTime + hours(3) + seconds(2) + minutes(6) + milliseconds(15) + microseconds(25) << "\n";
std::cout << "Print total seconds: " << xTime.total_seconds() << " milliseconds: " <<
xTime.total_milliseconds() << " microseconds: " << xTime.total_microseconds() << "\n";

* This source code was highlighted with Source Code Highlighter.

Вывод:
Print time: 01:02:03
Print increased time: 04:08:05.015025
Print total seconds: 3723 milliseconds: 3723000 microseconds: 3723000000

boost::posix_time::time_period — представляет собой отрезок во времени, класс схож gregorian::date_period, но имеет более низкую разрешающую способность. Функционал класса позволяет определять вхождение(contains()), пересечение(intersects()) и длину(length()) интервалов. Так же существует возможность расширения(expand()), смещения(shift()) и объединения(merge()) интервалов.
Пример:
ptime xDoomsday( date(2012, Jan, 1) );
time_period xArmageddonLast(xDoomsday, hours(1));
time_period xChakNorrisSmoke(xDoomsday, minutes(1));
std::cout << "Doomsday was during: " << xArmageddonLast<< "\n";
std::cout << "Chak Norris was smoking at " << xChakNorrisSmoke << "\n";
std::cout << "Did Chak Norris smoke during Doomsday breathtaking?" << std::boolalpha <<xArmageddonLast.contains(xChakNorrisSmoke);


* This source code was highlighted with Source Code Highlighter.

Вывод:
Doomsday was during: [2012-Jan-01 00:00:00/2012-Jan-01 00:59:59.999999]
Chak Norris was smoking at [2012-Jan-01 00:00:00/2012-Jan-01 00:00:59.999999]
Did Chak Norris smoke during Doomsday breathtaking?true

boost::posix_time::time_iterator — предназначен(как наверное все догадались :) ) для итерации по времени. Приятной особенностью данного итератора является возможность задания временного интервала, который будет использоваться при итерации, т.е. насколько и в каких единицах будет изменяться текущая точка во временном континууме при каждой итерации. В качестве временных единиц могут быть использованы все единицы от часа до наносекунд (если собрано с соответствующим флагом)
Приведу маленький пример:
ptime xTime(date(2012, Jan, 1));
time_iterator xIt(xTime, hours(6));
std::cout << "6 hours after Domsday has come!!!" << *++xIt;

* This source code was highlighted with Source Code Highlighter.

Вывод:
6 hours after Domsday has come!!!2012-Jan-01 06:00:00

Local Time System


Данный компонент предоставляет нам возможность работы со временем, в разных часовых поясах и с разными правилами перехода на летнее время. Это достаточно мощный и нужный компонент, особенно необходимый для тех приложений где важно не просто текущее(локальное) время, а важен учет различных смещений во времени, согласно региональным соглашениям относительно времени(переход на летние время, часовой пояс и т.д). Итак, приведу список частей, которые входят в данный компонент:


boost::local_time:: posix_time_zone представляет собой набор данных и правил для представления часовых поясов(смещение относительно GMT, правила перехода на летнее время и обратно, название часового пояса и его аббревиатура). Объект данного типа создается на основании строки, формат строки является стандартизированным POSIX форматом(IEEE Std 1003.1) для часовых поясов.
В общем виде эта строка выглядит следующим образом:
std offset dst [offset],start[/time],end[/time]
std — Аббревиатура часового пояса.
offset — Смещение относительно GMT.
dst — Аббревиатура часового пояса, во время действия летнего времени.
[offset] — Показывает насколько изменяется время (в часах), при переходе на летнее время. Опциональный параметр.
start и end — Задают интервал действия летнего времени.
[/time] — Задает точное время в пределах дня, в которое начинается переход на летнее время или летнее время прекращает свое действие.
offset и time имеют следующий формат: [+|-]hh[:mm[:ss]] {h=0-23, m/s=0-59}
start и end могут быть представлены в одном из следующих форматов:
  • Mm.w.d {month=1-12, week=1-5 (5 всегда последняя), day=0-6}
  • Jn {n=1-365 29 февраля – не учитывается}
  • n {n=0-365 Учитывает 29 февраля в високосные годы}

Каждую часть этой строки вы может получить отдельно, с помощью методов данного класса. Не вижу смысла приводить здесь их имена, они достаточно прозрачны и отражают суть их назначения, так что обратитесь к документации, за их списком.
В качестве примера я использовал часовой пояс GMT+3(Московское время):
posix_time_zone xZone("MSK+3MSD+01,M3.5.0/02:00,M10.5.0/02:00");
std::cout << "Dailight period in 2009 started at " << xZone.dst_local_start_time(2009) << "\nAnd it will finish at " << xZone.dst_local_end_time(2009);


* This source code was highlighted with Source Code Highlighter.


Вывод:
Dailight period in 2009 started at 2009-Mar-29 02:00:00
And it will finish at 2009-Oct-25 02:00:00


boost::local_time::tz_database — удобный класс для хранения множества различных часовых поясов. При создании объекта, создается пустая база данных (громко сказано конечно :) ), после чего она может быть вручную заполнена, с помощью метода add_record() или же прочитана из csv(comma separated values) файла, пример такого файла(с большим количеством записей) содержится в %boost%\libs\date_time\data\date_time_zonespec.csv
Формат записи внутри этого файла, должен удовлетворять следующему стандарту:
«ID»,«STD ABBR»,«STD NAME»,«DST ABBR»,«DST NAME»,«GMT offset»,«DST adjustment»,«DST Start Date rule»,«Start time»,«DST End date rule»,«End time»

Где:
ID – Содержит строку, однозначно идентифицирующую данный часовой пояс.
STD ABBR, STD NAME, DST ABBR, DST NAME — Эти поля заполняются строками с именами и аббревиатурами стандартного и летнего времен, зачастую имена и аббревиатуры идентичны.
GMT offset — Смещение во времени, относительно Гринвича. Его формат: {+|-}hh:mm[:ss]
DST adjustment — Смещение относительно GMT offset во время действия “летнего времени”. Формат тот же, что и у GMT offset.
DST Start Date rule — Строка описывающая день в году, который возвещает начало периода “летнего времени”. Так же имеет свой формат(сколько можно форматов разных?):
weekday;day-of-week;month
Где:
weekday — Порядковое числительное, индицирующее какой по счету день в месяце, нас интересует.
day-of-week — день недели.
month — месяц.
Пример:
-1;0;3 – Начало “летнего времени” в России(последнее воскресенье марта)

Start time — Время после полуночи, в которое вступает в силу “летнее время”. Формат тот же, что и у GMT offset.
DST End date rule — Строка описывающая день в году, который возвещает конец периода “летнего времени”. Формат идентичен DST Start Date rule.
End time — Аналог Start time, только для окончания “летнего времени”.
Пример:
tz_database xDb;
xDb.load_from_file("G:\\Program files\\boost\\boost_1_39_0\\\libs\\date_time\\data\\date_time_zonespec.csv");
const std::vector<std::string>& xAllRegions = xDb.region_list();
std::cout << "Print first 10 zone IDs from the boost time zone file:" << std::endl;
for(std::vector<std::string>::const_iterator it = xAllRegions.begin(); it != xAllRegions.begin() + 10; ++it)
std::cout << *it << std::endl;
std::cout << "And time when daylight saving was started at 2009: " << xDb.time_zone_from_region("Europe/Moscow")->dst_local_start_time(2009) << std::endl;

* This source code was highlighted with Source Code Highlighter.


Вывод:
Print first 10 zone IDs from the boost time zone file:
Africa/Abidjan
Africa/Accra
Africa/Addis_Ababa
Africa/Algiers
Africa/Asmera
Africa/Bamako
Africa/Bangui
Africa/Banjul
Africa/Bissau
Africa/Blantyre
And time when daylight saving was started at 2009: 2009-Mar-29 02:00:00


boost::local_time::custom_time_zone — класс для создания описания часового пояса, но в отличие от прежде описанного boost::local_time::posix_time_zone , данный класс использует четыре других класса, при конструировании часового пояса. Это классы: time_duration , time_zone_names , dst_adjustment_offsets и dst_calc_rule. Так как класс не выделяется ничем выдающимся, то просто приведу пример его использования:
time_zone_names xNames("Moscow Standart Time", "MST", "Moscow Daylight Time", "MDT");
time_duration xGMTOffset(3, 0, 0);
dst_adjustment_offsets xRulesOffsets( time_duration(1,0,0), time_duration(2,0,0), time_duration(3,0,0) );
//Mak daylight's rule
last_day_of_the_week_in_month xStartRule(Sunday, Mar);
last_day_of_the_week_in_month xEndRule(Sunday, Oct);

boost::shared_ptr<dst_calc_rule> xRules( new last_last_dst_rule(xStartRule, xEndRule) );
custom_time_zone xCustomTimeZone(xNames, xGMTOffset, xRulesOffsets, xRules);

std::cout << "The our time zone name is: " << xCustomTimeZone.std_zone_name() << "\n"
<< "It has an "<< xCustomTimeZone.base_utc_offset() << " offset from GMT.\n"
<< "And daylight period will end at "<< xCustomTimeZone.dst_local_end_time(2009) <<std::endl;
std::cout << "Posix string which represents our custom_time_zone object is:\n" << xCustomTimeZone.to_posix_string();

* This source code was highlighted with Source Code Highlighter.

Вывод:
The our time zone name is: Moscow Standart Time
It has an 03:00:00 offset from GMT.
And daylight period will end at 2009-Oct-25 03:00:00
Posix string which represents our custom_time_zone object is:
MST+03MDT+01,M3.5.0/02:00,M10.5.0/03:00

Классы boost::local_time::local_date_time и boost::local_time::local_time_period повторяют схожие классы из boost::posix_time, с привязкой к часовому поясу, поэтому я не буду их рассматривать.

Полезные особенности и заключение


В Boost.Data_time, помимо классов для создания дат, есть полезные утилиты, такие как: форматируемая обработка ввода\вывода и сериализация. Оба механизма достаточно просты.


P.S.: Наглым образом спёрто отсюда: Boost это просто. Часть 2. Boost.Date_time.

Использование интеллектуальных указателей из boost

Ни для кого не секрет, что работа с динамической памятью и системными ресурсами вообще является одним из самых сложных аспектов при программировании на С++. Сложность этой проблемы заключается в том, что память и ресурсы имеют обыкновение утекать… Безвозвратно и бесконтрольно. И если синтаксические и логические ошибки выловить в программе достаточно просто, то на поиск утечек памяти уходит не одна неделя. За все время существования этой проблемы разработчиками было придумано множество путей облегчения себе жизни. Кто-то, избрав путь наименьшего сопротивления, перешел на такие языки, как Java или C#, где за ресурсами следить вроде как не надо – на это существуют сборщики мусора, а динамическая память (в понимании С++) отсутствует как класс. Кто-то вооружившись BoundsChecker’ом анализирует результаты работы программы с целью выявить места, в которых происходит утечка. Кто-то, набравшись терпения, пишет собственные менеджеры памяти и сборщики мусора для С++. А кто-то, взяв на вооружения некоторые идеи языка С++, разрабатывает т. н. ведущие указатели, дабы, оставаясь в рамках языка, исключить саму возможность утечек.

Что есть ведущий указатель (или, иначе говоря Smart Pointer)? Это объект, поведение которого не отличается (ну, или почти не отличается) от поведения обычного указателя, но обладающий при этом рядом достоинств, отсутствующих у обычного указателя. Вообще говоря, по этому поводу написано столько, что писать еще раз тоже самое – большого смыла нет. Достаточно лишь упомянуть вот о чем. Ведущие (или умные) указатели обычно подразделяются на два основных типа – «слабые» и с «подсчетом ссылок». Слабые указатели позволяют без лишних затрат освободить хранящийся в них объект по выходу из блока, в котором такой указатель был объявлен. Тем самым решается проблема «потерянных ссылок», когда разработчик, по невнимательности, забывает освобождать динамическую память, указатель на которую сохраняется на стеке:

void SomeFunc()
{
char* buff = new char[16];
_itoa(125, buff, 10);

}

В этом примере буфер был выделен, но не был удален. Указатели же с подсчетом ссылок позволяют контролировать объект в течение гораздо более длительного срока. Достигается это тем, что такой указатель «считает ссылки» на указываемый объект и удаляет только тогда, когда контролируемый объект уже никому не нужен. Пример: Объявляется класс:

class SomeClass
{
public:
   SomeClass() {m_SomeObject = new int;}

   ~SomeClass() {delete m_SomeObject;}
private:
   int *m_SomeObject;
};
// потом где-то в коде имеет место такая конструкция:

SomeClass obj1;
{
   SomeClass obj2(obj1);
   // Что-то делаем с объектом obj1
}
// Что-то делаем с объектом obj2

Что произойдет? Правильно. После выхода из блока и вызова деструктора для obj2 указатель в obj1 станет недействительным. Избежать этого можно переписав конструктор копий и оператор присваивания. Но есть и более простой путь – воспользоваться указателем с подсчетом ссылок. Такой указатель можно реализовать самому. И наверняка многие так и делают. Но это не что иное, как изобретение велосипеда, название которому – boost::smart_ptr.

Как это использовать

scoped_ptr, shared_ptr, scoped_array, shared_array

Этот раздел библиотеки включает в себя группу классов, реализующих обе вышеописанные разновидности указателей: boost::scoped_ptr и boost::shared_ptr Первый используется для контроля времени жизни указателя в пределах операторного блока. Второй – для длительного контроля за указателем. Существуют разновидности этих классов для указателей на массивы: boost::scoped_array и boost::shared_array соответственно. Разница между этими группами классов очевидна. Первые для удаления контролируемого указателя используют оператор delete, а вторые – оператор delete[]. По этому путать их не рекомендуется во избежании возможных неприятностей с менеджером памяти. В общем случае использовать экземпляры этих классов можно почти также, как и указатели:

boost::shared_ptr<SomeClass> ptr(new SomeClass);
ptr->SomeFunc(); // вызываем метод класса по указателю

std::cout << *ptr; // выводим экземпляр класса в поток.
// а вот и то самое «почти»:
SomeClass* ptr1;
if (ptr == ptr1) // тут будет ошибка компиляции

   // что-то делаем

Дело в том, что ведущим указателям не рекомендуется иметь оператора преобразования к указываемому типу (подробней об этом можно прочитать у Александреску). Для получения явного указателя на ведущий объект используется метод get. Кроме метода get для этих классов определены следующие методы:

Метод Назначение
Общие
reset Уменьшает счетчик ссылок на единицу (если для shared_ptr) и, если необходимо, удаляет объект
swap Обменивает значения двух объектов
scoped_array/shared_array
operator [] Предоставляет доступ по индексу
shared_ptr/shared_array
unique Возвращает true, если счетчик ссылок == 1
use_count Возвращает значения счетчика ссылок
operator bool Возвращает true, если указатель проинициализирован

Подробно о сигнатурах этих методов можно прочитать в документации. Приведенные выше примеры кода с использованием этих классов будут выглядеть так:

void SomeFunc()

{
scoped_array<char> buff = new char[16];
_itoa(125, buff.get(), 10);

}

class SomeClass
{
public:
   SomeClass() {m_SomeObject.reset(new int);}

private:
   boost::shared_ptr<int> m_SomeObject;
};

Применение этих классов достаточно прозрачно, и не должно представлять каких-либо затруднений. Кроме этих четырех классов в джентельменский набор умных указателей входят еще два класса-помошника: boost::weak_ptr и boost::intrusive_ptr

weak_ptr

Первый (boost::weak_ptr) используется в паре с shared_ptr для хранения ссылок на объекты типа boost::shared_ptr. Так же, как и обычные ссылки/указатели на shared_ptr, weak_ptr не увеличивает счетчик ссылок. Но, в отличии от обычных ссылок/указателей, weak_ptr предоставляет дополнительный сервис, заключающийся в том, что если хранимый в нем объект в какой-то момент времени удаляется «извне», то попытка получения его из weak_ptr приводит к исключению. Используется weak_ptr в случае, когда постоянное владение объектом не нужно, но, тем не менее, необходим прозрачный контроль над ним. Например:

std::vector<boost::shared_ptr<SomeClass> > m_Vector;
std::map<std::string, boost::weak_ptr<SomeClass> > m_Map;

Предполагается, что m_Vector хранит и владеет экземплярами класса SomeClass, а m_Map – предоставляет быстрый доступ к этим объектам по некоторой строке. Инициализироваться они будут так:

m_Vector.push_back(boost::shared_ptr<SomeClass>(new SomeClass));
m_Map.insert(std::make_pair(m_Vector.back()->Key, m_Vector.back());

Теперь, если мы удалим какой-либо элемент из вектора, при попытке доступа к нему по ключу приведет к исключению. При этом дополнительных проверок на действительность ссылок делать не надо.

intrusive_ptr

Второй класс (boost::intrusive_ptr) используется при работе с объектами, использующими встроенный счетчик ссылок. Пример – COM-объекты. Этот класс предполагает, что определены функции intrusive_ptr_add_ref и intrusive_ptr_release (не являющиеся фунциями-членами хранимого класса), фактически увеличивающие или уменьшающие счетчик ссылок объекта. Для COM-объектов использование такого указателя могло бы выглядеть так:

int intrusive_ptr_add_ref(IUnknown* p) {return p->AddRef();}
int intrusive_ptr_release(IUnknown* p) {return p->Release();}

после чего можно смело писать: boost::intrusive_ptr<ISomeInterface> ptr(…);

Этот класс имеет интерфейс, аналогичный классу boost::shared_ptr, за исключением того, что его нельзя использовать в связке с boost::weak_ptr. Функциональность описанных классов покрывает большинство задач, которые могут возникнуть при решении проблем с контролем указателей. Если же потребуется что-то специфическое (что мало вероятно) всегда можно изобрести свой собственный велосипед и использовать его. Приведенное описание не претендует на полноту. Были опущены некоторые моменты, связанные с использованием классов, в частности класса weak_ptr. Для получения более полной информации можно заглянуть непосредственно в документацию: http://www.boost.org/libs/smart_ptr/smart_ptr.htm

P.S.: Наглым образом спёрто отсюда: [[doc:cpp:boost:shared_ptr]]

Использование boost::bind

Одним из самых мощных средств стандартной библиотеки языка являются алгоритмы. Их в STL много. А там, где алгоритмы – появляются функторы и их разновидности – предикаты. И все было бы замечательно, если бы не одна большая ложка дегтя – средства стандартной библиотеки предоставляют слишком мало возможностей для описания функуторов. Все просто, если в алгоритм необходимо передать какую-либо глобальную функцию, или простой указатель на метод класса, хранящегося в контейнере.

Но когда речь заходит о чем-то более сложном, как-то:

  • передача заданного значения в предикат;
  • передача указателя на метод класса, не являющегося членом контейнера;
  • предварительная обработка элементов контейнера перед передачей их в функтор;
  • передача в функтор значений элементов данных объектов обрабатываемой последовательности;
  • (список можно продолжить…)

то STL уже не может предложить чего-либо более-менее пригодного к использованию. Сразу же оказывается, что std::binder1st и std::binder2nd накладывают существенные ограничения на принимаемые параметры (кто не сталкивался с «reverence to reference is not allowed» при использовании их в связке с mem_fun_ref?), в стандартные предикаты нельзя передать результат выполнения некоторой операции над элементом контейнера, std::mem_fun и std::mem_fun_ref позволяют работать только с объектами, являющимися членами обрабатываемой последовательности… Вообщем мрак. Проблемы некоторым образом решаются с помощью написания собственных функторов и адаптеров функторов. Но разработка минимально необходимого джентльменского набора – сама по себе нетривиальная задача. Тем более, что она уже имеет весьма элегантное решение, называемое boost::bind. Без преувеличения можно сказать, что по сравнению со связкой boost::bind/boost::mem_fn близкие по функциональности классы из STL кажутся жалкой поделкой. И не без основания. При использовании классов из STL приходится всякий раз задумываться – а какой из вариантов связывателя подойдет к данному конкретному случаю, и подойдет ли вообще. С boost::bind о подобных вещах заморачиваться не надо. К каждому варианту использования компилятор самостоятельно подберет необходимый связыватель. Или выдаст ошибку, если соответствующего варианта подобрать нельзя. В коде использовать этот класс не просто, а очень просто. Рассмотрим несколько типовых вариантов.

Использование с глобальной функцией

Предположим, что есть функция:

bool compare(int a, int b) {return a < b;}

для того, чтобы использовать эту функцию в, например, алгоритме sort нам необходимо написать:

std::vector<int> vec(…);
std::sort(vec.begin(), vec.end(), compare);

Теперь немного усложним задачу – в зависимости от флага нам необходимо сортировать либо по возрастанию, либо по убыванию. При использовании только STL пришлось бы писать две функции. Накладно. С помощью boost эту задачу решить значительно проще:

bool compare(int a, int b, bool reverseSort) {return reverseSort ? a > b : a < b;}

 
std::vector<int> vec(…);
std::sort(vec.begin(), vec.end(), boost::bind(compare, _1, _2, order));

где order – булевская переменная, задающая порядок сортировки. Последняя строчка выглядит несколько странновато, но именно в ней и заключается вся суть. В результате ее компиляции будет получен код, реализующий сортировку массива, использующий для сравнения функцию compare, и передающей ей на вход три аргумента – два числа, которые необходимо сравнить, и способ их сравнения. Почему именно так? Рассмотрим этот код подробнее, записав его так:

int a = 1, b = 2;
boost::bind(compare, _1, _2, false)(a, b);

(что-то подобное содержится в недрах функции std::sort). Последняя строчка в этом коде обозначает следующее:

boost::bind – функция, создающая необходимый нам связыватель. Возвращает экземпляр функционального объекта (функтора), для которого применим оператор вызова функции. (compare, - первый аргумент функции определяет имя/указатель на функцию, которая должна быть вызвана в связывателе. В данном случае это функция compare; _1, _2 – плейсхолдеры, определяющие логику передачи параметров из оператора вызова функции в функцию compare. , false) – значение последнего аргумента, передаваемого в compare. (a, b) – оператор вызова функции, производящий связывание аргументов и вызов функции compare. В итоге будет выполнена следующий код:

compare(a, b, false);

Из приведенного примера можно увидеть, что упомянутые выше плейсхолдеры определяют связь между аргументами у оператора вызова функции связывателя и аргументами вызываемой функцией. Причем, количество аргументов у оператора вызова функции связывателя определяется количеством плейсхолдеров:

boost::bind(compare, _1, 4, false)(a, b); // неправильно. 

boost::bind(compare, _1, 4, false)(a); // правильно
boost::bind(compare, _1, _2, false)(a); // неправильно

boost::bind(compare, _1, _2, false)(a, b); // правильно

Очевидно, что попытка задать в вызове boost::bind количество параметров, несоответствующее количеству обязательных параметров в вызываемой функции приведет к ошибке. Равно как и попытка указания количества параметров большего, чем количество параметров в вызываемой функции. Действуя по аналогии мы можем записать:

std::find_if(vec.begin(), vec.end(), boost::bind(compare, _1, 4, false)); // поиск первого числа, меньшего 4

std::remove_if(vec.begin(), vec.end(), boost::bind(compare, _1, 4, true)); // удаление из последовательности всех элементов, больших 4

// и т. д.

В случае использования чистого STL для каждого случая пришлось бы искать подходящий функтор… Или писать его. Здесь необходимо обратить внимание на то, что в качестве первого параметра функции bind может выступать не только указатель на функцию, но и другой функциональный объект. Например:

std::find_if(vec.begin(), vec.end(), boost::bind(std::equal_to<int>(), _1, 4));

этот строчка найдет в массиве первый элемент, равный 4.

Использование с указателями на функции члены

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

class GraphicsObject

{
public:
   void Draw(Canvas* canvas, DrawModes mode);
};
// Объявляем коллекцию объектов этого класса:
std::list<GraphicsObject*> GraphObjects;

// А теперь нам нужно отрисовать все объекты коллекции на заданном канвасе
std::for_each(GraphObjects.begin(), GraphObjects.end(); ……..);

при использовании чистого STL нам бы пришлось писать свой собственный функтор, вызывающий у переданного объекта метод Draw с заданными параметрами. Но можно воспользоваться boost::bind:

std::for_each(GraphObjects.begin(), GraphObjects.end(), 
    boost::bind(&GraphObject::Draw, _1, canvas, mode));

в результате чего мы получаем требуемый результат. У всех объектов коллекции вызывается метод Draw, которому передаются параметры canvas и mode. При использовании указателя на функцию-член класса необходимо помнить о том, что первым параметром ей должен передаваться указатель на объект, для которого эта функция должна вызываться. В приведенном примере мы указываем, что метод должен вызываться у переданного в bind единственного параметра. Но никто не мешает нам в качестве первого передаваемого параметра указать this, или любой другой указатель. Но, как говориться в современных набивших оскомину рекламах, это еще не все.

Использование с указателем на член данных

Помимо указателей на члены-функции можно использовать указатели на члены данных. Синтаксис подобный и правила использования при этом почти такие же. Возьмем задачу. Есть структура и вектор ее экземпляров:

struct Point
{

    int x;
    int y;
};
 
std::vector<Point> PointsArray;

Теперь надо удалить из этого массива все элементы, у которых координата x равна нулю. Сделать это просто:

std::remove_if(PointsArray.begin(), PointsArray.end(), 
    boost::bind(std::equal_to<int>(), boost::bind(&Point::x, _1), 0));

Т. е. для каждого элемента массива вызывается вложенный связыватель, который возвращает значение соответствующего элемента структуры, после чего производится его сравнение с нулем. Тут мы видим еще одну возможность связывателей:

Каскадное использование связывателей

Связыватели могут вкладываться друг в друга. Один из вариантов такого вкладывания проиллюстрирован предыдущем примером. Единственное, что в этом случае надо «держать в голове» - это то, что плейсхолдеры, вне зависимости от того, в каком bind'ере (по уровню вложенности) они находятся, «адресуют» параметры самого внешго binder'а. Усложним предыдущий пример. Нужно выбросить все точки, имеющие нулевые координаты:

std::remove_if( PointsArray.begin(), PointsArray.end(), boost::bind(

    std::logical_and(), 
        boost::bind(std::equal_to<int>(), boost::bind(&Point::x, _1), 0),
        boost::bind(std::equal_to<int>(), boost::bind(&Point::y, _1), 0)

    )
));

Это хотя и работает так, как хочется, но выглядит слишком наворочено. По этому начиная с версии 1.33 в boost::bind появилась новая возможность -

Перегруженные операторы

Для упрощения приведенных выше многоэтажных конструкций для boost::bind (начиная с версии 1.33 boost'а) перегружены следующие операторы: !, ==, !=, <, ?, > и >=. Таким образом, приведенное выше выражение упрощается до:

std::remove_if( PointsArray.begin(), PointsArray.end(), boost::bind(

    boost::bind(&Point::x, _1) == 0 && boost::bind(&Point::y, _1) == 0)

    ));

Использование ссылок

Одна из неприятностей заключается в том, что если связываемая функция принимает какой-то из своих аргументов по ссылке, то с использованием boost::bind его (напрямую) передать нельзя. Т. е. например, в следующем случае:

void foo(int& a, int& b);

 
int a = 1, b = 2;
boost::bind(foo, a, b);

аргументы a и b по ссылке переданы не будут. Для того, чтобы действительно передать ссылки, необходимо делать такой вызов:

boost::bind(foo, boost::ref(a), boost::ref(b));

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

Полную документацию и примеры использования можно найти в оригинальной документации: http://www.boost.org/libs/bind/bind.html

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

#include <vector>
#include <boost/bind.hpp>
#include <boost/function.hpp>

class Test
{
public:
    void f_1()
    {
        std::cout << "void f()" << std::endl;
    }

    void f_2(int i)
    {
        std::cout << "void f_2(): " << i << std::endl;
    }

    void f_3(const int &i)
    {
        std::cout << "void f_2(): " << i << std::endl;
    }

    void two_params(int a, int b)
    {
        std::cout << "void two_params(int a, int b): " << a << ", " << b << std::endl;
    }
};

class Test2
{
public:
    void do_stuff(const std::vector<int>& v) 
    {
        std::copy(v.begin(), v.end(),  std::ostream_iterator<int>(std::cout, " "));
    }
};

void test(const std::string &a)
{
    std::cout << "void test(const std::string &a). a = " << a << std::endl;
}

void prn ( boost::function<void(int)> fn, int a )
{
    fn ( a );
}

int _tmain(int argc, _TCHAR* argv[])
{
    ////////////////////// bind //////////////////////
    std::cout << "boost::bind test" << std::endl;
    Test a;
    boost::bind(&Test::f_1, &a)();

    boost::bind(&Test::f_2, &a, 1)();
    int test_int(2);
    boost::bind(&Test::f_2, &a, _1)(test_int);

    boost::bind(&Test::f_3, &a, 3)();
    int test_int_2(4);
    boost::bind(&Test::f_3, &a, _1)(test_int_2);

    int one(100), two(200);
    boost::bind(&Test::two_params, &a, _1, _2)(one, two);
    boost::bind(&Test::two_params, &a, _2, _1)(one, two);

    std::string test_str("Hi there.");
    boost::bind(&test, test_str)();
    boost::bind(&test, _1)(test_str);
    std::cout << std::endl;
    
    ////////////////////// function //////////////////////
    std::cout << "boost::function test" << std::endl;
    
    boost::function<void (void)> func;
    func = boost::bind(&Test::f_1, &a);
    func();
    func = boost::bind(&Test::f_2, &a, 201);
    func();
    func = boost::bind(&Test::f_3, &a, 202);
    func();
    func = boost::bind(&Test::two_params, &a, 203, 204);
    func();

    boost::function<void (int)> func_2;
    func_2 = boost::bind(&Test::f_2, &a, _1);
    prn(func_2, 301);
    func_2 = boost::bind(&Test::f_3, &a, _1);
    prn(func_2, 302);

    int i_303(303), i_304(304), i_305(305), i_306(306);
    func_2 = boost::bind(&Test::two_params, &a, i_303, _1);
    prn(func_2, 1);
    func_2 = boost::bind(&Test::two_params, &a, _1, i_304);
    prn(func_2, 1);

    Test2 t;
    std::vector<int> vec;
    vec.resize(20);
    std::generate_n(vec.begin(), 20, rand);
    std::copy(vec.begin(), vec.end(),  std::ostream_iterator<int>(std::cout, " "));
    //simple_bind(&Test::do_stuff, t, _1)(vec);
    //boost::bind(&Test2::do_stuff, t, _1)(vec);

    return 0;
}

P.S.: Наглым образом спёрто здесь: [[doc:cpp:boost:bind]]

Использование boost::function

Во многих современных языках существует понятие «делегата» - некоторой сущности, позволяющей косвенно вызвать тот или иной метод. Наиболее близкая аналогия в С++ - это указатель на функцию или член класса. Но каждый разработчик на С++, кто сталкивался с такого рода указателями, в итоге выясняет, что они - не полноценный аналог делегата. По двум причинам:

Указатель на функцию и указатель на член класса не могут быть преобразованы друг в друга.
Для доступа к члену класса по указателю обязательно требуется указатель на объект, относительно которого производится вызов.

А по этому, если указатели на свободные функции или статические функции еще можно как-то рассматривать в качестве делегатов, то с указателями на члены класса все гозаздо сложнее. Для устранения этих проблем и был создан шаблонный класс boost::function (а точнее - набор шаблонных классов).

Что это такое? По сути - boost::function (точнее, результат его инстанцирования) - это функтор (функциональный объект), к которому применим оператор »()» (вызова функции). А потому в исходный тексте программы вызов метода напрямую или через boost::function ничем не отличимы.

В качестве аргументов шаблона boost::function принимает сигнатуру метода, который он будет «эмулировать». Например:

boost::function<void ()> foo1;
// (соответствует методу с сигнатурой void foo1();)

boost::function<int (int, float, SomeClass&)> foo2;
// соответствует методу
// int foo2(int arg1, float arg2, SomeClass& arg3)

// и т. д.

А инициализироваться может либо указателем на соответствующий метод, либо другим функциональным объектом, который может быть вызван с указанными параметрами:

void foo1(int a) {;}

struct Foo1
{
   void operator() (int a) {;}
}

boost::function<void (int)> Delegate;

//...
Delegate = foo1;
// ...
Delegate(10); // будет реально вызван foo1(10)
// ...
// ...
Delegate = Foo1();
// ...
Delegate(10); // будет вызван Foo1::operator()(10);

Как это использовать

boost::function можно использовать:

Со свободными (глобальными) функциями.
С функциями-членами.
С функциональными объектами.

Синтаксис объявления экземпляра boost::function не меняется. Аргументы, которые может принимать boost::function такие же, какие может принимать обычная функция. При этом (как и для обычной функции) можно перечислять как просто типы аргументов, так и типы с формальными именами параметров:

// оба объявления равноценны
boost::function<void (int, float[], SomeClass&)> f1;
boost::function<void (int a, float arr[], SomeClass& sc)> f2;

Максимальное количество аргументов, которое может принимать boost::function зависит от реализации. Чаще всего - не больше 10.

После объявления экземпляра boost::function его необходимо обязательно заполнить (проинициализировать). До этого момента экземпляр будет считаться «пустым» и при попытки применить к нему оператор вызова функции будет выброшено исключение bad_function_call.

Использование вместе со свободными функциями

Для использования boost::function совместно со свободными функциями достаточно проинициализировать объект адресом соответствующей функции:

void foo(int, int) {;}

boost::function<void (int, int)> f1;
f1 = foo;
// или
f1 = &foo;

Использование вместе с функциями-членами

Используется также, как и со свободными функциями:

class SomeClass
{
public:
 void foo(int, int) {;}
}

boost::function<void (int, int)> f1;
f1 = &SomeClass::foo;

Но использование такого функционального объекта требует передачи ему экземпляра класса, для которого должна быть вызвана функция:

SomeClass obj;
f1(&obj, 10);

В случае необходимости передачи «чистого» функционального объекта ситуацию спасает использование boost::bind, или std::bind1st:

SomeClass* obj = ...;
f1 = boost::bind(&SomeClass::foo, obj, _1);
//...
f1(10); // эквивалентен вызову f1 из предыдущего примера

Статические функции члены передаются в boost::function также, как и свободные функции.

Использование с функциональными объектами

Если для какого-то объекта можно применить оператор вызова функции, то он также, как и обычные функции, может быть использован для инициализации boost::function. Пример был приведен в предыдущем разделе, когда boost::function инициализировался функциональным объектом boost::bind.

Поскольку функциональный объект в boost::function хранится по значению, то в случае необходимости передачи ссылки необходимо воспользоваться boost::ref:

SomeFunctionObject foo;
boost::function<void (int)> f1;
f1 = boost::ref(foo);
boost::function<void (int)> f2;
f2(f1);

В этом случае f1 и f2 будут использовать для работы один и тот же функциональный объект.

P.S.: Наглым образом спёрто отсюда: [[doc:cpp:boost:function]]

Использование boost:array

Данный класс представляет собой STL-совместимый контейнер для массивов с постоянным размером.

Разработан Николаи Джоссатисом (автором таких книг как C++ Standard Library и C++ Templates)

Описание.


Конструктор.

template<typename U>
array& operator=(const array<U, N>& other);

Создаёт экземпляр объекта array


Итераторы.

iterator begin();
const_iterator begin() const;

Представляют соответственно неконстантный и константный итераторы для первого элемента array.

iterator end();
const_iterator end() const;

Представляют соответственно неконстантный и константный итераторы для конечного элемента array.


Обратные итераторы.

iterator rbegin();
const_iterator rbegin() const;

Представляют соответственно обратные неконстантный и константный итераторы для первого элемента array.

iterator rend();
const_iterator rend() const;

Представляют соответственно обратные неконстантный и константный итераторы для конечного элемента array.


Ёмкость.

array size_type size();

Возвращает число элементов array

bool empty();

Если array пустой возвращает true.

size_type max_size();

Возвращает максимальное число элементов.


Доступ к элементам array.

reference operator[](size_type i);
const_reference operator[](size_type i) const;

Возвращает элемент с индексом i.

В случает доступа к элементу с несуществующим индексом исключения не вызывается.

reference at(size_type i);
const_reference at(size_type i) const;

Возвращает элемент с индексом i.

В случает доступа к элементу с несуществующим индексом вызывается исключение std::range_error.

reference front();
const_reference front() const;

Возвращает первый элемент.

reference back();
const_reference back() const;

Возвращает последний элемент.

T* c_array();
const T* data() const;

Возвращает указатель на массив элементов типа T.


Модификаторы array.

void swap(array<T, N>& other);

Обмен элементами между двумя объектами типа array.

void assign(const T& value);

Заполнить array элементами из другого объектаСпециализированные алгоритмы

template<typename T, std::size_t N>
void swap(array<T, N>& x, array<T, N>& y);

Обмен элементами между двумя объектами типа array.


Операторы сравнения для array.

template<typename T, std::size_t N>
bool operator==(const array<T, N>& x, const array<T, N>& y);
template<typename T, std::size_t N>
bool operator!=(const array<T, N>& x, const array<T, N>& y);
template<typename T, std::size_t N>
bool operator<(const array<T, N>& x, const array<T, N>& y);
template<typename T, std::size_t N>
bool operator>(const array<T, N>& x, const array<T, N>& y);
template<typename T, std::size_t N>
bool operator<=(const array<T, N>& x, const array<T, N>& y);
template<typename T, std::size_t N>
bool operator>=(const array<T, N>& x, const array<T, N>& y);

P.S.: Нагло спёрто отсюда: forum.vingrad.ru -> Boost:Array

Использование характеристик типов boost/type_traits.hpp


Содержание


Вступление

Содержимое (классы) заголовочного файла <boost/type_traits.hpp> объявляется в пространстве имен boost.

В файле <boost/type_traits.hpp> определены три вида свойств типов:

  • свойства выбранного типа
  • отношения между двумя типами
  • преобразования от одного типа к другому

Если Вы ранее не работали с этой библиотекой, то предлагаем ознакомиться со статьей, где описываются основы и цели создания библиотеки.

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

Первичная категоризация типов

Нижеследующие шаблоны свойств типов позволяют определить, к какой категории типов принадлежит заданный тип. Для любого заданного типа только одно выражение получит значение true. Заметьте, что is_integral<T>::value и is_float<T>::value будут true только для встроенных типов; если есть необходимость проверки для пользовательских типов так же, как для встроенных, то следует использовать шаблон std::numeric_limits.

  • ::boost::is_void<T>::value
    становится true только если T является cv-qualified void типом.
  • ::boost::is_float<T>::value
    становится true только если T является cv-qualified типом с плавающей точкой
  • ::boost::is_integral<T>::value
    становится true только если T является cv-qualified целым типом.
  • ::boost::is_float<T>::value
    становится true только если T является cv-qualified типом с плавающей точкой
  • ::boost::is_pointer<T>::value
    становится true только если T является cv-qualified указателем (включая указатели на функции, но исключая указатели на члены классов).
  • ::boost::is_reference<T>::value
    становится true только если T является ссылкой.
    если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон может давать неправильные результаты.
  • ::boost::is_member_pointer<T>::value
    становится true только если T является a cv-qualified указателем на член класса (поле класса или метод).
  • ::boost::is_array<T>::value
    становится true только если T является массивом.
    если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон может давать неправильные результаты.
  • ::boost::is_union<T>::value
    становится true только если T является типом union. В настоящее время требуется некоторая поддержка со стороны компилятора, иначе union'ы рассматриваются как классы.
    Без определенной помощи со стороны компилятора (в настоящее время данная возможность не стандартизована), мы не можем отличить union от класса, в результате чего выражение никогда не получается true.
  • ::boost::is_class<T>::value
    становится true только если T является классом или структурой.
    Без определенной помощи со стороны компилятора (в настоящее время данная возможность не стандартизована), мы не можем отличать union от классов, в результате чего выражение дает неправильный ответ true для аргумента типа union.
  • ::boost::is_enum<T>::value
    становится true только если T является перечислением (enum).
    требуется правильно работающий шаблон is_convertible (это означает, что шаблон is_enum в настоящее время не работает в Borland C++ Builder 5, и в некоторых версиях компилятора Metrowerks).
  • ::boost::is_function<T>::value
    становится true только если T является функциональным типом (но не ссылкой и не указателем на функцию).
    если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон не компилируется для ссылочных типов.

Свойства типов

Нижеописанные шаблоны идентифицируют свойства, которые имеет заданный тип.

  • ::boost::alignment_of<T>::value
    идентифицирует величину выравнивания для T. в действительности возвращает величину, которая гарантированно является множителем действительной величины выравнивания. T должен быть полностью определен (complete type)
  • ::boost::is_empty<T>::value
    true если T является пустой структурой или классом. Если компилятор поддерживает метод оптимизации "пустые базовые классы нулевого размера", тогда шаблон is_empty может правильно определить, что T пустой. Используется шаблон is_class для определения, когда T является классом.
    T должен быть полностью определен (complete type)
    Компилятор должен поддерживать реализацию пустых базовых классов нулевого размера, чтобы обнаружение пустых классов работало.
    Не может быть использован с неопределенными полностью типами.
    Не может работать с union'ами до тех пор, пока шаблон is_union сможет работать (см. его описание).
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон не может быть использован с абстрактными типами.
  • ::boost::is_const<T>::value
    true только если  T квалифицирован как const (top-level const-qualified).
  • ::boost::is_volatile<T>::value
    true только если  T квалифицирован как volatile.
  • ::boost::is_polymorphic<T>::value
    true только если  T является полиморфным типом.
    T должен быть полностью определен (complete type)
    Требуется знание ABI компилятора, в действительности шаблон работоспособен на большинстве существующих компиляторов.
  • ::boost::is_pod<T>::value
    true только если  T является cv-qualified POD, то есть простой структурой без методов, с возможными квалификаторами const и volatile.
    T должен быть полностью определен (complete type).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон is_pod не может определить, что T является POD; это безопасно, хотя может быть не оптимальным.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, то данный шаблон не может быть использован с функциональными типами.
  • ::boost::has_trivial_constructor<T>::value
    true если T имеет простой деструктор, сгенерированный компилятором (trivial default constructor).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон has_trivial_constructor не сможет определить, что заданный тип имеет пустой конструктор. Это всегда безопасно, хотя может быть неоптимальным.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, то данный шаблон не может быть использован с функциональными типами.
  • ::boost::has_trivial_copy<T>::value
    true если T имеет простой сгенерированный компилятором конструктор копирования ( trivial copy constructor).
    T должен быть полностью определен (complete type).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон has_trivial_copy  не сможет определить, что заданный тип имеет пустой конструктор копирования. Это всегда безопасно, хотя может быть неоптимальным.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, то данный шаблон не может быть использован с функциональными типами.
  • ::boost::has_trivial_assign<T>::value
    true если T имеет простой сгенерированный компилятором оператор присваивания (trivial assignment operator).
    T должен быть полностью определен (complete type).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон has_trivial_assign  не сможет определить, что заданный тип имеет пустой оператор присваивания. Это всегда безопасно, хотя может быть неоптимальным.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, то данный шаблон не может быть использован с функциональными типами.
  • ::boost::has_trivial_destructor<T>::value
    true если T имеет простой, сгенерированный деструктор (trivial destructor).
    T должен быть полностью определен (complete type).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон  has_trivial_destructor не сможет определить, что заданный тип имеет пустой оператор присваивания. Это всегда безопасно, хотя может быть неоптимальным.
    Если компилятор не поддерживает частичную специализацию шаблонов классовв, то данный шаблон не может быть использован с функциональными типами.
  • ::boost::is_stateless<T>::value
    true если T не имеет состояния, то есть T не выделяет память и его конструкторы и деструктор сгенерированы компилятором.
    T должен быть полностью определен (complete type).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон is_stateless не сможет определить, что заданный тип не имеет состояния. Это всегда безопасно, хотя может быть неоптимальным.
    Выражение становится true только если становятся true все нижеуказанные:
    ::boost::has_trivial_constructor<T>::value,
    ::boost::has_trivial_copy<T>::value,
    ::boost::has_trivial_destructor<T>::value,
    ::boost::is_class<T>::value,
    ::boost::is_empty<T>::value
    Если компилятор не поддерживает частичную специализацию шаблонов классов, то данный шаблон не может быть использован с функциональными типами.
  • ::boost::has_nothrow_constructor<T>::value
    true если T имеет конструктор по умолчанию, не генерирующий исключения.
    T должен быть полностью определен (complete type).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон  has_nothrow_constructor не сможет определить, что заданный тип имеет конструктор, не генерирующий исключения. Это всегда безопасно, хотя может быть неоптимальным.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, то данный шаблон не может быть использован с функциональными типами.
  • ::boost::has_nothrow_copy<T>::value
    true если T имеет конструктор копирования, не генерирущий исключений.
    T должен быть полностью определен (complete type).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон has_nothrow_copy не сможет определить, что заданный тип имеет конструктор копирования, не генерирующий исключения. Это всегда безопасно, хотя может быть неоптимальным.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, то данный шаблон не может быть использован с функциональными типами.
  • ::boost::has_nothrow_assign<T>::value
    true если T имеет оператор присваивания, не генерирующий исключений.
    T должен быть полностью определен (complete type).
    Без некоторой помощи (в настоящее время эти возможности не стандартизованы) со стороны компилятора, шаблон  has_nothrow_assign не сможет определить, что заданный тип имеет оператор присваивания, не генерирующий исключения. Это всегда безопасно, хотя может быть неоптимальным.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, то данный шаблон не может быть использован с функциональными типами.

Отношения между типами

Следующая таблица содержит шаблоны, с помощью которых можно выяснить, есть ли какая-либо зависимость между двумя типами:

  • ::boost::is_same<T,U>::value
    становится true, если T и U - один и тот же тип.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон не может быть использован с  абстрактными, неполностью определенными или функциональными типами.
  • ::boost::is_convertible<T,U>::value
    становится true, если воображаемый объект типа  T может быт преобразован в тип U.
    Тип T не должен быть неполностью определенным типом.
    Тип U не должен быть неполностью определенным, либо абстрактным типом, либо функциональным типом.
    Подразумевается, что никакой тип не может быть преобразован в массив.
    Обратите внимание, что данный шаблон работает неправильно с Borland C++ Builder 5 (и более ранними версиями) для преобразований через конструкторы, для с компилятором Metrowerks 7 (и более ранними версиями) во всех случаях.
  • ::boost::is_base_and_derived<T,U>::value
    становится true, если T является базовым классом для типа U.
    Будут обнаружены не-публичные базовые классы, и неоднозначные базовые классы.
    Обратите внимание, что любой класс не рассматривается как собственный базовый класс. Аналогично, если T или U не являются классами, тогда результат будет в любом случае false.
    Типы T и U не должны быть неполностью описанными.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон не может быть использован с функциональными типами.

Обратите внимание, что как is_convertible, так и is_base_and_derived могут привести к ошибке компиляции, если преобразование неоднозначно.:

struct A {};
struct B : A {};
struct C : A {};
struct D : B, C {};
bool const x = boost::is_base_and_derived<A,D>::value;  // ошибка
bool const y = boost::is_convertible<D*,A*>::value;     // ошибка

Преобразования между типами

Следующие шаблоны преобразовывают один тип в другой, основываясь на простом правиле: каждый шаблон имеет единственный член type, который является результатом применения преобразования к аргументу шаблона T:

  • ::boost::remove_const<T>::type
    Создает тип такой же как T, но убирает квалификатор const самого верхнего уровня. К примеру, "const int" становится"int", но "const int*" остается неизменным.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон компилируется, но не работает, за исключением случаев, указанных в примечании ниже.
  • ::boost::remove_volatile<T>::type
    Создает тип такой же как T, но убирает квалификатор volatile самого верхнего уровня. К примеру, "volatile int" становится "int".
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон компилируется, но не работает, за исключением случаев, указанных в примечании ниже.
  • ::boost::remove_cv<T>::type
    Создает тип такой же как T, но убирает квалификаторы const и volatile самого верхнего уровня. К примеру "const volatile int" становится "int".
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон компилируется, но не работает, за исключением случаев, указанных в примечании ниже.
  • ::boost::remove_reference<T>::type
    Если T является ссылкой, тогда удаляет ссылку, иначе тип T остается неизменным. К примеру, "int&" становится "int", но "int*" не изменится.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон компилируется, но не работает, за исключением случаев, указанных в примечании ниже.
  • ::boost::remove_bounds<T>::type
    Если T является типом массива, тогда удаляет квалификатор верхнего уровня массива, в противном случае T не изменяется. К примеру,  "int[2][3]" становится "int[3]".
    если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон компилируется, но не работает.
  • ::boost::remove_pointer<T>::type
    Если T является указателем, тогда удаляет указатель верхнего уровня (top-level indirection) у T, иначе T остается неизменным. К примеру, "int*" становится "int", но "int&" не меняется.
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон компилируется, но не работает, за исключением случаев, указанных в примечании ниже.
  • ::boost::add_reference::type
    Если T является ссылкой, тогда тип T не меняется, иначе преобразует T в ссылку. К примеру, "int&" не меняется, а  "double" становится "double&".
  • ::boost::add_pointer<T>::type
    Результатом работы является тип, который получается как  remove_reference<T>::type*. К примеру, "int" и "int&" становятся "int*".
    Если компилятор не поддерживает частичную специализацию шаблонов классов, тогда этот шаблон на компилируется с ссылочными типами.
  • ::boost::add_const<T>::type
    То же, что "T const" для всех T.
  • ::boost::add_volatile<T>::type
    То же, что "T volatile" для всех T.
  • ::boost::add_cv<T>::type
    То же, что "T const volatile" для всех T.

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

  1. Вручную создать полные специализации шаблонов для всех типов преобразований для всех фундаментальных типов, а также всех их указателей (* и **) с/без квалификаторами const и volatile
  2. Создать пользовательские макросы, которые будут определять такие явные специализации для любого пользовательского типа  T.

Первый пункт гарантирует успешную компиляцию такого фрагмента:

BOOST_STATIC_ASSERT((is_same<char, remove_reference<char&>::type>::value));
BOOST_STATIC_ASSERT((is_same<char const, remove_reference<char const&>::type>::value));
BOOST_STATIC_ASSERT((is_same<char volatile, remove_reference<char volatile&>::type>::value));
BOOST_STATIC_ASSERT((is_same<char const volatile, remove_reference<char const volatile&>::type>::value));
BOOST_STATIC_ASSERT((is_same<char*, remove_reference<char*&>::type>::value));
BOOST_STATIC_ASSERT((is_same<char const*, remove_reference<char const*&>::type>::value));
....
BOOST_STATIC_ASSERT((is_same<char const volatile* const volatile* const volatile, remove_reference<char const volatile* const volatile* const volatile&>::type>::value));

и второй пункт обеспечивает для пользователей библиотеки механизм, с помощью которого вышеприведенный код будет работать не только для типов 'char', 'int' или других встроенных типов, но для любых пользовательских типов:

struct my {};
BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION(my)
BOOST_STATIC_ASSERT((is_same<my, remove_reference<my&>::type>::value));
BOOST_STATIC_ASSERT((is_same<my, remove_const<my const>::type>::value));
// etc.

Заметьте, что макрос BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION становится пустым для компиляторов, которые не поддерживают частичную специализацию.

Синтезирование типов

Следующие шаблоны синтезируют типы с желаемыми свойствами.

  • ::boost::type_with_alignment<Align>::type
  • пытается найти встроенный тип или POD, который имеет значение выравнивания такое же, как множитель заданного параметр а Align.

Свойства функций

Шаблон класса ::boost::function_traits извлекает информацию о функциональном типе.

  • ::boost::function_traits<F>::arity
    определяет арность функционального типа F.
    Без поддержки частичной специализации данный шаблон не компилируется для ссылочных типов.
  • ::boost::function_traits<F>::result_type
    тип, возвращаемый функциональным типом  F.
    Не компилируется без поддержки частичной специализации шаблонов классов.
  • ::boost::function_traits<F>::argN_type
    тип Nго аргумента функции F, где 1<=N<=арность(F).
    Не компилируется без поддержки частичной специализации шаблонов классов.

Заголовочные файлы свойств типов

Библиотека свойств типов нормально подключается заголовочным файлом:

#include <boost/type_traits.hpp>

Однако в действительности библиотека разбита на ряд небольших файлов, так что иногда может быть удобнее включать один из этих файлов напрямую, чтобы получить только те шаблоны, которые нужны. Отдельные хидеры всегда имеют то же имя, что и соответствующий шаблон, и хранятся в каталоге boost/type_traits/.  Так что если, к примеру, некоторый код нуждается в шаблоне  is_class<>, тогда просто включайте:

<boost/type_traits/is_class.hpp>

Пользовательские специализации

В ряде случаев конечный пользователь может нуждаться в собственных специализациях для какого-либо из шаблонов свойств типов - обычно когда используемый компилятор требует, чтобы соответствующее свойство типа было реализовано полностью. Эти специализации должны наследовать от boost::mpl::true_ или boost::mpl::false_ соответственно:

#  include <boost/type_traits/is_pod.hpp>
#  include <boost/type_traits/is_class.hpp>
#  include <boost/type_traits/is_union.hpp>

struct my_pod{};
struct my_union
{
char c;
int i;
};

namespace boost
{
template<>
struct is_pod<my_pod> 
: public mpl::true_{};
template<>
struct is_pod<my_union> 
: public mpl::true_{};
template<>
struct is_union<my_union> 
: public mpl::true_{};
template<>
struct is_class<my_union> 
: public mpl::false_{};
}

Примеры

Есть четыре примера работы с шаблонами свойств типов:

Copy_example.cpp

Демонстрируется версия std::copy, которая использует функцию memcpy, когда это возможно, для выполнения копирования (то есть если тип - один из фундаментальных, либо POD, и т.д.);

//
// opt::copy
// same semantics as std::copy
// calls memcpy where appropiate.
//

namespace detail{

template<typename I1, typename I2>
I2 copy_imp(I1 first, I1 last, I2 out)
{
while(first != last)
{
*out = *first;
++out;
++first;
}
return out;
}

template <bool b>
struct copier
{
template<typename I1, typename I2>
static I2 do_copy(I1 first, I1 last, I2 out)
{ return copy_imp(first, last, out); }
};

template <>
struct copier<true>
{
template<typename I1, typename I2>
static I2* do_copy(I1* first, I1* last, I2* out)
{
memcpy(out, first, (last-first)*sizeof(I2));
return out+(last-first);
}
};


}

template<typename I1, typename I2>
inline I2 copy(I1 first, I1 last, I2 out)
{
typedef typename boost::remove_cv<typename std::iterator_traits<I1>::value_type>::type v1_t;
typedef typename boost::remove_cv<typename std::iterator_traits<I2>::value_type>::type v2_t;
return detail::copier<
::boost::type_traits::ice_and<
::boost::is_same<v1_t, v2_t>::value,
::boost::is_pointer<I1>::value,
::boost::is_pointer<I2>::value,
::boost::has_trivial_assign<v1_t>::value
>::value>::do_copy(first, last, out);
}

fill_example.cpp

Демонстрируется версия std::fill, которая использует функцию memset, когда это возможно, для оптимизации операции заполнения. Также используется шаблон call_traits для оптимизации передачи параметров, чтобы избежать проблем с синонимами (aliasing issues):

namespace opt{
///
// fill
// same as std::fill, uses memset where appropriate, along with call_traits
// to "optimise" parameter passing.
//
namespace detail{

template <typename I, typename T>
void do_fill_(I first, I last, typename boost::call_traits<T>::param_type val)
{
while(first != last)
{
*first = val;
++first;
}
}

template <bool opt>
struct filler
{
template <typename I, typename T>
struct rebind
{
static void do_fill(I first, I last, typename boost::call_traits<T>::param_type val)
{ do_fill_<I,T>(first, last, val); }
};
};

template <>
struct filler<true>
{
template <typename I, typename T>
struct rebind
{
static void do_fill(I first, I last, T val)
{
std::memset(first, val, last-first);
}
};
};

}

template <class I, class T>
inline void fill(I first, I last, const T& val)
{
typedef detail::filler<
::boost::type_traits::ice_and<
::boost::is_pointer<I>::value,
::boost::is_arithmetic<T>::value,
(sizeof(T) == 1)
>::value> filler_t;
typedef typename filler_t:: template rebind<I,T> binder;
binder::do_fill(first, last, val);
}
}};   // namespace opt

iter_swap_example.cpp

Демонстрируется версия алгоритма std::iter_swap, который работает с прокси-итераторами (proxying iterators), а также с обычными итераторами; вызывает  std::swap для обычных итераторов, иначе выполняет медленный, но надежный обмен:

namespace opt{
///
// iter_swap:
// tests whether iterator is a proxying iterator or not, and
// uses optimal form accordingly:
//
namespace detail{

template <bool b>
struct swapper
{
template <typename I>
static void do_swap(I one, I two)
{
typedef typename std::iterator_traits<I>::value_type v_t;
v_t v = *one;
*one = *two;
*two = v;
}
};

template <>
struct swapper<true>
{
template <typename I>
static void do_swap(I one, I two)
{
using std::swap;
swap(*one, *two);
}
};

}

template <typename I1, typename I2>
inline void iter_swap(I1 one, I2 two)
{
typedef typename std::iterator_traits<I1>::reference r1_t;
typedef typename std::iterator_traits<I2>::reference r2_t;
detail::swapper<
::boost::type_traits::ice_and<
::boost::is_reference<r1_t>::value, 
::boost::is_reference<r2_t>::value,
::boost::is_same<r1_t, r2_t>::value
>::value>::do_swap(one, two);
}

};   // namespace opt

Trivial_destructor_example.cpp

Этот алгоритм выполняет обратную по отношению к std::unitialized_copy работу; он получает указатель на блок инициализированной памяти и вызывает деструкторы для каждого из объектов в нем. Это может быть использовано при реализации контейнерных классов, которые самостоятельно управляют своей памятью:

namespace opt{
//
// algorithm destroy_array:
// The reverse of std::unitialized_copy, takes a block of
// initialized memory and calls destructors on all objects therein.
//

namespace detail{

template <bool>
struct array_destroyer
{
template <class T>
static void destroy_array(T* i, T* j){ do_destroy_array(i, j); }
};

template <>
struct array_destroyer<true>
{
template <class T>
static void destroy_array(T*, T*){}
};

template <class T>
void do_destroy_array(T* first, T* last)
{
while(first != last)
{
first->~T();
++first;
}
}

}; // namespace detail

template <class T>
inline void destroy_array(T* p1, T* p2)
{
detail::array_destroyer<boost::has_trivial_destructor<T>::value>::destroy_array(p1, p2);
}
} // namespace opt

P.S.: Нагло утянуто отсюда: Библиотека BOOST C++: заголовочный файл <boost/type_traits.hpp>