1 апр. 2009 г.

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

Все типы Boost.MultiIndex объявлены в пространстве имен boost::multi_index. Дополнительно в пространство boost включены шаблонный класс multi_index_container и глобальные функции get и project. Для краткости большинство приводимых фрагментов кода предполагают предшествующее расширение области видимости:

using namespace ::boost;
using namespace ::boost::multi_index;

Введение

Мы предлагаем ознакомится с основными концепциями Boost.MultiIndex на паре простых примеров.

Проведение различных сортировок одного множества

Шаблоны STL set и multiset являются массивами переменной длины, где элементы отсортированы согласно установленному сравнительному предикату (критерию). Эти контейнерные класс немногим способны помочь программисту, когда ему необходимо сортировать и искать элементы опираясь на несколько критериев. Взгляните на пример:

struct employee
{
int id;
std::string name;
employee(int id,const std::string& name):id(id),name(name){}
bool operator<(const employee& e)const{return id<e.id;}
};

Тот факт, что ID-ключ является уникальным для каждого служащего, определяет возможность перегрузки оператора «<», что в свою очередь позволяет держать такие переменные типа employee в контейнере std::set<employee>. Теперь, если мы хотим вывести список работников в алфавитном порядке, мы столкнемся с рядом трудностей и недостатков, начиная от неразумного расходования памяти, кончая громоздкостью и ненаглядностью кода. Вот, что первое приходит в голову:

Копировать переменную типа std::set<employee> в переменную типа vector и в качестве критерия сортировки определить функтор, работающий с полем структуры name.

Хранить кроме основного массива еще массив указателей на его элементы, соответственно отсортированный по полю name.

Их этих двух решений, пожалуй, второе принимается большинством программистов, заботящимся о быстродействии, однако оно предполагает постоянно в программе «тащить» вместо одной структуры – две. Если от такого кода потребовать еще и устойчивости к исключительным ситуациям, то поддерживать такую конструкцию становится невозможным.

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

// определение бииндексированного множества с индексацией по полям name // и ID
typedef multi_index_container<
employee,
indexed_by<
// сортировка согласно перегруженному оператору “<”
ordered_unique<identity<employee> >,

// сортировка функтором less<string> по полю name
ordered_non_unique<member<employee,std::string,&employee::name> >
> 
> employee_set;

void print_out_by_name(const employee_set& es)
{
// установка доступа к элементам по первому индексу
const employee_set::nth_index<1>::type& name_index=es.get<1>();
// используйте name_index просто как std::set
std::copy(
name_index.begin(),name_index.end(),
std::ostream_iterator<employee>(std::cout));
}

Вместо единственного условного критерия сравнения, как в ассоциативных контейнерах STL, multi_index_container имеет typelist (список типов), называемый indexed_by, где конкретизируются типы, вы устанавливаете соответствующие индексы. Доступ через соответствующий индекс получается через get<N>(), где N изменяется от 0 до количества условных критериев минус один. Доступ через 0-ой индекс может осуществляется по умолчанию прямо из объекта multi_index_container без использования функции get<0>(). Например, es.begin() – то же, что и es.get<0>().begin().

Отметим, что get() возвращает ссылку на индексированное множество (будем называть множество, имеющее доступ к элементам основного массива, через один из индексов просто ИНДЕКСом) , а не объект. Индекс не может быть сконструирован в самостоятельный объект, отдельный от основного контейнера. Поэтому следующий код

// неверно: мы забыли & после employee_set::nth_index<1>::type
const employee_set::nth_index<1>::type name_index=es.get<1>();

будет вызывать ошибку, поскольку здесь осуществлена попытка сделать индекс-объект name_index. Это пример типичной ошибки среди пользователей этой библиотеки.

Двунаправленный список с быстрым поиском

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

typedef std::list<std::string> text_container;

std::string text=
"Alice was beginning to get very tired of sitting by her sister on the "
"bank, and of having nothing to do: once or twice she had peeped into the "
"book her sister was reading, but it had no pictures or conversations in "
"it, 'and what is the use of a book,' thought Alice 'without pictures or "
"conversation?'";

// внесение текста в список, используя шаблонный токенайзер из boost
text_container tc;
boost::tokenizer<boost::char_separator<char> > tok
(text,boost::char_separator<char>(" \t\n.,;:!?'\"-"));
std::copy(tok.begin(),tok.end(),std::back_inserter(tc));

//Если мы хотим посчитать, сколько раз слово включено в список, мы воспользуемся std::count:
std::size_t occurrences(const std::string& word)
{
return std::count(tc.begin(),tc.end(),word);
}

Однако исполнение такого алгоритма будет происходить за линейное время, что может быть неприемлемо в случае больших количеств текста. Аналогично, операция удаления слова из середины списка по времени также весьма затратна в std::list:

void delete_word(const std::string& word)
{
tc.remove(word); // scans the entire list looking for word
}

Если необходимо представление списка, к примеру, в алфавитном порядке, нам снова необходимо заводить отдельную структуру, индексирующую элементы в tc. Boost.MultiIndex делает это просто комбинируя последовательный и порядковый индексы:

// объявление multi_index_container со «списковым» индексом и порядковым 
// индексом 
typedef multi_index_container<
std::string,
indexed_by<
sequenced<>, // «списковый» индекс
ordered_non_unique<identity<std::string> > // слова в алфавит. Порядке
>
> text_container;

std::string text=...

// внесение текста в список
text_container tc;
boost::tokenizer<boost::char_separator<char> > tok
(text,boost::char_separator<char>(" \t\n.,;:!?'\"-"));
std::copy(tok.begin(),tok.end(),std::back_inserter(tc));

До сих пор, замена std::list на multi_index_container не давала особых преимуществ. Код для вставки текста в контейнер не изменился, поскольку последовательный индекс предоставляет интерфейс доступа, полностью аналогичный таковому у std::list (нет необходимости обращаться к этому индексу через get<0>(), поскольку, как было сказано выше, multi_index_container наследует весь интерфейс своего нулевого индекса.) Однако введение дополнительного порядкового индекса позволит нам выполнять процедуры подсчета слов и удаления слов более эффективно:

std::size_t occurrences(const std::string& word)
{
// устанавливаем доступ через №1 индекс
text_container::nth_index<1>::type& sorted_index=tc.get<1>();

// используем sorted_index просто как std::set
return sorted_index.count(word);
}

void delete_word(const std::string& word)
{
// устанавливаем доступ через №1 индекс
text_container::nth_index<1>::type& sorted_index=tc.get<1>();

// используем sorted_index просто как std::set
sorted_index.erase(word);
}

Теперь процедуры occurrences и delete_word выполняются за логарифмическое время. Программист может использовать №0 индекс для доступа к исходному тексту (как std::list) или использовать №1 индекс, когда необходим логарифмический поиск.

Определение индексов

Индексы в конкретизации multi_index_container объявляются внутри специальной конструкции indexed_by. Например,  конкретизация:

typedef multi_index_container<
employee,
indexed_by<
ordered_unique<identity<employee> >,
ordered_non_unique<member<employee,std::string,&employee::name> >
> 
> employee_set;
включает уникальный порядковый индекс и неуникальный порядковый индекс, а, следующим кодом:
typedef multi_index_container<
std::string,
indexed_by<
sequenced<>,
ordered_non_unique<identity<std::string> >
>
> text_container;

мы установили два индекса, первый последовательного типа, а второй – неуникальный порядковый индекс. В общем случае мы можем объявлять произвольное число индексов: каждый аргумент конструкции indexed_by называется спецификатором индекса. В зависимости от типа индекса, объявление соответствующего спецификатора будет требовать дополнительной информации: например, спецификаторы ordered_unique и ordered_non_unique требуют указания ключевого поля и (необязательно) сравнительного условного предиката; вместе они полностью определяют, как будет производиться сортировка.

конкретизация multi_index_container может быть объявлена без конструкции indexed_by: в таком случае индексация по умолчанию производится таким образом, что конкретизированный шаблон эквивалентен конкретизированному std::set. Если конкретно -  конкретизация

multi_index_container<(element)>
аналогична
multi_index_container<
(element),
indexed_by<
ordered_unique<identity<(element)> >
>
>

Метки

С целью получения доступа к индексу данного multi_index_container, программист должен помнить порядковый номер этого индекса в списке indexed_by. Однако такое обращение к индексу является неуклюжим и ненаглядным, поэтому в спецификатор индекса введен такой (необязательный) параметр, как метка (tag). Она всегда ставится первым параметром в соответствующем спецификаторе. Вот как будет выглядеть наш тип employee_set с использованием меток:

// метки
struct name{};

typedef multi_index_container<
employee,
indexed_by<
ordered_unique<identity<employee> >,
ordered_non_unique<tag<name>,member<employee,std::string,&employee::name> >
>
> employee_set;

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

typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name::iterator it=es.get<name>().begin();

Если метка для индекса не определена (как в случае №0 индекса в предыдущем примере), доступ к данному индексу предоставляется только по его номеру. Отметим, что typedef-конструкции для получения доступа к индексу через номер и через метку различны. Например:

// тип переменной доступа к индексу #1,
employee_set::nth_index<1>::type
// тип переменной доступа к индексу, меченному name (в данном случая эти два определения эквивалентны)
employee_set::index<name>::type

Наоборот, метод get() перегружен и используется одинаково в обоих случаях:

employee_set::index<name>::type& name_index=es.get<name>();
employee_set::nth_index<1>::type& name_index2=es.get<1>(); // тот же индекс

К тому же шаблон класса tag может получать в качестве параметров несколько меток для одного индекса: например, спецификация №1 индекса в предыдущем примере может быть переписана с использованием двух различных меток name и by_name:

// метки
struct name{};
struct by_name{};

typedef multi_index_container<
...
ordered_non_unique<
tag<name,by_name>,
member<employee,std::string,&employee::name>
>
...
> employee_set;

Типы индексов

В настоящее время Boost.MultiIndex имеет следующие типы индексов:

Порядковые индексы сортируют элементы как std::set-ы и предоставляют аналогичный интерфейс. Они могут быть двух вариантов: уникальные и неуникальные; первые не позволяют хранить идентичные элементы, тогда как вторые – позволяют (как std::multiset)

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

Пример во введении демонстрирует использование всех видов индексов.

Порядковые индексы

Порядковые индексы сортируют элементы multi_index_container согласно заданному ключевому полю и заданному сравнительному предикату. Доступ через эти индексы аналогичен работе с контейнерами std::set, и более того – полностью копируют их интерфейс, хотя присутствуют некоторые небольшие отличия, диктуемые общими принципами Boost.MultiIndex.

Уникальный и неуникальный варианты

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

typedef multi_index_container<
employee,
indexed_by<
ordered_unique<identity<employee> >,
ordered_non_unique<member<employee,std::string,&employee::name> >
> 
> employee_set;

Первый индекс объявлен как уникальный (поскольку поле ID уникально для каждого рабочего) и поэтому описан как тип ordered_unique, тогда как второй индекс неуникален (поскольку два Джона Смита могут служить в одной компании) и соответственно описан как ordered_non_unique.

Разделение порядковых индексов на уникальный и неуникальные влияет на то, какие элементы можно вставлять в multi_index_container. Короче включение через уникальный порядковый индекс проходит аналогично включению в std::set, тогда как через неуникальный – аналогично std::multiset. Например, employee_set может содержать структуры employee(0, “George Brown”) и employee(1, “George Brown”), но добавить структуру с таким же ID, какой там уже имеется, нельзя.

В конкретизации multi_index_container может быть объявлено несколько уникальных индексов. Например, если мы собираем данные об ИНН работников, который также уникален, мы можем организовать контейнер таким образом:

struct employee
{
int id;
std::string name;
int ssnumber;

employee(int id,const std::string& name,int ssnumber):
id(id),name(name),ssnumber(ssnumber){}

bool operator<(const employee& e)const{return id<e.id;}
};

typedef multi_index_container<
employee,
indexed_by<
// сортировать по employee::operator<
ordered_unique<identity<employee> >,

// сортировать согласно less<string> по полю name
ordered_non_unique<member<employee,std::string,&employee::name> >,

// сортировать согласно less<int> по полю ssnumber
ordered_unique<member<employee,int,&employee::ssnumber> >
>
> employee_set;

Спецификация индексов

Синтаксис спецификатора порядкового индекса в поле шаблона indexed_by выглядит следующим образом:

(ordered_unique | ordered_non_unique)
<[(tag)[,(key extractor)[,(comparison predicate)]]]>

(ordered_unique | ordered_non_unique)
<[(key extractor)[,(comparison predicate)]]>

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

Ключевое поле

Первым параметром шаблона спецификации индекса (или второй, если есть метка) является ключевое поле. Этот параметр передает ссылку на элемент (в нашем примере на всю структуру employee) и частично информацию о способе сортировки таких элементов. Вот основные варианты определения ключевого поля:

Целый элемент служит ключом, как в случае первого индекса в employee_set. А для определения ключевого поля можно использовать; identity как раз используется, когда ключом является целый объект.

Сравнение осуществляется по отдельному полю структуры элемента – это очень напоминает работу с индексами отдельной колонки реляционной базы данных. Boost.MultiIndex имеет шаблон member, который возвращает ключ на поле структуры, определяемое указателем

В качестве примера снова рассмотри наш employee_set. Первый индекс:

ordered_unique<identity<employee> >

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

ordered_non_unique<member<employee,std::string,&employee::name> >

Здесь мы используем шаблон member, чтобы определяем (извлекаем) ключ из поля структуры employee. Тип этого ключа – std::string.

Другой общий случай – когда сортировка производится по заданной функции-члену класса элемента. Это напоминает случай вычисляемого индекса в некоторых реляционных БД. В таких случаях ключ не является переменной-членом структуры элемента, а получает значение из заданной функции-члена. Boost.MultiIndex поддерживает такой механизм «извлечения» ключа посредством шаблона const_mem_fun. Рассмотрим расширение нашего примера, когда сортировка по третьему индексу проводится на основе длины поля name:

struct employee
{
int id;
std::string name;

employee(int id,const std::string& name):id(id),name(name){}

bool operator<(const employee& e)const{return id<e.id;}

// возвращает длину поля name
std::size_t name_length()const{return name.size();}
};

typedef multi_index_container<
employee,
indexed_by<
// сортировать согласно employee::operator<
ordered_unique<identity<employee> >,

// сортировать согласно less<string> по полю name
ordered_non_unique<member<employee,std::string,&employee::name> >,

// сортировать согласно less<int> по ключу name_length()
ordered_non_unique<
const_mem_fun<employee,std::size_t,&employee::name_length>
>
>
> employee_set;

Пример 2 в разделе примеров содержит законченную программу, демонстрирующую использование const_mem_fun. Почти всегда, используются именно константные функции-члены, поскольку в multi_index_container-е (как и в std::set-е) элементы предполагаются постоянными. Однако шаблон mem_fun предоставляет вам возможность использовать неконстантные функции-члены. Их использование и применение подробно обсуждено в параграфе о дополнительных характеристиках ключевых полей Boost MultiIndex в разделе дополнительной информации.

Более сложные типы контейнеров могут потребовать сложных ключей, собирающих информацию сразу от нескольких ключевых полей. Сложные ключи введены в Boost.MultiIndex при помощи конструкции composite_key.

identity, member и const_mem_fun (или их комбинации при помощи composite_key) наиболее часто используемые шаблоны, используемые для разработки multi_index_container-ов. Однако пользователь имеет возможность написать собственную процедуру «извлечения» ключа для более экзотических ситуаций, чем те, которые предполагает данная концепция. В примере №6 демонстрируется использование нескольких технологий «извлечения» ключа, которые необходимы когда доступ к элементам и/или ключам осуществляется через указатели.

Условные предикаты сравнения

И наконец, последнее поле спецификатора порядкового индекса – это предикат (критерий) сравнения, который представляет из себя оператор «меньше» (на деле – функтор), определенных для типа ключа. Этот критерий не отличается от таковых, использующихся в контейнерах STL, таких как std::set. По умолчанию (если поле предиката в спецификаторе не заполнено), индекс с ключом типа key_type сортирует элементы согласно функтору std::less<key_type>. Если требуется определить какой-то другой критерий сравнения, ссылка на него дается в конце объявления индекса:

// определяет полииндексированное множество с индексами по id и name
// в обратном алфавитном порядке
typedef multi_index_container<
employee,
indexed_by<
ordered_unique<identity<employee> >, // обычный индекс
ordered_non_unique<
member<employee,std::string,&employee::name>,
std::greater<std::string> //по умолчанию std::less<std::string>
>
>
> employee_set;

Специальные функции поиска

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

employee_set es;
...
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name::iterator it=es.get<name>().find("Veronica Cruz");

Кроме того, Boost.MultiIndex предоставляет специальные операции поиска, который ведется по ключу, отличному по типу от key_type-а нашего индекса. Это оказывается чрезвычайно полезным, когда затратно создавать переменные типа key_type. Последовательные контейнеры STL не имеют аналогичных возможностей, поэтому приходится прибегать к неэлегантным обходным путям. Например, рассмотрим задачу определения рабочих, чьи ID лежат в промежутке [0,100]. Принимая во внимание, что ключ индекса №0 это сам элемент employee, можем для начала написать следующее:

employee_set::iterator p0=es.lower_bound(employee(0,""));
employee_set::iterator p1=es.upper_bound(employee(100,""));

Однако помните, что std::less<employee> в действительности сравнивает ID рабочих, поэтому более уместно было бы запретить создание структуры employee целиком, а извлекать только их ID. Boost.MultiIndex предлагает для этой цели определить другой критерий-функтор:

struct comp_id
{
// compare an ID and an employee
bool operator()(int x,const employee& e2)const{return x<e2.id;}

// compare an employee and an ID
bool operator()(const employee& e1,int x)const{return e1.id<x;}
};

и написать теперь уже:

employee_set::iterator p0=es.lower_bound(0,comp_id());
employee_set::iterator p1=es.upper_bound(100,comp_id());

Здесь в ходе поиска мы не просто перебираем ID элементов вместо перебор целых employee-структур, мы создаем новый критерий поиска. В общем, если мы хотим перегрузить критерии поиска, мы должны позаботиться об их совместимости. Если не прибегать к строгим определениям (а их вы можете найти в главе «MultiIndex: Руководство»), то критерии называются совместимыми, когда любая последовательность, отсортированная по одному из них также отсортирована и по другому. Вот более интересный пример сортировки:

// сортировка по инициалам
struct comp_initial
{
bool operator()(char ch,const std::string& s)const{
if(s.empty())return false;
return ch<s[0];
}

bool operator()(const std::string& s,char ch)const{
if(s.empty())return true;
return s[0]<ch;
}
};

// получение первого рабочего с именем на 'J' (сортировка по имени)
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>(); 
employee_set_by_name::const_iterator it=
name_index.lower_bound('J',comp_initial());

Получение диапазонов

Диапазонный поиск, то есть поиск всех элементов данного интервала – очень частая операция, для которой стандартные lower_bound и upper_bound могут быть использованы, разве что в простейших случаях. Например, этот код отбирает элементы multi_index_container<double>, расположенные в интервале [100; 200]:

typedef multi_index_container<double> double_set;
// напомним, что параметры при конркретизации по умолчанию:
// multi_index_container<double,indexed_by<unique<identity<double> > > >.

double_set s;
...
double_set::iterator it0=s.lower_bound(100.0);
double_set::iterator it1=s.upper_bound(200.0);
// диапазон [it0,it1) содержит элементы [100,200]

Небольшие изменения в код следует внести, если мы хотим сделать одну из границ интервала нестрогой. Если мы, к примеру, ищем элементы интервала (100, 200), то писать надо так:

double_set::iterator it0=s.upper_bound(100.0);
double_set::iterator it1=s.lower_bound(200.0);
// диапазон [it0,it1) содержит элементы (100;200)

Чтобы внести в код такое усложнение, аккуратный программист обязан принимать во внимание, что верхняя и нижняя границы интервала – вещи нестрогие. Если, к примеру, нижняя грань 200, а верхняя – 100, итераторы it0 и it1, определенные в порядке, обратном приведенному, не будут охватывать никакого диапазона. При попытке пройтись в цикле по элементам от it0 до it1 вы вполне можете проворонить исключение. Эти особенности делают поиск диапазона стандартными STL функциями ненаглядным и незащищенным.

Встроенная функция range, часто в сочетании с выражениями Boost.Lambda, может сильно облегчить программирование такого рода поисков. Взгляните:

using namespace boost::lambda;

typedef multi_index_container<double> double_set;
double_set s;
...
std::pair<double_set::iterator,double_set::iterator> p=
s.range(100.0<=_1,_1<=200); // 100<= x <=200
...
p=s.range(100.0<_1,_1<200); // 100< x < 200
...
p=s.range(100.0<=_1,_1<200); // 100<= x < 200
Одна или обе границы могут быть обозначены ключевым словом unbounded (неограничен):
p=s.range(100.0<=_1,unbounded); // 100 <= x
p=s.range(unbounded,_1<200.0); // x < 200
p=s.range(unbounded,unbounded); // то же, что std::make_pair(s.begin(),s.end())

Обновление

Встроенная функция replace выполняет замену в месте, указанном итератором:

typedef index<employee_set,name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

employee_set_by_name::iterator it=name_index.find("Anna Jones");
employee anna=*it;
anna.name="Anna Smith"; // она уже вышла за муж за К. Смита
name_index.replace(it,anna); // обновляем запись

replace-процедура имеет в данном случае ряд особенности:

Выполнение ее происходит за постоянное время, если элемент после замены остается на то же месте, относительно всех индексов; в обратном случае она происходит за логарифмическое время.

Корректность итератора и ссылки проверяется.

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

replace – очень полезная функция, не объявленная в стандартах STL, очень удобна как раз когда требуется надежная защита от исключительных ситуаций.

Наблюдательный читатель может заметить, что удобство процедуры replace «влетает в копеечку»: элемент копируется целиком дважды. Первый раз при получении замещающего элемента, второй – при вставлении его в контейнер. Если структура громоздка для копирования, будет выгоднее модифицировать отдельные поля элемента. Для этого в Boost.MultiIndex введен альтернативный механизм обновления – функция modify:

struct change_name
{
change_name(const std::string& new_name):new_name(new_name){}
void operator()(employee& e)
{
e.name=new_name;
}

private:
std::string new_name;
};
...
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

employee_set_by_name::iterator it=name_index.find("Anna Jones");
name_index.modify(it,change_name("Anna Smith"));

Функция Modify получает на вход функтор (или указатель на функцию), который модифицирует элемент, тем самым, избавляя программиста от необходимости создавать лишнюю структуру, как это нужно было бы в случае replace. Так же, как и replace, эта функция хорошо защищена и сохраняет информацию контейнера при сбоях. Однако механизм ее действия не полностью аналогичен механизму replace. Рассмотрим, что происходит, если в результате замены возникает повтор по уникальному индексу. В случае replace, исходное значение поля будет возвращено и функция завершится, никак не повлияв на контейнер. В случае modify, такой возможности у функции нет, поэтому ошибочный элемент, появившийся в результате модификации, будет немедленно удалее, а функция вернет false. Это различие необходимо должно учитываться программистом при выборе между функциями replace и modify.

Аналогично modify, объявлена функция для модификации ключевого поля -- modify_key. В таком случае модифицирующий функтор получает ссылку на поле key_value элемента контейнера, а не на весь элемент. Обратите внимание, что такие модификации возможны (разумеется, иначе никак), только в том случае, когда ключ не извлекается специальными процедурами (как const_mem_fun), а получается прямо из поля структуры элемента

struct change_str
{
change_str(const std::string& new_str):new_str(new_str){}

// работает со строкой, не со структурой employee
void operator()(std::string& str)
{
str=new_str;
}

private:
std::string new_str;
};
...
typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

employee_set_by_name::iterator it=name_index.find("Anna Jones");
name_index.modify_key(it,change_str("Anna Smith"));

Точно так же, как и modify, modify_key удаляет элемент, если результат модификации вызывает противоречие в каком-то из индексов. Modify и modify_key позволяют использование удобного синтаксиса Boost.Lambda в указании модификаторов:

using namespace boost::lambda;

typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

employee_set_by_name::iterator it=name_index.find("Anna Jones");
name_index.modify_key(it,_1="Anna Smith");

Последовательные индексы

В отличие от порядковых индексов, последовательные индексы не имеют фиксированного правила следования элементов – наоборот, элементы могут быть помещены в любую точку последовательности (как в std::list). Интерфейс последовательных индексов, соответственно задуман очень похожим на std::list, по крайней мере, все функции std::list здесь продублированы. Их реализация может быть слегка отличной по аргументам и/или семантике, дабы удовлетворять единой концепции Boost.MultiIndex. В частности, имеется существенное ограничение в доступе к элементам через последовательный индекс: изменение элементов multi_index_container через итератор запрещено:

multi_index_container<
int,
indexed_by<sequenced<> >
> s; // контейнер-список

s.push_front(0);
*(s.begin()) = 1; // ERROR: элементы нельзя изменять ТАК

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

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

multi_index_container<
int,
indexed_by<
sequenced<>, // последовательный тип
ordered_unique<identity<int> > // дополнительный день
>
> s;

s.get<1>().insert(1); // вставка «1» посредством индекса #1
s.get<1>().insert(0); // вставка «0» посредством индекса #1

// список элементов, упорядоченных по индексу #0
std::copy(s.begin(),s.end(),std::ostream_iterator<int>(std::cout));
// результат: 1 0

Таким образом, последовательные индексы располагают элементы в порядке их вставления, даже если вставка производилась посредством непоследовательного индекса.

Спецификация

Последовательные индексы описываются при помощи шаблона sequenced:

sequenced<[(tag)]>

Параметр tag является необязательным.

Функции списка

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

Обновление

Так же, как и порядковые индексы, последовательные индексы предоставляют аналогичные функции modify и replace. Однако аналога modify_key нет, поскольку порядковые индексы не имеют ключа.

Проекция итераторов

Если в нашем multi_index_container-е определены два индекса i1 и i2, то при помощи функции project можно получить значение i1-итератора некоего элемента по известному i2-итератору того же элемента. Эта процедура позволяет программисту быстро перемещаться между различными индексами multi_index_container-а. Рассмотрим пример:

typedef employee_set::index<name>::type employee_set_by_name;
employee_set_by_name& name_index=es.get<name>();

// Список работников, упорядоченных по ID, начиная с ID Роберта Брауна

employee_set_by_name::iterator it1=name_index.find("Robert Brown");

// получение итератора по 0-индексу из it1
employee_set::iterator it2=es.project<0>(it1); 

std::copy(it2,es.end(),std::ostream_iterator<employee>(std::cout));

Чуть более сложный пример:

text_container tc;

// доступ к элементам через #1 (индекс, упорядоченный по словам)
text_container::nth_index<1>::type& sorted_index=tc.get<1>();

// вставка "older" после каждой из "sister"

text_container::nth_index_iterator<1>::type it1=
sorted_index.lower_bound("sister");

while(it1!=sorted_index.end()&&*it1=="sister"){
// преобразование итератора в итератор последовательного индекса
text_container::iterator it2=tc.project<0>(it1);

tc.insert(it2,"older");
++it1;
}

При необходимости может быть использован в сочетании с tags.

Безопасность и дополнительные возможности

multi_index_container предосталяет дополнительные возможности и гарантирует определенный уровень безопасности, как это делает STL. Обращение к элементам через итераторы защищено в смысле операций вставки, замены и редактирования элементов.

Конкретизация multi_index_container может быть проведена абсолютно аналогично std::set, std::multiset и (с небольшими ограничениями) std::list, см. дополнительные вопросы. Эта аналогия продолжается не только на синтаксис, но и на эффективность выполнения операций. Для получения дополнительной информациии касательно дополнительных функций смотри руководство. Для получения данных о производительности multi_index_container - смотрите презентацию.

P.S.: ... Нагло утянуто отсюда: BOOST C++: Multi_Index

Использование библиотеки boost::signals

Нередко при проектировании систем возникают задачи следующего характера. В системе появляется сущность, являющаяся генератором (источником) некоторых событий. И есть ряд других сущностей, которые должны быть уведомлены о возникновении этих событий. Простейший пример – на некоторой интерфейсной форме есть кнопка, по клику на которой необходимо выполнить некоторый код. Кнопка – источник события. Класс формы – получатель уведомления о возникновении события и его обработчик. В книге «Паттерны проектирования» от GoF такое взаимодействие сущностей описано как паттерн «Команда» или «Действие». Непосредственной поддержки описанного механизма подписки-уведомления в языке С++ (в отличии от C#, например) нет. Но реализацию этого механизма можно найти в boost. А именно – в библиотеке boost::signal. Описанный простейший пример с кнопкой и формой будет выглядеть следующим образом:

#include <iostream>
#include <boost/signal.hpp>
#include <boost/bind.hpp>

 
//////////////////////////////////////////////////////////////////////////////////////////
// Класс кнопки
// Пример класса-источника событий
//////////////////////////////////////////////////////////////////////////////////////////
class SampleButton
{
public:
 // Метод, добавляющий новый обработчик события

 // h - функтор, реализующий обработчик
 void SetOnClick(boost::function<void()> h) 
 {
  // Присоединяем к событию новый обоаботчик

  m_OnClick.connect(h);
 }
 // Метод, инициирущий событие
 void FireOnClick() {m_OnClick();}

private:
 // Член данных, хранящий коллекцию обработчиков события OnClick
 boost::signal<void ()> m_OnClick;
};
 
//////////////////////////////////////////////////////////////////////////////////////////

// Класс формы
// Пример класса-обработчика событий
//////////////////////////////////////////////////////////////////////////////////////////
class SampleForm
{
public:
 // Конструктор
 SampleForm()

 {
  // Добавляем к каждой кнопке обработчики события OnClick
  m_Button1.SetOnClick(boost::bind(&SampleForm::Handler1, this));
  m_Button2.SetOnClick(boost::bind(&SampleForm::Handler2, this));
  m_Button3.SetOnClick(boost::bind(&SampleForm::Handler2, this));
  m_Button3.SetOnClick(boost::bind(&SampleForm::Handler1, this));
 }

 
 // Тестовый обработчик события
 void Handler1()
 {
  std::cout << "Handler 1 entered" << std::endl;
 }

 
 // Тестовый обработчик события
 void Handler2()
 {
  std::cout << "Handler 2 entered" << std::endl;
 }

 
 // Пример кнопок
 SampleButton m_Button1;
 SampleButton m_Button2;
 SampleButton m_Button3;
};
 
int main(int argc, char** argv)

{
 // Создаем форму
 SampleForm form;
 
 // Для каждой кнопки на форме инициируем событие OnClick
 form.m_Button1.FireOnClick();
 form.m_Button2.FireOnClick();
 form.m_Button3.FireOnClick();

 
 // Вывод на консоль следующий
 // Handler 1 entered
 // Handler 2 entered
 // Handler 2 entered
 // Handler 1 entered
 return 0;

}

Что, собственно, здесь происходит.

  1. Объявляется член данных, описывающий событие. В этом примере это SampleButton::m_OnClick. Тип этого члена данных – boost::signal. Этот класс – шаблонный, и в качестве обязательного параметра принимает сигнатуру обработчика сигнала. В примере эта сигнатура – void (). Т. е. обработчик не принимает параметров и ничего не возвращает.
  2. Реализуется метод, осуществляющий подписку на соответствующее событие. В примере – это метод AddOnClick. Подписка осуществляется с помощью метода signal::connect, в который передается обработчик, с которым необходимо связать событие.
  3. Где-то в коде производится инициация события. В данном случае это делается в методе SampleButton::FireOnClick. Инициация события производится путем применения оператора () к члену данных, описывающего событие. В данном случае – к члену m_OnClick.

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

Как видно из примера, на одно событие можно назначить сразу несколько обработчиков. При этом (в простейшем случае) будут вызваны все назначенные обработчики. Правда, последовательность их вызова разработчиками библиотеки не гарантируется. Нередко для обозначения сущности, описывающей событие (члена данных m_OnClick) используется термин сигнал (signal). А для обозначения подписчиков на это событие – термин слот (slot). В частности, эта терминология используется в оригинальной документации на эту библиотеку.

Объявление объекта-сигнала

Объект-сигнал объявляется как экземпляр класса boost::signal<Sig, Comb, Group, GroupComp, SlotFn>. У этого шаблонного класса первый аргумент является обязательным, и определяет сигнатуру вызова сигнала. Оставшиеся – определяют некоторые аспекты поведения объекта-сигнала, о которых будет рассказано ниже. Примеры объявления объекта-сигнала:

boost::signal<void ()> sig; // Сигнал без аргументов, который ничего не возвращает
boost::signal<int ()>sig; // Сигнал без аргументов, возвращающий целое число

boost::signal(void (float, int)> sig; // Сигнал, принимающий два аргумента и ничего не возвращающий

и т. п. Сигнатура сигнала определяет сигнатуру слотов, которые могут быть присоединены к такому сигналу. Вообще говоря, в ряде случаев требуется точное соответствие сигнатур слотов сигнатуре сигнала.

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

Присоединение слотов

Слот может быть присоединен к сигналу с помощью метода connect. В общем случае этот метод принимает на вход один аргумент – сущность, реализующую концепцию функционального объекта. Таким образом, к слоту может быть присоединено все что угодно, если к этому примени оператор вызова функции с подходящей сигнатурой. В частности, это означает, что вместе с boost::signal может легко применяться такие компоненты boost, как boost::function и boost::bind.

Указание порядка вызова слотов

В случае простого присоединения нескольких слотов к сигналу библиотека, вообще говоря, не специфицирует, в каком порядке будут вызваны соответствующие слотам функции. Если все слоты присоединяются в одном месте, то порядок предсказать можно (вызываться они будут в порядке присоединения). Если же присоединение слотов «размазано по коду», то такое предсказание сделать гораздо сложнее. И, если порядок вызова слотов важен, то его надо специфицировать явно путем специальной формы вызова метода connect:

float foo(int, int)

{
 return 1.5;
}
 
int bar(float, float)
{
 return 2.5;

}
 
boost::signal<float (int, int)> test_sig;

В случае такого присоедининия слотов:

test_sig.connect(0, &foo);
test_sig.connect(1, &bar);
std::cout << test_sig(1.5, 2.5) << std::endl;

на экран будет выведено число 1.5. А в случае такого присоединения:

test_sig.connect(1, &foo);
test_sig.connect(0, &bar);
std::cout << test_sig(1.5, 2.5) << std::endl;

число 2.

Не сложно понять, что все дело в первом параметре метода connect. В общем случае – это целое число, задающее очередность вызова слотов при инициации сигнала. Но, вообще говоря, это может быть и совсем не целое число, а, например строка. Тип группирующего параметра а также способ их упорядочивания задаются третьим и четвертым шаблонным параметром класса signal (Group и GroupComp). По умолчанию это int и less<int> соответственно. Например:

boost::signal<float (int, int), boost::last_value<float>, std::string> test_sig;

 
test_sig.connect("aaa", &foo);
test_sig.connect("bbb", &bar);
std::cout << test_sig(1.5, 2.5) << std::endl;

Выведет на экран число 2.

У метода connect может быть еще один параметр (последний), определяющий место, куда помещается новый слот при присоединении. Варианты этого параметра – boost::signals::at_front (слот добавляется в начало списка) или boost::signals::at_back (слот добавляется в конец списка). По умолчанию этот параметр равен boost::signals::at_back, т. е. слоты добавляются в конец списка. Если добавляемый слот не содержит идентификатора группы, то (в зависимости от значения этого параметра) он добавляется либо в начало, либо в конец общего списка слотов. В случае указания идентификатора группы этот параметр определяет способ добавления слота в рамках других слотов с тем же идентификатором. Например:

boost::signal<float (int, int), boost::last_value<float>, std::string> test_sig;

 
test_sig.connect("aaa", &foo, boost::signals::at_front);
test_sig.connect("aaa", &bar, boost::signals::at_front);
std::cout << test_sig(1.5, 2.5) << std::endl;

Выведет на экран число 1.5.

P.S.: Нагло спёрто отсюда: [[doc:cpp:boost:signal]]

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

Нередко в практике программистов встречаются задачи, когда нужно разобрать строку (как еще иногда говорят - распарсить). Решений этой задачи - множество. Это и простейший поиск очередного символа-разделителя, и регулярные выражения, и специальные алгоритмы (например, из коллекции boost::string_algo). Но иногда логика разбиения строки на части (т. е. логика разбора) может быть ну совсем уж специфичная. Или регулярное выражение получается очень уж навороченное. Или по каким-то причинам библиотека регулярных выражений (тот же boost::regex) неприменима. Тогда на помощь может придти boost::tokenizer.

Вот пример его использования:

#include <iostream>
#include <string>
#include <list>

#include <boost/tokenizer.hpp>
#include <boost/iostreams/device/file.hpp> // file_source
#include <boost/iostreams/stream.hpp>      // stream

int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        // определяем длиннючие типы
        typedef boost::iostreams::file_source file_device_type;
        typedef boost::iostreams::stream<file_device_type> stream_type;
        typedef boost::char_separator<char> token_func_type;
        typedef boost::tokenizer<token_func_type> tokenizer_type;
        typedef std::list<std::string> token_list_type;
        
        // открываем файл
        stream_type strm ( file_device_type("C:\\temp.txt", BOOST_IOS::out|BOOST_IOS::binary) );
        strm >> std::noskipws;
        
        // читаем его содержимое в буфер
        std::string buffer;
        std::copy (
            std::istream_iterator<char>(strm),
            std::istream_iterator<char>(),
            std::back_inserter<std::string>(buffer) );
    
        // разбиваем буфер на токены-слова
        // и помещаем их в список строк
        //boost::tokenizer<> tok(buffer);
        token_func_type sep(" \t\n");
        tokenizer_type tok(buffer, sep);
        token_list_type tokens_list;
        std::copy (
            tok.begin(),
            tok.end(),
            std::back_inserter<token_list_type>(tokens_list) );

        // выводим список на печать
        std::copy (
            tokens_list.begin(),
            tokens_list.end(),
            std::ostream_iterator<std::string>(std::cout, "\n") );
    }
    catch ( std::exception & exc )
    {
        std::cerr << "Exception: " << exc.what() << std::endl;
    }
    return 0;
}

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

Каждому в своей программисткой практике приходилось сталкиваться с ситуациями, когда необходимо было что-либо преобразовать из строки в, предположим, число, или обратно. В зависимости от того, что, куда и как преобразовывается, для этого можно применять sprintf/sscanf, _itoa/atoi (и другие функции из этой группы), strtoi, и т. п. Все эти методы по своему хороши, но для каждого варианта преобразования необходимо помнить - «а что лучше всего для этого преобразования подходит?»

Стандарт языка C/C++ предусматривает ряд средств для выполнения таких преобразований. Однако они различаются по простоте использования, расширяемости и безопасности.

К примеру, существует ряд ограничений для стандартной для языка C функции atoi:

  • Преобразование выполняется только в одном направлении: из текстовой формы во внутренний тип данных. Обратное преобразование с использованием стандартных функций C требует либо использования неудобной и не совсем безопасной функции sprintf, или потери платформонезависимости при использовании таких функций, как itoa.
  • Набор поддерживаемых типов является лишь подмножеством встроенных числовых типов, а именно int, long, и double.
  • Этот набор типов не может быть расширен однородным образом. К примеру, преобразование из строкового представления в complex или rational.

Стандартная функция C strtol имеет те же же основные ограничения, хотя предлагает более аккуратный контроль за процессом преобразования. Однако, в общем случае такой контроль или не требуется, или не используется. Семейство функций scanf предоставляет еще больший контроль, однако им не хватает надежности и простоты использования.

Стандартная библиотека языка C++ содержит stringstream как возможную основу для обсуждаемого форматирования с преобразованием. Она предлагает удобный набор средств для контроля за форматированием и преобразованием I/O из и в произвольный тип через текст. Для простых преобразований непосредственное использование stringstream может быть неудобным (или введения дополнительных локальных переменных и невозможности использования удобной инфиксной формы выражений) или неочевидным и туманным (когда объекты типа stringstream создаются как временные объекты в выражении). Локаль (facets) содержит приемлемый набор средств для контроля за представлением в текстовом виде, однако сложность кода приводит к тому, что использовать эти средства удобно только небольшму количеству программистов.

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

Детальное обсуждение всех опций и вопросов, возникающих при форматировании строк, включая сравнение stringstream, lexical_cast, и других средств, можно найти в статье Херба Саттера (Herb Sutter)  The String Formatters of Manor Farm.

boost::lexical_cast позволяет снять эту проблему. Для его использования достаточно знать - какой тип в какой вы ходите преобразовать. И все. Например:

// преобразует целое в строку
std::string str = boost::lexical_cast<std::string>(10);

// строку в целое
int value = boost::lexical_cast<int>("10");
// преобразует пару целых в экземпляр класса Point
Point pt = boost::lexical_cast<Point>("10, 20");

// обратное преобразование
std::wstring wstr = boost::lexical_cast<std::wstring>(pt);

// и т. д.

Достаточно удобно, не правда ли?

Принципы работы.

Принцип работы boost::lexical_cast очень прост. Для преобразования он использует строковый поток (std::strstream), выводя (с помощью оператора «) в него преобразуемое значние, после чего читая из него значение типа, в который делается преобразование. Т. е. вызов boost::lexical_cast эквивалентен:

// положим, что from_val и to_val - это, соответственно преобразуемое

// значение и приемник результата преобразования
std::ostringstream o_str;
o_str << from_val
std::istringstream i_str(o_str.str());
i_str >> to_val;

Из этого становится очевидным, что для корректного выполнения преобразования необходимо (и достаточно) наличия для преобразуемого типа перегруженного оператора вывода в поток и/или чтения из потока. Для всех примитивных типов такие операторы реализованы в библиотеке STL, а для «пользовательских» типов такой оператор может написать сам разработчик.

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

Определенные в хидере "boost/lexical_cast.hpp" части библиотеки:

namespace boost
{
    class bad_lexical_cast;
    template <typename Target, typename Source>
      Target lexical_cast(Source arg);
}

Тестовая программа - в файле lexical_cast_test.cpp.

lexical_cast

template <typename Target, typename Source>
  Target lexical_cast(Source arg);

Функция возвращает результат передачи значение arg в стандартный строковый поток и обратного преобразования в объект типа Target. Когда тип Target это std::string или std::wstring, извлечение из потока получает полное содержимое строки, включая пробелы, вместо того, чтобы использовать operator>> по умолчанию. Если преобразование дает ошибку, то генерируется исключение bad_lexical_cast.

Требования к аргументу и возвращаемому значению:

  • Source является OutputStreamable, что значает, что определен operator<<, который получает объект типа std::ostream или std::wostream слева и объект типа аргумента справа.

  • Target является InputStreamable, что означает, что определен operator>>, который берет объект типа std::istream или std::wistream слева (left hand side) и объект возвращаемого типа справа.

  • Source и Target следуют парадигме CopyConstructible [20.1.3].

  • Target следует парадигме DefaultConstructible, что означает, что возможна инифиализация по умолчанию объекта этого типа [8.5, 20.1.4].

Символьный тип используемого потока должен быть char, если только Source или Target не требуют использования потоков с wchar_t. Типы Source, которые требую использования wchar_t-потоков это wchar_t, wchar_t *, и std::wstring. Типы Target, которые требуют использования wchar_t-потоков это wchar_t и std::wstring.

Если требуется более высокий уровень контроля за преобразованием, лучше использовать std::stringstream и std::wstringstream. Когда требуется выполнить преобразования без использования потоков, lexical_cast является неподходящим средством.

bad_lexical_cast

class bad_lexical_cast : public std::bad_cast
{
{
public:
    ... // same member function interface as std::exception
};

Для сообщений об ошибках преобразования в lexical_cast используется генерация исключений.

Недостатки

Но не смотря на свою красоту, универсальность и соответствие общепринятым стандартам преобразования, у boost::lexical_cast есть и следущие недостатки:

  • Низкая скорость работы. По тестам, проведенным Гербом Саттером, результаты которых он описал в своих «Новых сложных задачах на С++», boost::lexical_cast работает на порядок медленнее, чем тот же sprintf.
  • При преобразованиях нельзя специфицировать формат желаемой или исходной строки. Т. е. преобразования выполняются в соответствии со стандартными настройками потоков.
  • (для компилятора Visual C++) нельзя (без дополнительных телодвижений) выполнять преобразования в std::wstring/wchar_t*, если в настройках проекта не указано, что wchar_t считается встроенным типом.
  • приведение в тип double из строкового типа, возможно лишь в том случае если в качестве разделителя (дробной части от целой) используется точка.

Достоинства

Два из трех указанных выше недостатков достаточно легко обходятся. Поскольку boost::lexical_cast - это шаблон, то достаточно несложно написать необходимую специализацию, выполняющую преобразование настолько быстро, насколько это необходимо разработчику, а также учитывающую особенности типа wchar_t в VC++.

Изменения

  • Предыдущая версия шаблона lexical_cast использовала установки точности в потоках по умолчанию для чтения и записи чисел с плавающей запятой. Для числовых типов есть соответствующая специализация в std::numeric_limits, а текущая версия шаблона преобразования соответствующую точность.
  • Предыдущая версия шаблона lexical_cast не поддерживала преобразование в или из типов на основе wchar_t. Для компиляторов, которые имеют полную поддержку для wchar_t, lexical_cast теперь поддерживает преобразование из wchar_t, wchar_t *, и std::wstring и в wchar_t и std::wstring.
  • Предыдущая версия шаблона exical_cast основывалась на предположении, что обычные операторы извлечения из потока (stream extractor operators) достаточны для чтения значений. Однако, строковый ввод/вывод асимметричен, так как пробелы играют роль разделителей, а не входят в состав строк. Текущая версия исправляет эту ошибку для std::string и, где возможно, std::wstring: lexical_cast<std::string>("Hello, World") выполняется успешно вместо ошибки с генерацией исключения bad_lexical_cast.
  • Предыдущая версия шаблона lexical_cast допускала небезопасное и бессмысленное преобразование к указателям. Текущая версия генерирует исключение bad_lexical_cast для преобразований в указатель: lexical_cast<char*>("Goodbye, World") генерирует исключение вместо неопределенного поведения в предыдущей версии.

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

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

int main(int argc, char * argv[])
{
    using boost::lexical_cast;
    using boost::bad_lexical_cast;

    std::vector<short> args;

    while(*++argv)
    {
        try
        {
            args.push_back(lexical_cast<short>(*argv));
        }
        catch(bad_lexical_cast &)
        {
            args.push_back(0);
        }
    }
    ...
}

Приведенный далее пример использует числовые величины в строковой выражении:

void log_message(const std::string &);

void log_errno(int yoko)
{
    log_message("Error " + boost::lexical_cast<std::string>(yoko) + ": " + strerror(yoko));
}

Приведенный далее пример переводит числовые величины в строковое представление:

#include <string>
#include <iostream>
#include <boost/lexical_cast.hpp>

int _tmain(int argc, _TCHAR* argv[])
{
    try {
        int a(41);
        std::string b;
        b = boost::lexical_cast<std::string>(a);
        std::cout << b << std::endl;
    }
    catch (const std::exception& exc)
    {
        std::cout << exc.what() << std::endl;
    }
    return 0;
}

P.S.: Нагло спёрто отсюда [[doc:cpp:boost:lexical_cast]] и отсюда библиотека преобразований - хидер boost/lexical_cast.hpp

P.P.S.: Об эффективности boost::lexical_cast<> можно прочитать здесь: http://www.rsdn.ru/forum/flame.comp/2986431.flat.1.aspx

Использование Boost.Regex

В данной статье рассматриваются следующие вопросы касательно регулярных выражений:
  • regex_match
  • regex_search
  • regex_replace
  • regex_iterator
  • regex_token_iterator
  • Partial match


Введение


Я не хочу вступать в полемику по поводу нужности или не нужности регулярных выражений, каждый для себя решает сам. Моей целью было донести простоту использования Boost.Regex для тех, кому нравится использовать регулярные выражения. Для тех кому регулярные выражения не знакомы я советую прочесть хотя бы Википедию, а если кто-то хочет поглубже с ними познакомится, то я бы посоветовал Mastering regular expressions.
Boost.Regex является собираемой библиотекой, т.е для ее использования необходимо ее собрать. Как это сделать написано в Getting started.
Собирая библиотек вы можете выбрать один из двух алгоритмов, которые будет использоваться в движке регулирных выражений: рекурсивный и не-рекурсивный. Первый быстрый, но может грозить переполнением стека, второй немного медленней, но безопасный. Макросы для определения разных способов BOOST_REGEX_RECURSIVE и BOOST_REGEX_NON_RECURSIVE соответственно. Так же, каждый алгоритм может быть немного настроен. Макросы для настройки и их описание можно посмотреть Здесь
Boost.Regex поддерживает следующие типы синтаксисов для регулярных выражений:
  1. Perl(по умолчанию)
  2. POSIX Extended
  3. POSIX Basic

Необходимо учесть, что '.'(точка) по умолчанию включает в себя '\n'. Это может быть изменено передачей специального флага, в соответствующий алгоритм.

Основные алгоритмы


boost::regex_match


Данный алгоритм используется для проверки соответсвия входящей строки и некоторого регулярного выражения, возвращая true Если строка соответсвует и false в другом случае.
Типичный способ использования: regex_match(входящая_строка, [результаты_нахождения_соответствий], регулярное_выражение, [флаги]).
Полный список всех перегруженных объявлений смотри в документации.
Пример его использования:
std::string xStr("AAAA-12222-BBBBB-44455");
boost::regex xRegEx("(\\w+)-(\\d+)-(\\w+)-(\\d+)");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
std::cout << "Does this line match our needs? " << std::boolalpha << boost::regex_match(xStr, xResults, xRegEx) << "\n";
std::cout << "Print entire match:\n " << xResults[0] << std::endl;
std::cout << "Print the former string into another format:\n" << xResults[1] << "+"
<< xResults[2] << "+"
<< xResults[3] << "+"
<< xResults[4] << std::endl;


* This source code was highlighted with Source Code Highlighter.

Результатом работы будет:
==========================Results==============================
Does this line match our needs? true
Print entire match:
AAAA-12222-BBBBB-44455
Print the former string into another format:
AAAA+12222+BBBBB+44455

Небольшое отступление от алгоритма, для описания его параметров. Эти параметры используются во всех алгоритмах, но рассмотрим их только здесь.


результаты_нахождения_соответствий — является опциональным параметром и есть ни что иное, как объект класса match_results. Этот объект является массивом объектов класса sub_match, который в свою очередь, не более чем объект-хранитель итераторов на начало и конец найденного соответсвия в строке. результаты_нахождения_соответствий служит для сохранения результатов работы алгоритма. Так, если алгоритм был выполнен успешно, то нулевой член массива будет хранить sub_match для всего найденного соответствия(исключение состовляет использование partial match, но об этом позже). Каждый последующий член массива будет хранить итераторы на каждый capture содержащийся в регулярном выражении. Каждый элемент массива может быть проверен на наличие контента через флаг matched. Важно помнить, что каждый sub_match хранит итераторы на входящую_строку, поэтому нельзя передавать в качестве исходной строки временный объект и в дальнейшем использовать результаты алгоритма, в лучшем случае получите assert в дебаге, в худшем undefined behavior с головной болью. При рекурсивном capture в регулярном выражении(например "(\w)+")) в результирующий match_result попадет только последний capture, это поведение по умолчание, которое можно изменить. Чтобы мы могли получить доступ ко всем рекурсивным capture, мы должны передать флаг match_extra в [флаги], но это еще не все, для того, чтобы match_extra сработал, необходим объявить дефайн BOOST_REGEX_MATCH_EXTRA во всех транслируемых юнитах. Или просто раскомментировать define в boost/regex/user.hpp. Это функциональность помечена как эксперементальная и сильно уменьшающая производительность. У меня так и не получилось ее опробовать, т.к моя VS2008 выдает Access violation в недрах xutulity при попытке использования алгоритмов regex_* с раскомментированным дефайном. Не протестированный пример ее использования:
std::string xStr("The boost library has a great opportunity for the regex!");
boost::regex xRegEx("(\\b\\w{5}\\b)*");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
if( boost::regex_search(xStr, xResults, xRegEx, boost::match_extra) )
{
std::cout << "Words consist from exact 5 digits have been found in our line:\n";
for(int j = 0; j < xResults.captures(1).size(); ++j)
std::cout << xResults.captures(1)[j] << std::endl;
}


* This source code was highlighted with Source Code Highlighter.

[Флаги] — необязательный параметр, с дефолтовым значением match_default. Про доступные флаги, можно посмотреть здесь. Флаги комбинируются посредством '|'(or).

Partial match


Частичное соответсвие необходимо для проверки входной строки, на частичное соответсвие регулярному выражению. Это может быть полезным при валидации поступающих асинхронно данных или при больших объемах данных, т.е в тех случаях когда в конкретный момент времени нет возможности провести полное соответсвие между регулярным выражением и исходной строкой. Чтобы использовать partial match, необходимо передать флаг match_partial в [флаги]. При этом, если используется частичное соответствие, то используемый алгоритм(regex_match, regex_search etc.) вернет true, но флаг matched у нулевого элеменат match_results будет уставновлен в false. То, что было найдено в результате частичного соответствия, можно получить через этот же нулевой элемент.
Пример использования:
std::string xStr("AAAA-12222");
boost::regex xRegEx("(\\w+)-(\\d+)-(\\w+)-(\\d+)");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
std::cout << "Does this line match the regex? " << std::boolalpha << boost::regex_match(xStr, xResults, xRegEx,
boost::match_default | boost::match_partial) << "\n";
std::cout << "Is it the partial match? " << std::boolalpha << !xResults[0].matched << "\nPrint the partial match:\n" << xResults[0] << std::endl;


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
Does this line match the regex? true
Is it the partial match? true
Print the partial match:
AAAA-12222

regex_search


Данный алгоритм предназначен для поиска подстроки в исходной строке, по заданному регулярному выражению.
Формат использования выглядит следующим образом:
regex_search(входящая_строка, [результаты_нахождения_соответствий], регулярное_выражение, [флаги]).
Пример использования:
std::string xStr("The boost library has a great opportunity for the regex!");
boost::regex xRegEx("\\b(?:\\w+?)((\\w)\\2)(?:\\w+?)\\b");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
std::string::const_iterator xItStart = xStr.begin();
std::string::const_iterator xItEnd = xStr.end();
while( boost::regex_search(xItStart, xItEnd, xResults, xRegEx) )
{
std::cout << "Word, we've searched, is \"" << xResults[0] << "\". It has two \"" << xResults[2] << "\" inside itself.\n";
xItStart = xResults[1].second;
}


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
Word, we've searched, is «boost». It has two «o» inside itself.
Word, we've searched, is «opportunity». It has two «p» inside itself.


regex_replace


Алгоритм используется для замены всех вхождений подстрок, соответсвующих регулярному выражению, на строку заданному в определенном формате. Результат может быть получен через итератор, переданный в качестве аргумента либо как возращаемая строка. Части сроки которые не соответствуют регулярному выражению, копируются в выходнуб строку не измененными, если не задан флаг format_no_copy, который оставляет только заматченные строки в результате. При переданном флаге format_first_only, заменяется только первая подстрока, соответствующая регулярному выражению.
Типично используемый формат:
regex_replace(входящая_строка, регулярное_выражение, форматная_строка, [флаги]).
форматная_строка определяет строку, на которую будет заменятся найденная подстрока.
Она может подчинятся одному из следующих правил синтаксиса:
  • sed флаг:format_sed
  • Perl(по умолчанию) флаг:format_perl
  • Boost-extended флаг:format_all
  • Литеральный, т.е не использует никаких специальных символов. Флаг:format_literal

Пример использования:
std::string xStr("AAAA-12222-BBBBB-44455");
boost::regex xRegEx("(\\w+)-(\\d+)-(\\w+)-(\\d+)");
std::string xFormatString("$1*$2*$3*$4");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
std::cout << "Print string after replace:\n " << boost::regex_replace(xStr, xRegEx, xFormatString, boost::match_default | boost::format_perl) << std::endl;


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
Print string after replace:
AAAA*12222*BBBBB*44455


Вспомогательные средства


regex_iterator


Данный итератор может быть удобен для последовательного поиска вхождений подстроки, соответствующей регулярному выражению. При каждом инкрементировании находится следующая подстрока, с помощью regex_search. При разыменовывании итератора мы получаем объект типа match_results, с помощью которого мы можем получить всю необходимую информацию.
Формат использования: regex_iterator(начальный_итератор, конечный _итератор, регулярное_выражение)
Пример использования:
std::string xStr("AAAA-12222-BBBBB-44455");
boost::regex xRegEx("(\\w|\\d)+");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
boost::sregex_iterator xIt(xStr.begin(), xStr.end(), xRegEx);
boost::sregex_iterator xInvalidIt;
while(xIt != xInvalidIt)
std::cout << *xIt++ << "*";


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
AAAA*12222*BBBBB*44455*


regex_token_iterator


Очень полезный интрумент для разбиаения строки на токены,
Формат использования: regex_token_iterator(начальный_итератор, конечный _итератор, регулярное_выражение, [submatch])
[submatch] используется для указания, как следует интерпретировать токены в строке.
При -1 итератор возвращает часть последовательности, которая не соответствует регулярному выражению. Т.е возвращается либо строка, которая идет после первого совпадения, до начала следующего совпадения(не включая первый символ совпадения). Либо, с начала строки, если начала строки не удовлетворяет регулярному выражению. Т.е при передаче -1, регулярное выражения является разделителем. При 0, каждое смещение итератора(++) дает следующую часть строки которая была “заматчена“, т.е каждый разыменованный итератор является capture строки. При любом положительном числе, в качестве параметра, выбирается capture регулярного выражения соответствующий числу, переданному в качестве параметра. Так же можно передать массив индексов в качестве параметра, тогда итератор будет искать каждый capture согласно индексам в массиве, т.е если массив состоит из {4, 2, 1}, тогда начальный итератор будет указывать на 4 capture, следующий итератор на 2 и т.д. Процесс будет повторятся для всей последовательности, пока не закончатся соответствия для данного регулярного выражения. По дефолту это параметр равен 0.
Разыменованный итератор является объектом класса sub_match.
Примеры использования:
std::string xStr("AAAA-12222-BBBBB-44455");
boost::regex xRegEx("(\\w|\\d)+");
boost::smatch xResults;
std::cout << "==========================Results============================== \n";
boost::sregex_token_iterator xItFull(xStr.begin(), xStr.end(), xRegEx, 0);
boost::sregex_token_iterator xInvalidIt;
std::cout << "Result the same as the regex_iterator: \n";
while(xItFull != xInvalidIt)
std::cout << *xItFull++ << "*";
//Parts of captures
boost::regex xRegEx2("(\\w+)-(\\d+)");
boost::sregex_token_iterator xItFirstCapture(xStr.begin(), xStr.end(), xRegEx2, 1);
std::cout << "\nShow only first captures: \n";
while(xItFirstCapture != xInvalidIt)
std::cout << *xItFirstCapture++ << "*";
//Reverse order
int aIndices[] = {2,1};
boost::sregex_token_iterator xItReverseCapture(xStr.begin(), xStr.end(), xRegEx2, aIndices);
std::cout << "\nShow captures in the reverse order: \n";
while(xItReverseCapture != xInvalidIt)
std::cout << *xItReverseCapture++ << "*";
//Delimiters
boost::regex xRegEx3("(\\w|\\d)+");
boost::sregex_token_iterator xItDelimiters(xStr.begin(), xStr.end(), xRegEx3, -1);
std::cout << "\nShow delimiters: \n";
while(xItDelimiters != xInvalidIt)
std::cout << *xItDelimiters++ << " ";


* This source code was highlighted with Source Code Highlighter.

Вывод:
==========================Results==============================
Result the same as the regex_iterator:
AAAA*12222*BBBBB*44455*
Show only first captures:
AAAA*BBBBB*
Show captures in the reverse order:
12222*AAAA*44455*BBBBB*
Show delimiters:
— — -

Замечание


Любой алгоритм может выбросить исключение типа std::runtime_error в случае если сложность проверки полного соответствия(matching) N элементов начнет превышать О(N^2) или в случае переполнения стека(если Boost.Regex был собран в рекурсивном режиме)

_________

P.S.: ... наглым и безобразным образом утянуто отсюда: Boost это просто. Часть 1. Boost.Regex

P.S.: ... также о boost::regex можно прочитать здесь: BOOST C++: библиотека Regex - регулярные выражения

Сборка библиотеки LibTorrent

прочитайте прежде статью: Development Environment Settings

Сборка с помощью Visual Studio 2005

cd "%LIBRARIES_ROOT%libtorrent"

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-8.0 link=static debug logging=none dht-support=on runtime-link=static asynch-exceptions=on character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-8.0 link=static release logging=none dht-support=on runtime-link=static asynch-exceptions=on character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-8.0 link=static debug logging=none dht-support=on runtime-link=static asynch-exceptions=off character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-8.0 link=static release logging=none dht-support=on runtime-link=static asynch-exceptions=off character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-8.0 link=static debug logging=none dht-support=on runtime-link=shared asynch-exceptions=on character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-8.0 link=static release logging=none dht-support=on runtime-link=shared asynch-exceptions=on character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-8.0 link=static debug logging=none dht-support=on runtime-link=shared asynch-exceptions=off character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 8\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-8.0 link=static release logging=none dht-support=on runtime-link=shared asynch-exceptions=off character-set=unicode openssl=pe

Сборка с помощью Visual Studio 2008
set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-9.0 link=static debug logging=none dht-support=on runtime-link=static asynch-exceptions=on character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-9.0 link=static release logging=none dht-support=on runtime-link=static asynch-exceptions=on character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-9.0 link=static debug logging=none dht-support=on runtime-link=static asynch-exceptions=off character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-9.0 link=static release logging=none dht-support=on runtime-link=static asynch-exceptions=off character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-9.0 link=static debug logging=none dht-support=on runtime-link=shared asynch-exceptions=on character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-9.0 link=static release logging=none dht-support=on runtime-link=shared asynch-exceptions=on character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-9.0 link=static debug logging=none dht-support=on runtime-link=shared asynch-exceptions=off character-set=unicode openssl=pe

set INCLUDE=%BOOST_ROOT%;%LIBRARIES_ROOT%\libtorrent;%LIBRARIES_ROOT%\openssl\include;
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\VSVARS32.BAT"
%LIBRARIES_ROOT%boost-jam-3.1.17\bin.ntx86\bjam --v2 toolset=msvc-9.0 link=static release logging=none dht-support=on runtime-link=shared asynch-exceptions=off character-set=unicode openssl=pe


P.S.: Смотри также:
http://www.rsdn.ru/forum/network/3404574.flat.aspx#3404574
http://www.rsdn.ru/forum/dotnet/3404592.flat.aspx#3404592

Сборка библиотеки OpenSSL

Сборка с помощью Visual Studio 2005:
set OPTS=no-asm

perl Configure VC-WIN32
perl util\mkfiles.pl >MINFO
perl util\mk1mf.pl %OPTS% debug VC-WIN32 >d32.mak
perl util\mk1mf.pl %OPTS% VC-WIN32 >32.mak
perl util\mk1mf.pl %OPTS% debug dll VC-WIN32 >d32dll.mak
perl util\mk1mf.pl %OPTS% dll VC-WIN32 >32dll.mak
perl util\mkdef.pl 32 libeay > ms\libeay32.def
perl util\mkdef.pl 32 ssleay > ms\ssleay32.def

rem set PATH=%MSSDK6DIR%\VC\Bin;%PATH%
call "c:\Program Files\Microsoft Visual Studio 8\Common7\Tools\vsvars32.bat"

nmake -f d32.mak
@if errorlevel 1 goto end
nmake -f 32.mak
@if errorlevel 1 goto end
nmake -f d32dll.mak
@if errorlevel 1 goto end
nmake -f 32dll.mak

:end

Сборка с помощью Visual Studio 2008:
set OPTS=no-asm

perl Configure VC-WIN32
perl util\mkfiles.pl >MINFO
perl util\mk1mf.pl %OPTS% debug VC-WIN32 >d32.mak
perl util\mk1mf.pl %OPTS% VC-WIN32 >32.mak
perl util\mk1mf.pl %OPTS% debug dll VC-WIN32 >d32dll.mak
perl util\mk1mf.pl %OPTS% dll VC-WIN32 >32dll.mak
perl util\mkdef.pl 32 libeay > ms\libeay32.def
perl util\mkdef.pl 32 ssleay > ms\ssleay32.def

rem set PATH=%MSSDK6DIR%\VC\Bin;%PATH%
call "c:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat"

nmake -f d32.mak
@if errorlevel 1 goto end
nmake -f 32.mak
@if errorlevel 1 goto end
nmake -f d32dll.mak
@if errorlevel 1 goto end
nmake -f 32dll.mak

:end

Сборка с помощью Vista SDK
set OPTS=no-asm

perl Configure VC-WIN32
perl util\mkfiles.pl >MINFO
perl util\mk1mf.pl %OPTS% debug VC-WIN32 >d32.mak
perl util\mk1mf.pl %OPTS% VC-WIN32 >32.mak
perl util\mk1mf.pl %OPTS% debug dll VC-WIN32 >d32dll.mak
perl util\mk1mf.pl %OPTS% dll VC-WIN32 >32dll.mak
perl util\mkdef.pl 32 libeay > ms\libeay32.def
perl util\mkdef.pl 32 ssleay > ms\ssleay32.def

@SET VSINSTALLDIR=c:\Program Files\Microsoft Visual Studio 8
@SET VCINSTALLDIR=%VSINSTALLDIR%\VC
@SET FrameworkDir=c:\WINDOWS\Microsoft.NET\Framework
@SET FrameworkVersion=v2.0.50727
@SET FrameworkSDKDir=%VSINSTALLDIR%\SDK\v2.0
@set DevEnvDir=%VSINSTALLDIR%\Common7\IDE
@set PATH=%MSSDK6DIR%\VC\Bin;%VSINSTALLDIR%\Common7\IDE;%VCINSTALLDIR%\BIN;%VSINSTALLDIR%\Common7\Tools;%VSINSTALLDIR%\Common7\Tools\bin;%VCINSTALLDIR%\PlatformSDK\bin;%VSINSTALLDIR%\SDK\v2.0\bin;c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;%VCINSTALLDIR%\VCPackages;%PATH%
@set INCLUDE=%MSSDK6DIR%\Include;%MSSDK6DIR%\VC\INCLUDE;%VCINSTALLDIR%\ATLMFC\INCLUDE;%VCINSTALLDIR%\INCLUDE;%VCINSTALLDIR%\PlatformSDK\include;%VSINSTALLDIR%\SDK\v2.0\include;%INCLUDE%
@set LIB=%MSSDK6DIR%\Lib;%MSSDK6DIR%\VC\LIB;%VCINSTALLDIR%\ATLMFC\LIB;%VCINSTALLDIR%\LIB;%VCINSTALLDIR%\PlatformSDK\lib;%VSINSTALLDIR%\SDK\v2.0\lib;%LIB%
@set LIBPATH=c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727;%VCINSTALLDIR%\ATLMFC\LIB

nmake -f d32.mak
@if errorlevel 1 goto end
nmake -f 32.mak
@if errorlevel 1 goto end
nmake -f d32dll.mak
@if errorlevel 1 goto end
nmake -f 32dll.mak

:end

Скрипт для инсталла:
set OPENSSL_ROOT=%~dp0
set TOOLSET=%1%

cd %OPENSSL_ROOT%
call :CreateDirectories
call :MoveOutFiles
call :RemoveOldDirectories
goto end

:CreateDirectories
mkdir lib
cd ./lib
mkdir %TOOLSET%
cd ./%TOOLSET%
mkdir debug
cd ./debug
mkdir shared
mkdir static
cd ..
mkdir release
cd ./release
mkdir shared
mkdir static
cd ..
exit /b

:MoveOutFiles
cd %OPENSSL_ROOT%
move %OPENSSL_ROOT%out32\* %OPENSSL_ROOT%lib\%TOOLSET%\release\static\
move %OPENSSL_ROOT%out32dll\* %OPENSSL_ROOT%lib\%TOOLSET%\release\shared\
move %OPENSSL_ROOT%out32.dbg\* %OPENSSL_ROOT%lib\%TOOLSET%\debug\static\
move %OPENSSL_ROOT%out32dll.dbg\* %OPENSSL_ROOT%lib\%TOOLSET%\debug\shared\
cd %OPENSSL_ROOT%
exit /b

:RemoveOldDirectories
del /S /Q %OPENSSL_ROOT%out32\*
del /S /Q %OPENSSL_ROOT%out32dll\*
del /S /Q %OPENSSL_ROOT%out32.dbg\*
del /S /Q %OPENSSL_ROOT%out32dll.dbg\*
del /S /Q %OPENSSL_ROOT%out32
del /S /Q %OPENSSL_ROOT%out32dll
del /S /Q %OPENSSL_ROOT%out32.dbg
del /S /Q %OPENSSL_ROOT%out32dll.dbg
del /S /Q %OPENSSL_ROOT%tmp32\*
del /S /Q %OPENSSL_ROOT%tmp32dll\*
del /S /Q %OPENSSL_ROOT%tmp32.dbg\*
del /S /Q %OPENSSL_ROOT%tmp32dll.dbg\*
del /S /Q %OPENSSL_ROOT%tmp32
del /S /Q %OPENSSL_ROOT%tmp32dll
del /S /Q %OPENSSL_ROOT%tmp32.dbg
del /S /Q %OPENSSL_ROOT%tmp32dll.dbg
exit /b

:end

P.S.: Возможно после конфигурации придётся слегуа поправить make-файлы, поскольку собирают они библиотеку не во всех возможных конфигурациях. Например когда собирается статическая версия OpenSSL, то с С++ CRT она линкуется статически, если же собирается динамическая версия OpenSSL, то она с С++ библиотекой линкуется динамически. Скажем если вы хотите собрать статическую версию OpenSSL, а запользовать её в проекте, линкующимся с CRT динамически, вот тогда и придётся править фалики.


Смотри также: Compiling OpenSSL for Windows, Linux, and Macintosh