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

1 апр. 2009 г.

Base64 Encoder

Велосипед отечественного производства для кодирования бинарных данных в текст с кодировкой Base64 и обратно.

Файл check_helpers.hpp
//------------------------------------------------------------------------
#ifndef __CHERCKHELPERS_HEADER_INCLUDED__
#define __CHERCKHELPERS_HEADER_INCLUDED__
//------------------------------------------------------------------------
#include <boost/type_traits.hpp>
//------------------------------------------------------------------------
template<class IterTypeT>
struct IsRandomAccesIterator
{
    static const bool value = false;
};
template<>
struct IsRandomAccesIterator<std::random_access_iterator_tag>
{
    static const bool value = true;
};
//------------------------------------------------------------------------
#define CHARTYPE_SOURCE_SEQUENCE_CHECK(stl_container_t) \
    BOOST_STATIC_ASSERT ( \
    boost::is_fundamental<typename stl_container_t::value_type>::value ); \
    BOOST_STATIC_ASSERT ( \
    sizeof(typename stl_container_t::value_type) == static_cast<std::size_t>(1) );
//------------------------------------------------------------------------
#define CHARTYPE_SOURCE_ITERATOR_CHECK(iterator_t) \
    BOOST_STATIC_ASSERT ( \
        boost::is_fundamental<typename std::iterator_traits<iterator_t>::value_type>::value ); \
    BOOST_STATIC_ASSERT ( \
        sizeof(typename std::iterator_traits<iterator_t>::value_type) == static_cast<std::size_t>(1) );
//------------------------------------------------------------------------
#define RANDOM_ACCESS_ITERATOR_CHECK(iterator_t) \
    BOOST_STATIC_ASSERT ( \
    IsRandomAccesIterator< \
        typename std::iterator_traits<iterator_t>::iterator_category>::value );
//------------------------------------------------------------------------
//------------------------------------------------------------------------
#endif // __CHERCKHELPERS_HEADER_INCLUDED__
//------------------------------------------------------------------------

Файл base64_conv.h
//------------------------------------------------------------------------
#ifndef __BASE64_CONVERTER_HEADER_INCLUDED__
#define __BASE64_CONVERTER_HEADER_INCLUDED__
//------------------------------------------------------------------------
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/type_traits.hpp>
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
#include "check_helpers.hpp"
//------------------------------------------------------------------------
class base64_conv : private boost::noncopyable
{
public:
    typedef std::vector<char>           buffer_t;
    typedef boost::shared_ptr<buffer_t> p_buffer_t;

public:
    base64_conv();
    ~base64_conv();

    template<typename stl_container_t>
    void to_base64 ( const stl_container_t & sourse_data )
    {
        // This code can not be compiled, because a sequence is contained
        // by a wrong type; need sequence which contained by predefined char type
        CHARTYPE_SOURCE_SEQUENCE_CHECK(stl_container_t);

        std::vector<typename stl_container_t::value_type> tmp_buffer (
            sourse_data.begin(), sourse_data.end() );
        to_base64(tmp_buffer);
    }
    
    template<typename char_t, typename allocator_t>
    void to_base64 (
        const std::vector<char_t, allocator_t> & sourse_data )
    {
        if ( sourse_data.empty() )
        {
            p_buffer->clear();
            return;
        }

        to_base64_for_vector_or_string ( sourse_data.begin(), sourse_data.end() );
    }

    template<typename char_t, typename char_traits_t, typename allocator_t>
    void to_base64(
        const std::basic_string<char_t, char_traits_t, allocator_t> & sourse_data )
    {
        if ( sourse_data.empty() )
        {
            p_buffer->clear();
            return;
        }

        to_base64_for_vector_or_string ( sourse_data.begin(), sourse_data.end() );
    }

    template<typename stl_container_t>
    void from_base64( const stl_container_t & sourse_data )
    {
        // This code can not be compiled, because a sequence is contained
        // by a wrong type; need sequence which contained by predefined char type
        CHARTYPE_SOURCE_SEQUENCE_CHECK(stl_container_t);

        std::vector<typename stl_container_t::value_type> tmp_buffer (
            sourse_data.begin(), sourse_data.end() );
        from_base64(tmp_buffer);
    }

    template<typename char_t, typename allocator_t>
    void from_base64 (
        const std::vector<char_t, allocator_t> & sourse_data )
    {
        if ( sourse_data.empty() )
        {
            p_buffer->clear();
            return;
        }

        from_base64_for_vector_or_string (
            sourse_data.begin(), sourse_data.end() );
    }

    template<typename char_t, typename char_traits_t, typename allocator_t>
    void from_base64 (
        const std::basic_string<char_t, char_traits_t, allocator_t> & sourse_data )
    {
        if ( sourse_data.empty() )
        {
            p_buffer->clear();
            return;
        }

        from_base64_for_vector_or_string (
            sourse_data.begin(), sourse_data.end() );
    }

    p_buffer_t get_result();
    void get_result ( std::string & out_str );
    void get_result ( std::vector<char> & out_buffer );

private:
    template<typename in_rait_t>
    void to_base64_for_vector_or_string (
        const in_rait_t it_begin, const in_rait_t it_end )
    {
        // This code can not be compiled, because a sequence is contained
        // by a wrong type; need sequence which contained by predefined char type
        CHARTYPE_SOURCE_ITERATOR_CHECK(in_rait_t);

        // This code can not be compiled, because iterator_category of in_rait_t
        // is not equal std::random_access_iterator_tag
        RANDOM_ACCESS_ITERATOR_CHECK(in_rait_t);

        std::size_t data_length = std::distance(it_begin, it_end);
        if ( data_length <= 0 )
        {
            p_buffer->clear();
            return;
        }

        const char * p_data_begin = reinterpret_cast<const char*>( & * it_begin );
        do_encode_to_base64 ( p_data_begin, data_length );
    }

    template<typename in_rait_t>
    void from_base64_for_vector_or_string (
        const in_rait_t it_begin, const in_rait_t it_end )
    {
        // This code can not be compiled, because a sequence is contained
        // by a wrong type; need sequence which contained by predefined char type
        CHARTYPE_SOURCE_ITERATOR_CHECK(in_rait_t);

        // This code can not be compiled, because iterator_category of in_rait_t
        // is not equal std::random_access_iterator_tag
        RANDOM_ACCESS_ITERATOR_CHECK(in_rait_t);

        std::size_t data_length = std::distance(it_begin, it_end);
        if ( data_length <= 0 )
        {
            p_buffer->clear();
            return;
        }

        const char * p_data_begin = reinterpret_cast<const char*>( & * it_begin );
        do_decode_from_base64 ( p_data_begin, data_length );
    }

    void do_encode_to_base64 (
        const char * sourse_data,
        std::size_t sourse_data_size );

    void do_decode_from_base64 (
        const char * sourse_data,
        std::size_t sourse_data_size );

private:
    enum { MAX_LINE_LENGTH = 72 };
    p_buffer_t p_buffer;
};
//------------------------------------------------------------------------
#endif // __BASE64_CONVERTER_HEADER_INCLUDED__
//------------------------------------------------------------------------

Файл base64_conv.cpp
//------------------------------------------------------------------------
#include "stdafx.h"
#include "base64_conv.h"
//------------------------------------------------------------------------
namespace {

// Translation Table as described in RFC1113
//------------------------------------------------------------------------
const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

// Translation Table to decode (created by author)
//------------------------------------------------------------------------
const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";

// encode 3 8-bit binary bytes as 4 '6-bit' characters
//------------------------------------------------------------------------
void encodeblock ( unsigned char in[3], unsigned char out[4], int len )
{
    out[0] = cb64[ in[0] >> 2 ];
    out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
    out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
    out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
}

// decode 4 '6-bit' characters into 3 8-bit binary bytes
//------------------------------------------------------------------------
void decodeblock( unsigned char in[4], unsigned char out[3] )
{   
    out[ 0 ] = (unsigned char ) (in[0] << 2 | in[1] >> 4);
    out[ 1 ] = (unsigned char ) (in[1] << 4 | in[2] >> 2);
    out[ 2 ] = (unsigned char ) (((in[2] << 6) & 0xc0) | in[3]);
}

// base64 encode a sequence adding padding and line breaks as per spec.
//------------------------------------------------------------------------
void encode (
    const char * in_sequence,
    std::size_t in_length,
    std::vector<char> & out_sequence,
    int line_size )
{
    unsigned char in[3], out[4];
    int i, len, blocks_out = 0;

    out_sequence.clear();
    out_sequence.reserve(in_length * 4/3);

    const char * it_begin   = in_sequence;
    const char * it_end     = in_sequence + in_length;

    while ( it_begin < it_end )
    {
        len = 0;
        for ( i = 0; i < 3; i++ )
        {
            if ( it_begin < it_end )
            {
                in[i] = static_cast<unsigned char>(*it_begin++);
                len++;
            }
            else
            {
                in[i] = 0;
            }
        }
        if ( len )
        {
            encodeblock( in, out, len );
            for( i = 0; i < 4; i++ )
            {
                out_sequence.push_back(out[i]);
            }
            blocks_out++;
        }
        if ( blocks_out >= (line_size/4) || ( it_begin == it_end ) )
        {
            if ( blocks_out )
            {
                out_sequence.push_back('\r');
                out_sequence.push_back('\n');
            }
            blocks_out = 0;
        }
    }
}

// decode a base64 encoded sequence discarding padding, line breaks and noise
//------------------------------------------------------------------------
void decode (
    const char * in_sequence,
    std::size_t in_length,
    std::vector<char> & out_sequence )
{
    unsigned char in[4], out[3], v;
    int i, len;

    out_sequence.clear();
    out_sequence.reserve(in_length * 3/4);

    const char * it_begin   = in_sequence;
    const char * it_end     = in_sequence + in_length;

    while( it_begin < it_end )
    {
        for( len = 0, i = 0; i < 4 && (it_begin < it_end); i++ )
        {
            v = 0;
            for ( ; (it_begin < it_end) && v == 0 ; ++it_begin )
            {
                v = static_cast<unsigned char>(*it_begin);
                v = static_cast<unsigned char>((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]);
                if( v )
                {
                    v = static_cast<unsigned char>((v == '$') ? 0 : v - 61);
                }
            }
            if ( v )
            {
                len++;
                in[i] = static_cast<unsigned char>(v - 1);
            }
            else
            {
                in[i] = 0;
            }
        }
        if( len )
        {
            decodeblock( in, out );
            for( i = 0; i < len - 1; i++ )
            {
                out_sequence.push_back(out[i]);
            }
        }
    }
}

}
//------------------------------------------------------------------------
base64_conv::base64_conv()
    : p_buffer ( new std::vector<char> )
{
};
//------------------------------------------------------------------------
base64_conv::~base64_conv()
{
};
//------------------------------------------------------------------------
void base64_conv::do_encode_to_base64 (
    const char * sourse_data, std::size_t sourse_data_size )
{
    encode ( sourse_data, sourse_data_size, *p_buffer, MAX_LINE_LENGTH );
}
//------------------------------------------------------------------------
void base64_conv::do_decode_from_base64 (
    const char * sourse_data, std::size_t sourse_data_size )
{
    decode ( sourse_data, sourse_data_size, *p_buffer );
}
//------------------------------------------------------------------------
base64_conv::p_buffer_t base64_conv::get_result()
{
    return p_buffer;
}
//------------------------------------------------------------------------
void base64_conv::get_result (
    std::string & out_str )
{
    std::string ret_val;
    ret_val.reserve(p_buffer->size());
    std::copy (
        p_buffer->begin(),
        p_buffer->end(),
        std::back_inserter(ret_val) );
    std::swap(out_str, ret_val);
}
//------------------------------------------------------------------------
void base64_conv::get_result (
    std::vector<char> & out_buffer )
{
    std::swap(out_buffer, *p_buffer);
    p_buffer->clear();
}
//------------------------------------------------------------------------

Использование 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