<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="W/&quot;A0ANR34-fSp7ImA9WxNUFko.&quot;"><id>tag:blogger.com,1999:blog-22921254</id><updated>2009-11-08T14:29:56.055+03:00</updated><title>Raider's Programming Blog</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/" /><link rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>137</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><link rel="self" href="http://feeds.feedburner.com/RaidersProgrammingBlog" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry gd:etag="W/&quot;DkUDSH87eCp7ImA9WxNUEUU.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-6224091986558861327</id><published>2009-11-02T20:46:00.003+03:00</published><updated>2009-11-02T20:51:19.100+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-02T20:51:19.100+03:00</app:edited><title>Unicode: life-hack</title><content type="html">Мой обычный сценарий при создании новых файлов в проекте:&lt;br /&gt;1. right click в Windows explorer на папке, где нужно создать файл&lt;br /&gt;2. выбор Create/"New Text File.txt"&lt;br /&gt;3. ввод имени файла&lt;br /&gt;4. right click на файле&lt;br /&gt;5. SVN/Add&lt;br /&gt;6. Перетаскивание файла в IDE&lt;br /&gt;&lt;br /&gt;Небольшой лайф-хак позволяет сразу создавать файлы в UTF-8:&lt;br /&gt;&lt;img src="http://sraider.2x4.ru/raider/blogger/2009/ShellNewUTF8.png"&gt;&lt;br /&gt;Файл создается с BOM для UTF-8.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-6224091986558861327?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/6224091986558861327/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=6224091986558861327" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/6224091986558861327?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/6224091986558861327?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/11/unicode-life-hack.html" title="Unicode: life-hack" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry gd:etag="W/&quot;CkAARH4yfCp7ImA9WxNVEU4.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-4226808316767578597</id><published>2009-10-21T16:57:00.005+04:00</published><updated>2009-10-21T17:12:25.094+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-21T17:12:25.094+04:00</app:edited><title>Yo!</title><content type="html">Отсортировать строки по алфавиту очень просто - все необходимые сведения о порядке символов есть в классе std::locale. Остается только надеяться что std::locale не подведет. А, как оказалось, она может. Вот пример:&lt;br /&gt;&lt;pre&gt;struct lexicographical_order&lt;br /&gt;{&lt;br /&gt;    std::locale Locale;&lt;br /&gt;&lt;br /&gt;    lexicographical_order(std::locale Locale) : Locale(Locale)&lt;br /&gt;    {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    bool operator()(std::wstring const &amp;lhs, std::wstring const &amp;rhs) const&lt;br /&gt;    {&lt;br /&gt;        return boost::ilexicographical_compare(lhs, rhs, Locale);&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int main()&lt;br /&gt;{&lt;br /&gt;    std::vector&lt;std::wstring&gt; v;&lt;br /&gt;    v.push_back(L"яблоко");&lt;br /&gt;    v.push_back(L"апельсин");&lt;br /&gt;    v.push_back(L"банан");&lt;br /&gt;    v.push_back(L"персик");&lt;br /&gt;    v.push_back(L"ёжик");&lt;br /&gt; &lt;br /&gt;    std::locale Locale(""); // используем текущую локаль системы&lt;br /&gt;    std::cout &lt;&lt; Locale.name() &lt;&lt; std::endl;&lt;br /&gt;    std::sort(v.begin(), v.end(), lexicographical_order(Locale));&lt;br /&gt;&lt;br /&gt;    _setmode(_fileno(stdout), _O_U16TEXT);&lt;br /&gt;    std::copy(v.begin(), v.end(), std::ostream_iterator&lt;std::wstring, wchar_t&gt;(std::wcout, L"\n"));&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Под русской Windows получаем:&lt;pre&gt;Russian_Russia.1251&lt;br /&gt;ёжик&lt;br /&gt;апельсин&lt;br /&gt;банан&lt;br /&gt;персик&lt;br /&gt;яблоко&lt;/pre&gt;&lt;br /&gt;Буква "ё" явно пользуется у данной локали большей популярностью, чем все остальные. Ну прям хоть свою локаль пиши...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-4226808316767578597?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/4226808316767578597/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=4226808316767578597" title="Комментарии: 6" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/4226808316767578597?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/4226808316767578597?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/10/yo.html" title="Yo!" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total></entry><entry gd:etag="W/&quot;D0UESXw_eSp7ImA9WxNSFEo.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-1242109153950030624</id><published>2009-08-28T19:41:00.003+04:00</published><updated>2009-08-28T20:00:08.241+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-08-28T20:00:08.241+04:00</app:edited><title>Boost Tuple Serialization</title><content type="html">Сложно поверить, но в Boost-е нет сериализации для tuple.&lt;br /&gt;&lt;br /&gt;Видимо вся фигня в том, что при сериализации кортежей нужно написать сериализацию для кортежей 0..N элементов, а это можно сделать:&lt;br /&gt;а) руками: написать вручную сериализацию для 0, 1, 2 и т.д. элементов, что совсем не кошерно;&lt;br /&gt;б) препроцессором: использовать boost preprocessor - тоже считается не очень кошерно;&lt;br /&gt;в) компилятор: но тут вся загвоздка как я понял в том, что это нельзя сделать используя публичный интерфейс tuple, можно только зная особенности реализации (details), что тоже вроде бы не очень кошерно.&lt;br /&gt;&lt;br /&gt;В результате был выбран списоб "г" - вообще не писать сериализацию для tuple. Что я считаю полным бредом.&lt;br /&gt;&lt;br /&gt;PS. Если что - есть готовая &lt;a href="http://uint32t.blogspot.com/2008/03/update-serializing-boosttuple-using.html"&gt;реализация через препроцессор&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-1242109153950030624?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/1242109153950030624/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=1242109153950030624" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/1242109153950030624?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/1242109153950030624?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/08/boost-tuple-serialization.html" title="Boost Tuple Serialization" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry gd:etag="W/&quot;A08HSXk5fip7ImA9WxJUGUk.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-818165887949434612</id><published>2009-07-19T00:24:00.007+04:00</published><updated>2009-07-19T00:43:58.726+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-19T00:43:58.726+04:00</app:edited><title>Sorting by several fields</title><content type="html">Как вы сортируете по нескольким критериям?&lt;br /&gt;&lt;pre&gt;struct foo&lt;br /&gt;{&lt;br /&gt;    A a; B b; C c; D d;&lt;br /&gt;    X x; Y y;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// нужно отсортировать по a, затем по b, затем по c, ну а затем еще и по d&lt;br /&gt;// по x и y сортировать не нужно&lt;br /&gt;bool s(foo const &amp;l, foo const &amp;r)&lt;br /&gt;{&lt;br /&gt;    if (l.a &amp;lt; r.a) return true;&lt;br /&gt;    if (l.a &amp;gt; r.a) return false;&lt;br /&gt;    if (l.b &amp;lt; r.b) return true;&lt;br /&gt;    if (l.b &amp;gt; r.b) return false;&lt;br /&gt;    if (l.c &amp;lt; r.c) return true;&lt;br /&gt;    if (l.c &amp;gt; r.c) return false;&lt;br /&gt;    return l.d &amp;lt; r.d;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;std::vector&amp;lt;foo&amp;gt; f;&lt;br /&gt;std::sort(f.begin(), f.end(), s);&lt;/pre&gt;&lt;br /&gt;Признаюсь, я примерно так раньше и сортировал. Хотя по сути-то [a,b,c,d] - это ж по-сути обычный &lt;a href="http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D1%80%D1%82%D0%B5%D0%B6"&gt;кортеж&lt;/a&gt;, а для них уже есть готовые библиотеки. И операции сравнения кортежей там должны быть, в этих библиотеках. Нужно просто загнать &lt;code&gt;l&lt;/code&gt; и &lt;code&gt;r&lt;/code&gt; в кортежи (только для скорости - не в &lt;code&gt;[A,B,C,D]&lt;/code&gt;, а в &lt;code&gt;[const&amp;A,const&amp;B,const&amp;C,const&amp;D]&lt;/code&gt;), и сравнить:&lt;br /&gt;&lt;pre&gt;#include &amp;lt;boost/tuple/tuple_comparison.hpp&amp;gt;&lt;br /&gt;&lt;br /&gt;bool s(foo const &amp;l, foo const &amp;r)&lt;br /&gt;{&lt;br /&gt;    return boost::tie(l.a, l.b, l.c, l.d) &amp;lt; boost::tie(r.a, r.b, r.c, r.d);&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-818165887949434612?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/818165887949434612/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=818165887949434612" title="Комментарии: 4" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/818165887949434612?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/818165887949434612?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/07/sorting-by-several-fields.html" title="Sorting by several fields" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></entry><entry gd:etag="W/&quot;A0MNQHs4fip7ImA9WxJUF0s.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-3257485818160660334</id><published>2009-07-16T22:31:00.002+04:00</published><updated>2009-07-16T22:38:11.536+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-07-16T22:38:11.536+04:00</app:edited><title>Static member definition</title><content type="html">Интересно, что статический член класса нельзя определить прямо в декларации класса:&lt;pre&gt;class foo&lt;br /&gt;{&lt;br /&gt;    static std::string s('hello world');&lt;br /&gt;};&lt;/pre&gt;А иногда сильно этого хочется - например в декларации шаблона. Или просто в каком-нибудь header-only классе. Самое интересное, что внутри функции такое провернуть можно. Поэтому можно сделать финт ушами - сделать доступ к статическому члену через member-функцию:&lt;pre&gt;class foo&lt;br /&gt;{&lt;br /&gt;    static std::string &amp;s()&lt;br /&gt;    {&lt;br /&gt;        static std::string impl('hello world');&lt;br /&gt;        return impl;&lt;br /&gt;    }&lt;br /&gt;};&lt;/pre&gt;Синтаксис при обращении немного поменяется - &lt;code&gt;s()&lt;/code&gt; вместо &lt;code&gt;s&lt;/code&gt;, но часто оно того стоит.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-3257485818160660334?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/3257485818160660334/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=3257485818160660334" title="Комментарии: 12" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/3257485818160660334?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/3257485818160660334?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/07/static-member-definition.html" title="Static member definition" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">12</thr:total></entry><entry gd:etag="W/&quot;CUMCRXk8eSp7ImA9WxJWF00.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-458847073921618068</id><published>2009-06-23T00:15:00.005+04:00</published><updated>2009-06-23T00:44:24.771+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-23T00:44:24.771+04:00</app:edited><title>Fast parsing</title><content type="html">Чем вы парсите всякую "мелочёвку" типа целочисленных значений и прочее? Самописными парсерами с приправами вроде boost::tokenizer, boost::lexical_cast или даже atoi, etc.? Я обычно использовал boost::regex + lexical_cast. А тут недавно посмотрел презентацию &lt;a href="http://spirit.sourceforge.net/"&gt;Spirit&lt;/a&gt;-а с BoostCon-а, даже немного проникся. Раньше как-то я к Спириту более равнодушен был.&lt;br /&gt;&lt;br /&gt;Описывать грамматику на C++ и потом давать компилятору [несколько минут] это компилировать - это, конечно, большой изврат. Но весьма забавно. И, говорят, работает потом очень быстро. Даже если нужно просто число распарсить - &lt;a href="http://groups.google.com/group/boost-list/browse_thread/thread/fb690fe1fb38a7f2/d2fc6f2436492a4a"&gt;говорят&lt;/a&gt; быстрее всех парсит.&lt;pre&gt;std::string buffer("1234");&lt;br /&gt;int value = 0;&lt;br /&gt;&lt;br /&gt;using namespace boost::spirit;&lt;br /&gt;bool r = qi::parse(buffer.begin(), buffer.end(), int_, value);&lt;br /&gt;assert(r &amp;&amp; value == 1234);&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-458847073921618068?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/458847073921618068/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=458847073921618068" title="Комментарии: 10" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/458847073921618068?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/458847073921618068?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/06/fast-parsing.html" title="Fast parsing" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">10</thr:total></entry><entry gd:etag="W/&quot;D0AHQ3c5eyp7ImA9WxJXGU4.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-2467619801791641093</id><published>2009-06-14T03:16:00.003+04:00</published><updated>2009-06-14T03:28:52.923+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-14T03:28:52.923+04:00</app:edited><title>Visual Studio debug visualizers</title><content type="html">Начиная с Visual Studio 2005 можно писать свои правила для отображения значений в отладчике. Более подробнее об этом можно почитать &lt;a href="http://www.virtualdub.org/blog/pivot/entry.php?id=120"&gt;в блоге virtualdub&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Правила для Boost-овских типов можно найти на &lt;a href="https://svn.boost.org/trac/boost/wiki/DebuggerVisualizers"&gt;Boost Wiki&lt;/a&gt;. Единственное, не нашел там то, что искал &amp;mdash; для boost::variant. Набросал на коленке:&lt;br /&gt;&lt;pre&gt;boost::variant&lt;*,*,*,*,*,*,*,*,*,*,*&gt; {&lt;br /&gt;    preview (&lt;br /&gt;        #(&lt;br /&gt;            #switch($c.which_)&lt;br /&gt;            #case 0 ( *($T1 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 1 ( *($T2 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 2 ( *($T3 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 3 ( *($T4 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 4 ( *($T5 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 5 ( *($T6 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 6 ( *($T7 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 7 ( *($T8 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 8 ( *($T9 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 9 ( *($T10 *)&amp;($c.storage_.data_) )&lt;br /&gt;        )&lt;br /&gt;    )&lt;br /&gt;    children&lt;br /&gt;    (&lt;br /&gt;        #(&lt;br /&gt;        value:&lt;br /&gt;            #switch($c.which_)&lt;br /&gt;            #case 0 ( *($T1 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 1 ( *($T2 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 2 ( *($T3 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 3 ( *($T4 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 4 ( *($T5 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 5 ( *($T6 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 6 ( *($T7 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 7 ( *($T8 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 8 ( *($T9 *)&amp;($c.storage_.data_) )&lt;br /&gt;            #case 9 ( *($T10 *)&amp;($c.storage_.data_) )&lt;br /&gt;        )&lt;br /&gt;    )&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-2467619801791641093?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/2467619801791641093/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=2467619801791641093" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/2467619801791641093?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/2467619801791641093?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/06/visual-studio-debug-visualizers.html" title="Visual Studio debug visualizers" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry gd:etag="W/&quot;CkYGSHs6fyp7ImA9WxJQEU0.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-189703349703608512</id><published>2009-05-23T21:10:00.003+04:00</published><updated>2009-05-23T21:35:29.517+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-23T21:35:29.517+04:00</app:edited><title>Container with a preallocated buffer</title><content type="html">STL-ные контейнеры - замечательная вещь, вот только хранят они все на куче. А иногда ведь так хочется, чтобы они использовали какой-нибудь буфер внутри объекта. Что-то типа такого:&lt;pre&gt;template &amp;lt;typename T, size_t N&amp;gt;&lt;br /&gt;struct Container&lt;br /&gt;{&lt;br /&gt;   size_t size; // сколько элементов в контейнере&lt;br /&gt;   T storage[N]; // заранее выделенный буфер для объектов&lt;br /&gt;};&lt;/pre&gt;Такой контейнер будет полезен, например, когда создается временный контейнер на стеке, максимальное количество элементов в нем предполагается известным. Или, например, когда нам нужен контейнер на небольшое нефиксированное количество элементов. Ну правда, стоит ли из-за контейнера на 1-2 небольших элемента заниматься выделением-освобождением памяти на куче (что довольно затратно по времени)?&lt;br /&gt;&lt;br /&gt;Есть похожие на это контейнеры:&lt;br /&gt;- boost.array, но он хранит только фиксированное количество элементов, для переменного количества не подойдет&lt;br /&gt;- boost.optional - это почти то, что нужно, но если нужно хранить не более 1 элемента ;)&lt;br /&gt;&lt;br /&gt;Нужный мне контейнер есть в стандартной библиотеке C++, которая поставляется с Visual C++, называется он basic_string. В этой реализации (возможно и в каких-то других) он имеет буфер на небольшое количество символов - чтобы для небольших по размеру строк не лазить на кучу. Однако, размер буфера там не регулируется "снаружи", да и нет смысла полагаться на конкретную реализацию - в другой стандартной библиотеке может быть все по-другому, да и эта может "изменить" в любой момент.&lt;br /&gt;&lt;br /&gt;В boost-е планируется к review нужный контейнер - &lt;a href="http://www.cs.aau.dk/~nesotto/boost/auto_buffer.zip"&gt;&lt;b&gt;auto_buffer&lt;/b&gt;&lt;/a&gt;. Его еще немного допилят по интерфейсу, но пользоваться уже можно смело. В добавок он может, как и basic_string, расти в кучу, если нужно.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-189703349703608512?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/189703349703608512/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=189703349703608512" title="Комментарии: 5" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/189703349703608512?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/189703349703608512?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/05/container-with-preallocated-buffer.html" title="Container with a preallocated buffer" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total></entry><entry gd:etag="W/&quot;C0cMQn48cSp7ImA9WxJSFk8.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-708497786386191462</id><published>2009-05-06T18:42:00.001+04:00</published><updated>2009-05-06T18:44:43.079+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-06T18:44:43.079+04:00</app:edited><title>Presentations from BoostCon 2009</title><content type="html">Доступны для скачивания &lt;a href="http://www.boostcon.com/community/wiki/show/private/2009/"&gt;презентации с BoostCon 2009&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-708497786386191462?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="related" href="http://www.boostcon.com/community/wiki/show/private/2009/" title="Presentations from BoostCon 2009" /><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/708497786386191462/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=708497786386191462" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/708497786386191462?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/708497786386191462?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/05/presentations-from-boostcon-2009.html" title="Presentations from BoostCon 2009" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;CEYBSH49fyp7ImA9WxVaEk8.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-7793881145739472891</id><published>2009-04-08T23:56:00.003+04:00</published><updated>2009-04-09T00:22:39.067+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-09T00:22:39.067+04:00</app:edited><title>Range concatenation</title><content type="html">В библиотеках для работы с итераторами (Boost.Iterators, Boost.Range, RangeEx) куча полезных вещей, но почему-то нигде не нашел механизма "склеить" два диапазона в один. Такого, чтобы можно было сделать вот так:&lt;pre&gt;template &amp;lt;class Range&amp;gt; void process_range(Range const &amp;);&lt;br /&gt;&lt;br /&gt;std::vector&amp;lt;foo&amp;gt; v = ...;&lt;br /&gt;std::list&amp;lt;foo&amp;gt; l = ...;&lt;br /&gt;process_range(&lt;b&gt;concat(v, l)&lt;/b&gt;);&lt;/pre&gt;Вот разбить диапазон на части - пожалуйста, перемещать значения в случайном порядке - да не вопрос, а вот склеить - нифига.&lt;br /&gt;&lt;br /&gt;Можно, конечно, создать вектор из ссылок на &lt;code&gt;foo&lt;/code&gt;, заполнить его ссылками на обекты из первого и второго диапазона, но как-то не спортивно: во-первых само выделение динамической памяти для вектора - не слишком быстрая операция, плюс потом еще ссылки туда копировать. Пришлось написать &lt;a href="http://sraider.2x4.ru/raider/blogger/2009/concat.zip"&gt;свой велосипед&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;С использованием &lt;code&gt;boost::variant&lt;/code&gt; все получилось достаточно компактно, хотя не максимально эффективно:&lt;pre&gt;template &amp;lt;class TIterator1, class TIterator2, ...&amp;gt;&lt;br /&gt;class ConcatIterator : public boost::iterator_facade&amp;lt;...&amp;gt;&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    reference dereference() const&lt;br /&gt;    {&lt;br /&gt;        return boost::apply_visitor(DereferenceVisitor&amp;lt;reference&amp;gt;(), It);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void increment()&lt;br /&gt;    {&lt;br /&gt;        boost::apply_visitor(IncrementVisitor(), It);&lt;br /&gt;        StabilizeForward();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    void decrement()&lt;br /&gt;    {&lt;br /&gt;        StabilizeBackward();&lt;br /&gt;        boost::apply_visitor(DecrementVisitor(), It);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    bool equal(const ConcatIterator&amp; lhs) const&lt;br /&gt;    {&lt;br /&gt;        return (Range == lhs.Range) &amp;&amp; (It == lhs.It);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;private:&lt;br /&gt;    size_t Range; // 0 for the first range, 1 for the second range&lt;br /&gt;    boost::variant&amp;lt;TIterator1, TIterator2&amp;gt; It, End1, Begin2;&lt;br /&gt;};&lt;/pre&gt;&lt;br /&gt;PS. Нет худа без добра. Мой велосипед &lt;a href="http://thread.gmane.org/gmane.comp.lib.boost.user/46411/focus=46436"&gt;вдохновил&lt;/a&gt; разработчиков Boost.Range добавить объединение диапазонов в RangeEx.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-7793881145739472891?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/7793881145739472891/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=7793881145739472891" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/7793881145739472891?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/7793881145739472891?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/04/range-concatenation.html" title="Range concatenation" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;C08FSHk9fyp7ImA9WxVbGEw.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-3311772043214824580</id><published>2009-04-04T06:02:00.005+04:00</published><updated>2009-04-04T06:23:39.767+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-04T06:23:39.767+04:00</app:edited><title>Unexpected side effect</title><content type="html">У каждого windows-процесса есть такая "глобальная переменная" - текущая директория. Её можно прочитать функцией GetCurrentDirectory() и изменить функцией SetCurrentDirectory().&lt;br /&gt;&lt;br /&gt;Вот чего я не ожидал, что её меняют кто попало. Например, она может изменится после вызовов GetSaveFileName(), GetOpenFileName(). В их документации такое поведение описано и дана возможность его отключить. Но другое дело, что она может измениться внутри... функции &lt;a href="http://msdn.microsoft.com/en-us/library/dd145114(VS.85).aspx"&gt;StartDoc()&lt;/a&gt;. Это такая функция, которая "The StartDoc function starts a print job.", вобщем начинает печать.&lt;br /&gt;&lt;br /&gt;И вот почему такое может происходить: драйвер принтера Microsoft Office Document Image Writer - поставляется с Microsoft Office и "печатает" в TIFF-файлы. При получении новой задачи он спрашивает куда же нужно сохранить создаваемый файл. Делает он это, я полагаю, с помощью GetSaveFileName(). Та, в свою очередь, меняет текущую директорию. Я бы сказал что врядли рядовой программист будет ожидать такие побочные эффекты от функции StartDoc().&lt;br /&gt;&lt;br /&gt;Мораль (кроме той, что нужно драйвер подправить, добавив флажок OFN_NOCHANGEDIR) - старайтесь не менять глобального окружения кроме случаев, где это будет очевидно.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-3311772043214824580?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/3311772043214824580/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=3311772043214824580" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/3311772043214824580?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/3311772043214824580?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/04/unexpected-side-effect.html" title="Unexpected side effect" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry gd:etag="W/&quot;C04MRns4fSp7ImA9WxVbEUs.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-1240382669154353328</id><published>2009-03-27T16:22:00.002+03:00</published><updated>2009-03-27T16:53:07.535+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-27T16:53:07.535+03:00</app:edited><title>RangeEx</title><content type="html">Концепция итераторов в STL - замечательная вещь. Можно сказать это C++-ная замена С-шным указателям, причем обратно совместимая с последними. Но вот в реальной жизни пользоваться только итераторами не настолько удобно - чаще больше подходят более высокоуровневые концепции - диапазоны (ranges) и контейнеры. Чаще бывает удобнее писать &lt;code&gt;copy(from, to)&lt;/code&gt;, чем &lt;code&gt;copy(from.begin(), from.end(), make_back_inserter(to))&lt;/code&gt;. Да и вернуть диапазон вместо пары итераторов гораздо удобнее:&lt;pre&gt;encode(substring());&lt;/pre&gt;вместо&lt;pre&gt;pair&amp;lt;iterator, iterator&amp;gt; sub(substring());&lt;br /&gt;encode(sub.first, sub.second);&lt;/pre&gt;Помню как-то читал как Александреску в языке D активно продвигает диапазоны вообще на замену итераторам. Можно так критично не относиться к итераторам, но я при написании кода вовсю стараюсь специализировать алгоритмы не только для итераторов, но и диапазонов. Алгоритмы в Boost, кстати, тоже очень range-friendly.&lt;br /&gt;&lt;br /&gt;И очень правильный шаг в этом направлении - &lt;a href="http://article.gmane.org/gmane.comp.lib.boost.announce/226"&gt;[Boost.]RangeEx&lt;/a&gt;. Эта библиотека позволяет сильно упростить код, который до этого использовал итераторы. Конечно, раньше кое-как спасал Boost.Iterators, но код был не в пример этому:&lt;pre&gt;boost::copy(rng |&lt;br /&gt;    boost::adaptors::filtered(pred) |&lt;br /&gt;    boost::adaptors::unique,&lt;br /&gt;    out)&lt;/pre&gt;Синтакис очень знаком:&lt;pre&gt;ls | grep ".xml" | wc -l&lt;/pre&gt;&lt;br /&gt;Вобщем, я очень проникся и вам советую попробовать.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-1240382669154353328?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/1240382669154353328/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=1240382669154353328" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/1240382669154353328?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/1240382669154353328?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/03/rangeex.html" title="RangeEx" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry><entry gd:etag="W/&quot;CkENRHk5fip7ImA9WxVVGEU.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-747025629852150374</id><published>2009-03-12T20:46:00.002+03:00</published><updated>2009-03-12T20:58:15.726+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-12T20:58:15.726+03:00</app:edited><title>Прибирайте за собой</title><content type="html">Довольно просто на стороне клиента открыть полученный с сервера файл в его родном приложении: &lt;code&gt;ShellExecute(..., FileName, ...)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Единственное, перед этим нужно сохранить файл на диск. Например, во временную папку. Вопрос только - когда его удалять? Сразу же после вызова &lt;code&gt;ShellExecute()&lt;/code&gt; это делать вряд ли разумно - запущенное приложение может не успеть еще забрать данные из файла. А может успеть открыть файл, и оставить его заблокированным на время просмотра.&lt;br /&gt;&lt;br /&gt;Ничего красивее чем &lt;code&gt;MoveFileEx(FileName, NULL, MOVEFILE_DELAY_UNTIL_REBOOT)&lt;/code&gt; в голову не приходит. Правда до ближайшего ребута может быть много лет, но на клиентских машинах такое редко бывает. С другой стороны - многие вообще не прибирают за собой временные файлы, так что это лучше чем ничего.&lt;br /&gt;&lt;br /&gt;Интересно, есть ли более элегантное решение?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-747025629852150374?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/747025629852150374/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=747025629852150374" title="Комментарии: 9" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/747025629852150374?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/747025629852150374?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/03/blog-post.html" title="Прибирайте за собой" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">9</thr:total></entry><entry gd:etag="W/&quot;DkICSX08cSp7ImA9WxVXGUQ.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-5233555861295328533</id><published>2009-02-18T15:03:00.003+03:00</published><updated>2009-02-19T01:02:48.379+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-19T01:02:48.379+03:00</app:edited><title>Container as a stream</title><content type="html">Как быть, если есть данные в контейнере, а нужно получить доступ к нему в виде стандартного потока (basic_iostream)? Например, данные лежат в памяти, а нужно скормить их какой-нибудь функции из third party библиотеки, которая принимает iostream?&lt;br /&gt;&lt;pre&gt;typedef std::deque&amp;lt;char&amp;gt; container;&lt;br /&gt;container c;&lt;br /&gt;&lt;/pre&gt;В примерах boost::iostreams есть замечательная штука, не знаю почему ее не включили в саму библиотеку - адаптер для контейнеров. Использовать можно так:&lt;br /&gt;&lt;pre&gt;#include &amp;lt;boost/../libs/iostreams/example/container_device.hpp&amp;gt;&lt;br /&gt;&lt;br /&gt;typedef boost::iostreams::example::container_device&amp;lt;container&amp;gt; device;&lt;br /&gt;boost::iostreams::stream&amp;lt;device&amp;gt; stream(c);&lt;br /&gt;&lt;br /&gt;stream &amp;lt;&amp;lt; "hello, world";&lt;br /&gt;stream &amp;gt;&amp;gt; variable;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-5233555861295328533?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/5233555861295328533/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=5233555861295328533" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/5233555861295328533?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/5233555861295328533?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/02/container-as-stream.html" title="Container as a stream" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry gd:etag="W/&quot;D0IBSH45eip7ImA9WxVREUQ.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-5683553868678055007</id><published>2009-01-17T14:39:00.005+03:00</published><updated>2009-01-17T15:32:39.022+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-01-17T15:32:39.022+03:00</app:edited><title>Different libraries</title><content type="html">На новогодних праздниках нашлось время посмотреть разные C++ библиотеки. А то несправедливо &amp;mdash; люди во всю стараются, делают всякие удобные, мощные, гибкие, быстрые и стабильные библиотеки со вкусными лицензиями, а я тут сижу велосипеды изобретаю.&lt;br /&gt;&lt;br /&gt;Краткий список того, что нашел для себя интересного.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lists.boost.org/boost-announce/2009/01/0219.php"&gt;&lt;b&gt;Futures&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;В C++0x будет замечательная вещь &amp;mdash; futures. Коротко говоря &amp;mdash; это лениво-вычисляемые функции. Причем их можно "отправить" вычисляться на другой тред, а потом просто забрать результат. А еще можно организовать обмен сообщениями между тредами. Парочка реализаций futures &lt;a href="http://lists.boost.org/boost-announce/2009/01/0219.php"&gt;уже предлагается для включения в Boost&lt;/a&gt;, в том числе и версия от Anthony Williams, по которой и писался стандарт C++0x в части futures (доступна также &lt;a href="http://www.stdthread.co.uk/"&gt;полностью соответствующая стандарту C++0x библиотека threads&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.boost.org/doc/libs/1_37_0/doc/html/mpi.html"&gt;&lt;b&gt;Boost.MPI&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Библиотека для взаимодействия между процессами с крайне удобным интерфейсом. Приведенные примеры использования вдохновляют.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/any_iterator.html"&gt;&lt;b&gt;Any Iterator&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Давно подумывал написать обертку для итераторов, которая преобразует статический полиморфизм STL-ных итераторов в динамический. Например, если вы хотите иметь интерфейс с парочкой &lt;b&gt;виртуальных&lt;/b&gt; функций RangeBegin() и RangeEnd(), то какой тип они будут возвращать? std::vector&amp;lt;T&amp;gt;::iterator или std::list&amp;lt;T&amp;gt;::iterator? Или даже boost::transform_iterator&amp;lt;...&amp;gt;? Вам не нужно знать какой конкретно тип итераторов будет использовать та или иная реализация интерфейса. Вместо этого вы делаете тип возвращаемого значения RangeBegin() и RangeEnd() как any_iretator&amp;lt;T&amp;gt;, а внутри реализации автоматически преобразовываете свой std::deque&amp;lt;T&amp;gt;::iterator или my::whatever&amp;lt;T&amp;gt;::iterator к этому типу. Подробнее &amp;mdash; в &lt;a href="http://www.artima.com/cppsource/type_erasure.html"&gt;статье&lt;/a&gt;, там же ссылка на саму библиотеку.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lists.boost.org/boost-announce/2008/11/0212.php"&gt;&lt;b&gt;UUID&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Кроссплатформенная библиотека для работы с UUID-ами. Правда, я ожидал от нее быстрых преобразований св строку и обратно, однако там это сделано через потоки ввода-вывода, что видимо будет по производительности далеко от идеала.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://home.roadrunner.com/~hinnant/unique_ptr03.html"&gt;&lt;b&gt;unique_ptr&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Для тех кто уже понял чем unique_ptr лучше auto_ptr (первый заменяет собою второй в C++0x), но еще пользуется компиляторами без поддержки C++0x &amp;mdash; готовая библиотека эмуляции unique_ptr. Да, кстати, а в Boost будет эмуляция rvalues для "старых" компиляторов.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lists.boost.org/Archives/boost/2008/01/132351.php"&gt;&lt;b&gt;Traversal&lt;/b&gt;&lt;/a&gt;&lt;br /&gt;Boost.Iterators предоставляют хорошие возможности по трансформации итераторов, однако по иерархическим структурам с помощью них бегать достаточно трудновато. Как-то я писал STL-style итераторы для MS XML и MS DOM, так вот там бы пригодилась новая библиотека Traversal &amp;mdash; позволяет итерироваться по иерархиям.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-5683553868678055007?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/5683553868678055007/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=5683553868678055007" title="Комментарии: 4" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/5683553868678055007?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/5683553868678055007?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2009/01/different-libraries.html" title="Different libraries" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></entry><entry gd:etag="W/&quot;DkUBRnY6cSp7ImA9WxVTEk8.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-3006756653745778051</id><published>2008-12-25T18:53:00.004+03:00</published><updated>2008-12-25T19:57:37.819+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-25T19:57:37.819+03:00</app:edited><title>this == NULL. WTF?</title><content type="html">Что вы подумаете, если встретите код типа такого:&lt;pre&gt;if (this == NULL)&lt;br /&gt;    ...;&lt;/pre&gt;Нормально ли что &lt;code&gt;this&lt;/code&gt; может быть нулевым, ведь при этом даже к членам класса обратиться нельзя, не то что вызвать виртуальную функцию.&lt;br /&gt;Однако тех, кого не удивляет конструкции вида &lt;code&gt;delete this;&lt;/code&gt;, не удивит и проверка this на NULL. В самом деле, this - это &lt;a href="http://dvinogradov.blogspot.com/2007/08/this-is-just-ordinary-pointer.html"&gt;обычный указатель&lt;/a&gt;, и как любой другой указатель он может быть равен NULL:&lt;pre&gt;class foo {&lt;br /&gt;public:&lt;br /&gt;    bool am_i_alive() { return this != NULL; }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;foo *p = NULL;&lt;br /&gt;assert(!p-&gt;am_i_alive());&lt;/pre&gt;&lt;br /&gt;Но не разумнее ли, спрашивается, проверять указатель на NULL "снаружи" класса (в данном примере - проверять p на NULL), а не внутри его. То есть не вызывать методы объекта, если указатель на него нулевой. И так действительно будет разумнее:&lt;pre&gt;void foo(Bar &amp;bar, IFilter *filter = NULL)&lt;br /&gt;{&lt;br /&gt;    if (filter)&lt;br /&gt;        filter-&gt;init(bar);&lt;br /&gt;&lt;br /&gt;    for (auto b : bar)&lt;br /&gt;        if (!filter || filter-&gt;is_accepted(b))&lt;br /&gt;            out.insert(b);&lt;br /&gt;}&lt;/pre&gt;Единственное преимущество внесения проверки внутрь класса - сокращение вызывающего кода, немного повышающее его написание и читаемость. То есть по сути "синтаксический сахар":&lt;pre&gt;void foo(Bar &amp;bar, IFilter *filter = NULL)&lt;br /&gt;{&lt;br /&gt;    filter-&gt;init(bar);&lt;br /&gt;&lt;br /&gt;    for (auto b : bar)&lt;br /&gt;        if (filter-&gt;is_accepted(b))&lt;br /&gt;            out.insert(b);&lt;br /&gt;}&lt;/pre&gt;Но ценой этого будет то, что вызываемые функции не должны быть виртуальными, то есть придется каждую виртуальную функцию "обернуть" примерно так:&lt;pre&gt;&lt;br /&gt;class IFilter&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    void init(Bar&amp; bar) { if (this) init_impl(bar; }&lt;br /&gt;    bool is_accepted(Bar::value_type b) { return !this || is_accepted_impl(b); }&lt;br /&gt;private:&lt;br /&gt;    virtual void init_impl(Bar&amp;) = 0;&lt;br /&gt;    virtual bool is_accepted_impl(Bar::value_type) = 0;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Любая книжка по рефакторингу скажет, что добиться подобного кода можно с помощью определеннго паттерна, который предлагает замену нулевых указателей на указатели ненулевые, но указывающие на объект-пустышку:&lt;pre&gt;&lt;br /&gt;class IFilter&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    virtual void init(Bar&amp;) = 0;&lt;br /&gt;    virtual bool is_accepted(Bar::value_type) = 0;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class NullFilter&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    virtual void init(Bar&amp;) {}&lt;br /&gt;    virtual bool is_accepted(Bar::value_type) { return true; }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;void foo(Bar &amp;bar, IFilter &amp;filter = NullFilter())&lt;br /&gt;{&lt;br /&gt;    filter.init(bar);&lt;br /&gt;&lt;br /&gt;    for (auto b : bar)&lt;br /&gt;        if (filter.is_accepted(b))&lt;br /&gt;            out.insert(b);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;В качестве члена какого-нибудь класса это может выглядеть так:&lt;pre&gt;class Foo&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    Foo() : filter(new NullFilter) {}&lt;br /&gt;    void set_filter(auto_ptr&amp;lt;IFilter&amp;gt; f) { filter = f; }&lt;br /&gt;    void use_filter()&lt;br /&gt;    {&lt;br /&gt;        // можно смело использовать конструкции filter.get()-&gt;xxx()&lt;br /&gt;    }&lt;br /&gt;private:&lt;br /&gt;    auto_ptr&amp;lt;IFilter&amp;gt; filter;    &lt;br /&gt;};&lt;/pre&gt;Единственное, что может заставить не послушаться книжку по рефакторингу, так это профайлер ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-3006756653745778051?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/3006756653745778051/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=3006756653745778051" title="Комментарии: 11" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/3006756653745778051?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/3006756653745778051?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/12/this-null-wtf.html" title="this == NULL. WTF?" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">11</thr:total></entry><entry gd:etag="W/&quot;CU4DR3w7fSp7ImA9WxRaFEs.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-7810638536241910438</id><published>2008-12-17T00:33:00.005+03:00</published><updated>2008-12-17T00:46:16.205+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-17T00:46:16.205+03:00</app:edited><title>Logical XOR operator in C++</title><content type="html">Понадобилось использовать оператор logical XOR и... поначалу не нашел я его в С++. В смысле не нашел оператора, на котором написано "logical XOR". А вообще оператор есть: &lt;code&gt;&lt;font size="+1"&gt;&lt;b&gt;!=&lt;/b&gt;&lt;/font&gt;&lt;/code&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-7810638536241910438?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/7810638536241910438/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=7810638536241910438" title="Комментарии: 5" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/7810638536241910438?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/7810638536241910438?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/12/logical-xor-operator-in-c.html" title="Logical XOR operator in C++" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total></entry><entry gd:etag="W/&quot;CEIERH48cSp7ImA9WxRUEE4.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-4228892922708612515</id><published>2008-11-18T21:07:00.004+03:00</published><updated>2008-11-18T21:21:45.079+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-18T21:21:45.079+03:00</app:edited><title>The magic of shared_ptr</title><content type="html">В качестве еще одной иллюстрации к предыдущей заметке про "&lt;a href="http://dvinogradov.blogspot.com/2008/11/storing-dynamic-created-objects-in.html"&gt;магию shared_ptr&lt;/a&gt;", в ответ один из комментариев - небольшой пример: создадим простую иерархию классов, где базовый класс не имеет виртуального деструктора.&lt;pre&gt;struct A { ~A() { cout &amp;lt;&amp;lt; "~A()" &amp;lt;&amp;lt; endl; } };&lt;br /&gt;struct B : public A { ~B() { cout &amp;lt;&amp;lt; "~B()" &amp;lt;&amp;lt; endl; } };&lt;/pre&gt;А теперь создадим объект класса B и поместим его в контейнер указателей на A:&lt;pre&gt;{&lt;br /&gt;    auto_ptr&amp;lt;B&amp;gt; b(new B);&lt;br /&gt;    ptr_vector&amp;lt;A&amp;gt; x;&lt;br /&gt;    x.push_back(b);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;{&lt;br /&gt;    auto_ptr&amp;lt;B&amp;gt; b(new B);&lt;br /&gt;    vector&amp;lt; shared_ptr&amp;lt;A&amp;gt; &amp;gt; x;&lt;br /&gt;    x.push_back(b);&lt;br /&gt;}&lt;/pre&gt;В случае контейнера, хранящего только указатели (в данном случае - boost::ptr_vector) - получим при разрушении объекта вызов только ~A(), а в случае с shared_ptr будет вызван ~B(), так как shared_ptr &lt;a href="http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm#Members"&gt;немного умнее&lt;/a&gt; обычного указателя.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-4228892922708612515?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/4228892922708612515/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=4228892922708612515" title="Комментарии: 6" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/4228892922708612515?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/4228892922708612515?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/11/magic-of-sharedptr.html" title="The magic of shared_ptr" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total></entry><entry gd:etag="W/&quot;DEUFRXw4eCp7ImA9WxRbE04.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-727211606308313319</id><published>2008-11-17T21:45:00.008+03:00</published><updated>2008-12-03T23:30:14.230+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-03T23:30:14.230+03:00</app:edited><title>Storing dynamicly created objects in a container</title><content type="html">Хранение полиморфных объектов в контейнере - классика жанра:&lt;pre&gt;class A { ... };&lt;br /&gt;class B : public A { ... };&lt;br /&gt;std::vector&amp;lt;A*&amp;gt; BunchOfObjects;&lt;br /&gt;BunchOfObjects.push_back(new B);&lt;/pre&gt;Главное - не забыть уничтожить удаляемые из контейнера объекты. И если делать это через оператор &lt;code&gt;delete&lt;/code&gt; - еще про виртуальный деструктор у базового класса не забыть бы.&lt;br /&gt;&lt;br /&gt;Хотя... другое классическое решение позволяет обойтись и без первого и без второго:&lt;pre&gt;std::vector&amp;lt; boost::shared_ptr&amp;lt;A&amp;gt; &amp;gt; BunchOfObjects;&lt;br /&gt;BunchOfObjects.push_back(boost::shared_ptr&amp;lt;A&amp;gt;(new B));&lt;/pre&gt; Наличие виртуального деструктора в таком случае не принципиально - shared_ptr запоминает как нужно удалять объект, по умолчанию это будет оператор delete для нужного типа. Таким образом можно хранить, например, список динамически созданных окон - предков ATL::CWindow. Более того, если от хранимых объектов нужна лишь возможность их в нужный момент удалить, то можно вообще не задумываться от их типе используя boost::shared_ptr&amp;lt;void&amp;gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-727211606308313319?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/727211606308313319/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=727211606308313319" title="Комментарии: 6" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/727211606308313319?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/727211606308313319?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/11/storing-dynamic-created-objects-in.html" title="Storing dynamicly created objects in a container" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">6</thr:total></entry><entry gd:etag="W/&quot;CE8BQ3o-eSp7ImA9WxRVFk0.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-5333479092911852832</id><published>2008-11-13T21:40:00.004+03:00</published><updated>2008-11-13T22:00:52.451+03:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-11-13T22:00:52.451+03:00</app:edited><title>Wake me up tomorrow</title><content type="html">Функции WinAPI, которые можно использовать в качестве "будильника" - SetTimer(), WaitForSingleObject(), и прочие принимают время относительное, а не абсолютное. Нельзя сказать "Разбудите меня завтра!". Только "Пните меня через 13786 миллисекунд".&lt;br /&gt;&lt;br /&gt;Разумный вариант - подсчитать сколько миллисекунд до заданного времени, но ничто не вечно под луной. Время может перевести пользователь, программка, синхронизирующаяся с сервером, да сама Windows, в конце концов. Так что заодно мониторим WM_TIMECHANGE и пересчитываем сколько осталось.&lt;br /&gt;&lt;pre&gt;void SetTimerToTommorrow()&lt;br /&gt;{&lt;br /&gt;    using namespace boost::posix_time;&lt;br /&gt;    using namespace boost::gregorian;&lt;br /&gt;&lt;br /&gt;    ptime Now(second_clock::local_time()), Tomorrow(Now.date() + days(1));&lt;br /&gt;    SetTimer(..., (Tomorrow - Now).total_milliseconds() + 1, ...);&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-5333479092911852832?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/5333479092911852832/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=5333479092911852832" title="Комментарии: 4" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/5333479092911852832?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/5333479092911852832?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/11/wake-me-up-tomorrow.html" title="Wake me up tomorrow" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></entry><entry gd:etag="W/&quot;DkQNQ3szeip7ImA9WxRQGUQ.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-4007372040314157578</id><published>2008-10-14T18:06:00.006+04:00</published><updated>2008-10-14T18:26:32.582+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-10-14T18:26:32.582+04:00</app:edited><title>Fading instead of cutting</title><content type="html">Если текст не влезает в отведенное ему пространство на экране, можно просто его отрезать по краю области. Однако часто будет непонятно что текст обрезан - не очень очевидно что за "Пушкин" скрывается обрезанная "Пушкинская". Решение получше - добавить в конце троеточие: "Пушки...". Но тоже иногда забавно смотрится.&lt;br /&gt;&lt;br /&gt;Красивый вариант - уходить в полупрозрачность:&lt;br /&gt;&lt;img src="http://sraider.2x4.ru/raider/blogger/2008/fader.png"&gt;&lt;br /&gt;&lt;br /&gt;Реализовать можно довольно просто:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;перед отрисовкой текста запоминаем тот фрагмент фона, на котором текст будет уходить в полупрозрачность&lt;br /&gt;&lt;li&gt;после отрисовки текста выводим этот фрагмент с учетом полупрозрачности.&lt;/ul&gt;&lt;br /&gt;Для удобства использования этих двух процессов (который "перед" и который "после") вынесем первый в конструктор класса Fader, второй в деструктор. Использование будет такое:&lt;pre&gt;{&lt;br /&gt;    Fader fader(dc, rect); // выполняется то, что "перед"&lt;br /&gt;    SomeDrawing(dc, rect); // непосредственно отрисовка текста&lt;br /&gt;    // выполняется то, что "после" - в деструкторе ~Fader()&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Сама реализация предельно проста:&lt;pre&gt;class Fader&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    Fader::Fader(CDCHandle DC, const CRect&amp; ClientRect) : DC_(DC), FaderRect_(ClientRect)&lt;br /&gt;    {&lt;br /&gt;        // опеределяем область, в которой будем уходить в полупрозрачность&lt;br /&gt;        const int FaderWidth = 32;&lt;br /&gt;        FaderRect_.left = max(FaderRect_.right - FaderWidth, 0);&lt;br /&gt;&lt;br /&gt;        // создаем bitmap&lt;br /&gt;        BYTE *Bits = static_cast&lt;BYTE*&gt;(CreateBGRABitmap(Bitmap_, DC, FaderRect_.Size()));&lt;br /&gt;        if (!Bits)&lt;br /&gt;            return;&lt;br /&gt;&lt;br /&gt;        // копируем фон в bitmap&lt;br /&gt;        BitmapDC_.CreateCompatibleDC(DC);&lt;br /&gt;        BitmapDC_.SelectBitmap(Bitmap_);&lt;br /&gt;        BitBlt(BitmapDC_, 0, 0, FaderRect_.Width(), FaderRect_.Height(), DC, FaderRect_.left, FaderRect_.top, SRCCOPY);&lt;br /&gt;&lt;br /&gt;        // уводим в полупрозрачность&lt;br /&gt;        GdiFlush();&lt;br /&gt;        for (int Line = FaderRect_.Height(); Line; --Line)&lt;br /&gt;            for (int Pos = 0; Pos &lt; FaderRect_.Width(); ++Pos, Bits += 4)&lt;br /&gt;            {&lt;br /&gt;                BYTE Alpha = Pos &lt;&lt; 3; // как удачно, что FaderWidth является степенью двойки ;)&lt;br /&gt;                ApplyAlpha(Bits[0], Alpha);&lt;br /&gt;                ApplyAlpha(Bits[1], Alpha);&lt;br /&gt;                ApplyAlpha(Bits[2], Alpha);&lt;br /&gt;                Bits[3] = Alpha;&lt;br /&gt;            }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    Fader::~Fader()&lt;br /&gt;    {&lt;br /&gt;        // накладываем полупрозрачный bitmap с фоном&lt;br /&gt;        if (BitmapDC_)&lt;br /&gt;            AlphaBlend(DC_, FaderRect_, BitmapDC_, CRect(CPoint(0, 0), FaderRect_.Size()));&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;private:&lt;br /&gt;    HDC DC_;&lt;br /&gt;    CRect FaderRect_;&lt;br /&gt;    CBitmap Bitmap_;&lt;br /&gt;    CDC BitmapDC_;&lt;br /&gt;};&lt;/pre&gt;&lt;br /&gt;PS. Парочка helper-ов, которые используются выше:&lt;pre&gt;void *CreateBGRABitmap(CBitmap&amp; Bitmap, HDC DC, CSize Size, bool UpsideDown)&lt;br /&gt;{&lt;br /&gt;    BITMAPV5HEADER bi;&lt;br /&gt;    memset(&amp;bi, 0, sizeof(bi));&lt;br /&gt;    bi.bV5Size = sizeof (BITMAPV5HEADER);&lt;br /&gt;    bi.bV5Width = Size.cx;&lt;br /&gt;    bi.bV5Height = UpsideDown ? Size.cy : -Size.cy;&lt;br /&gt;    bi.bV5Planes = 1;&lt;br /&gt;    bi.bV5BitCount = 32;&lt;br /&gt;    bi.bV5Compression = BI_BITFIELDS;&lt;br /&gt;    bi.bV5RedMask   = 0x00FF0000;&lt;br /&gt;    bi.bV5GreenMask = 0x0000FF00;&lt;br /&gt;    bi.bV5BlueMask  = 0x000000FF;&lt;br /&gt;    bi.bV5AlphaMask = 0xFF000000;&lt;br /&gt;    void *Bits = NULL;&lt;br /&gt;    Bitmap.CreateDIBSection(DC, (BITMAPINFO*)&amp;bi, DIB_RGB_COLORS, &amp;Bits, NULL, 0);&lt;br /&gt;    return Bits;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void ApplyAlpha(BYTE&amp; Value, BYTE Alpha)&lt;br /&gt;{&lt;br /&gt;    Value = ((unsigned)Value * Alpha) &gt;&gt; 8;&lt;br /&gt;}&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-4007372040314157578?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/4007372040314157578/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=4007372040314157578" title="Комментарии: 0" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/4007372040314157578?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/4007372040314157578?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/10/fading-instead-of-cutting.html" title="Fading instead of cutting" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;D08ARH49eip7ImA9WxRSEEs.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-4763929205968130228</id><published>2008-09-10T20:19:00.003+04:00</published><updated>2008-09-10T20:57:25.062+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-10T20:57:25.062+04:00</app:edited><title>Animated GIFs using GDI+</title><content type="html">GDI+ как-то поздновато появилась. Именно такой по архитектуре нужно было делать GDI (которая без плюса) изначала. Но, так как изначально GDI сделали как сделали - получили кучу &lt;a href="http://www.rsdn.ru/article/qna/gdi/drawimg.xml"&gt;извращений&lt;/a&gt; для простой операции - отобразить картинку на экране.&lt;br /&gt;&lt;br /&gt;А с GDI+ даже анимированные гифы рисовать - как два пальца. В кратце расскажу как.&lt;br /&gt;&lt;br /&gt;Для начала абстрагируемся от GDI+, мало ли потом захотим использовать другую библиотеку. Создадим небольшой интерфейс:&lt;pre&gt;class CImage&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    virtual CSize Size() const = 0;&lt;br /&gt;    virtual void Draw(HDC DC, const CRect&amp; DestRect, const CRect&amp; SrcRect) const = 0;&lt;br /&gt;    virtual ~CImage() {}&lt;br /&gt;&lt;br /&gt;    virtual size_t GetFrameCount() const = 0;&lt;br /&gt;    virtual void SelectActiveFrame(size_t Frame) = 0;&lt;br /&gt;    virtual int /*ms*/ GetFrameDelay(size_t Frame) const = 0;&lt;br /&gt;};&lt;/pre&gt;Создадим реализацию этого интерфейса на основе GDI+:&lt;pre&gt;class CGdiPlusImage : public CImage {...}&lt;/pre&gt;Уже можно рисовать! Image.Draw(...) и вперед ;) Не забыть только вызвать в нужный момент SelectAcriveFrame() - и все заанимируется сразу.&lt;br /&gt;&lt;br /&gt;Один товарисч из Объединенного Королевства &lt;a href="http://www.codeproject.com/KB/GDI-plus/imageexgdi.aspx"&gt;предлагает&lt;/a&gt; создавать отдельный поток, чтобы там можно было через WaitEvent() дожидаться наступления следующего фрейма. Но зачем нам по отдельному потоку на каждую анимацию? Мы сделаем по-русски - через SetTimer(). Заведем будильник на время, когда нужно будет следующий фрейм отрисовать - и по WM_TIMER отрисуем. Сделаем небольшой helper-класс для этого:&lt;pre&gt;class CAnimation&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    // all time/reriods are in ms&lt;br /&gt;    CAnimation(CImage&amp;amp; Image, DWORD Time = timeGetTime());&lt;br /&gt;&lt;br /&gt;    typedef std::pair&amp;lt;size_t /*current frame*/, DWORD /*period till next frame*/&amp;gt; TInfo;&lt;br /&gt;    TInfo CurrentInfo(DWORD Time = timeGetTime());&lt;br /&gt;&lt;br /&gt;    size_t FrameByTime(DWORD Time = timeGetTime());&lt;br /&gt;    DWORD PeriodTillNextFrame(DWORD CurrentTime = timeGetTime());&lt;br /&gt;&lt;br /&gt;private:&lt;br /&gt;    size_t nFrames_;&lt;br /&gt;    DWORD Start_;&lt;br /&gt;    typedef std::vector&amp;lt;DWORD&amp;gt; TFrameEnds;&lt;br /&gt;    TFrameEnds FrameEnds_;&lt;br /&gt;};&lt;/pre&gt;Этот helper выдаст нам:&lt;br /&gt;1) какой фрейм показывать на такой-то момент времени (чтобы в любой момент времени при отрисовке мы знали какой фрейм рисовать)&lt;br /&gt;2) сколько осталось до показа следующего фрейма (чтобы знать на сколько нам будильник заводить).&lt;br /&gt;Выдаст нам это либо по-отдельности, либо сразу (в виде TInfo).&lt;br /&gt;&lt;br /&gt;Осталось реализовать получение этого самомго TInfo. Для этого в самом начале посчитаем когда какой фрейм заканчивается:&lt;pre&gt;DWORD LastEnd = 0;&lt;br /&gt;for (size_t nFrame = 0; nFrame &lt; nFrames_; ++nFrame)&lt;br /&gt;{&lt;br /&gt;    LastEnd += Image.GetFrameDelay(nFrame);&lt;br /&gt;    FrameEnds_[nFrame] = LastEnd;&lt;br /&gt;}&lt;/pre&gt;тогда потом легко найдем нужный фрейм простым двоичным поиском:&lt;pre&gt;DWORD FullCyclePeriod = FrameEnds_.back();&lt;br /&gt;DWORD NormalizedTime = (Time - Start_) % FullCyclePeriod;&lt;br /&gt;TFrameEnds::iterator Current(std::upper_bound(FrameEnds_.begin(), FrameEnds_.end(), NormalizedTime));&lt;br /&gt;return TInfo(Current - FrameEnds_.begin(), *Current - NormalizedTime);&lt;/pre&gt;И все! Теперь при старте заводим будильник на начало второго фрейма, а при звоне будильника делаем invalidate и заводим будильник на начало следующего фрейма - и т.д. по кругу:&lt;pre&gt;void OnCreate()&lt;br /&gt;{&lt;br /&gt;    SetTimer(TimerId, Animation.PeriodTillNextFrame());&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void OnTimer()&lt;br /&gt;{&lt;br /&gt;    CAnimation::TInfo AnimationInfo(Animation.CurrentInfo());&lt;br /&gt;    Image.SelectActiveFrame(AnimationInfo.first);&lt;br /&gt;    SetTimer(TimerId, AnimationInfo.second);&lt;br /&gt;    Invalidate();&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://sraider.2x4.ru/raider/coding/AnimatedGif.zip"&gt;&lt;b&gt;Полный код примера.zip&lt;/b&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-4763929205968130228?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/4763929205968130228/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=4763929205968130228" title="Комментарии: 2" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/4763929205968130228?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/4763929205968130228?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/09/animated-gifs-using-gdi.html" title="Animated GIFs using GDI+" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry gd:etag="W/&quot;AkQHR3Y8eSp7ImA9WxdaFEw.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-3090643241768613200</id><published>2008-08-22T18:53:00.003+04:00</published><updated>2008-08-22T19:18:56.871+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-08-22T19:18:56.871+04:00</app:edited><title>Debugging using binary search</title><content type="html">Бывает обидно когда вылезают баги в том месте, где раньше все работало нормально. Смотришь - баг, но точно помнишь что месяц назад этого бага не было. Причем в какой момент образовался этот баг - непонятно. Столько кода с уже добавлено и переписано.&lt;br /&gt;&lt;br /&gt;Решение такой проблемы на корню: обвешивание всего кода юнит-тестами и прогон всех тестов после каждого коммита. Однако не всегда это реализуемо. Либо тесты писать времени нет, либо очень сложно их писать (например, для GUI).&lt;br /&gt;&lt;br /&gt;В таком случае можно прибегнуть к двоичному поиску. У нас есть репозиторий и можно получить функцию от номера ревизии - воспроизводится ли данный баг в этой ревизии или нет. Например, в 500-ой его не было а сейчас 1030-ая и он есть. Делим отрезок ревизий [500,1030] пополам, попадаем где-то в 765-ую, апдейтимся до нее, ребилдим проект, смотрим воспроизводится ли баг. Не воспроизводится? Значит он где-то в отрезке [765,1030]. Делим его пополам, и т.д пока не найдем отрезок длинной в один коммит, например [897,898]. Значит 898-ой коммит вызвал появление бага. Смотрим этот коммит и разбираемся в чем дело, выясняем какие изменения привели к багу.&lt;br /&gt;&lt;br /&gt;Если коммиты небольшие по объему (а я рекомендую именно так и делать), то довольно быстро можно понять где вылез баг и пофиксить его.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-3090643241768613200?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/3090643241768613200/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=3090643241768613200" title="Комментарии: 5" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/3090643241768613200?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/3090643241768613200?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/08/debugging-using-binary-search.html" title="Debugging using binary search" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">5</thr:total></entry><entry gd:etag="W/&quot;D0IEQn8-fip7ImA9WxdbEk8.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-467301253859669950</id><published>2008-08-08T23:42:00.003+04:00</published><updated>2008-08-08T23:58:23.156+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-08-08T23:58:23.156+04:00</app:edited><title>Typedefs</title><content type="html">Кстати, о &lt;a href="http://alenacpp.blogspot.com/2008/08/typedef-const.html"&gt;typedef-ах&lt;/a&gt;. Typedef не создает новый тип - только его псевдоним. В этом можно убедиться на простом примере:&lt;pre&gt;template &amp;lt;typename T&amp;gt; class foo {};&lt;br /&gt;template &amp;lt;&amp;gt; class foo&amp;lt;char&amp;gt; {};&lt;br /&gt;typedef char bar;&lt;br /&gt;template &amp;lt;&amp;gt; class foo&amp;lt;bar&amp;gt; {} // compile error&lt;/pre&gt;В последней строке  получим ошибку, говорящую нам что шаблон foo для типа char уже определен.&lt;br /&gt;&lt;br /&gt;Кстати, на счет типов. Как думаете, что выдаст следующий код:&lt;pre&gt;template &amp;lt;typename T&amp;gt; struct foo { static const int x = 1; };&lt;br /&gt;template &amp;lt;&amp;gt; struct foo&amp;lt;signed char&amp;gt; { static const int x = 2; };&lt;br /&gt;template &amp;lt;&amp;gt; struct foo&amp;lt;unsigned char&amp;gt; { static const int x = 3; };&lt;br /&gt;cout &amp;lt;&amp;lt; foo&amp;lt;char&amp;gt;::x;&lt;/pre&gt;Единицу. Char, signed char и unsigned char - три разных типа. В зависимости от настроек компилятора char ведет себя как signed или unsigned, но это отдельный тип.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-467301253859669950?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/467301253859669950/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=467301253859669950" title="Комментарии: 4" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/467301253859669950?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/467301253859669950?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/08/typedefs.html" title="Typedefs" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">4</thr:total></entry><entry gd:etag="W/&quot;A0ENRXY8fCp7ImA9WxdbEk0.&quot;"><id>tag:blogger.com,1999:blog-22921254.post-2438110420693757572</id><published>2008-08-08T18:50:00.003+04:00</published><updated>2008-08-08T19:34:54.874+04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-08-08T19:34:54.874+04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C++" /><title>String parameters</title><content type="html">Пытался установить Visual Studio 2005 Service Pack 1 на машине, у которой всего полтора гигабайта свободного места на диске. Думаете получилось? Нифига - места на диске не хватило!&lt;br /&gt;&lt;br /&gt;Этот сервис пак представляет собой .exe размером 500Mb, внутри которого .msp, а внутри .msp видимо все обновляемые файлы лежат. То сначала распаковывается содержимое .exe в Temp, потом туда же распаковывается содержимое .msp, и видимо там дальше еще что-то распаковывается... Вобщем, красота.&lt;br /&gt;&lt;br /&gt;К чему это я - к тому как часто передаются строки в качестве параметров. Строка в C++ - чаще всего std::basic_string:&lt;br /&gt;&lt;code&gt;void foo(const std::string&amp; Param);&lt;/code&gt;&lt;br /&gt;Если у нас строка хранится не в std::string, а например в const char*, то получим неявное копирование строки в конструкторе std::string.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;void foo(const char* Param);&lt;/code&gt;&lt;br /&gt;Уже лучше - можем передать string.c_str(), можем что угодно другое, где есть null terminator. А если у нас строка (или даже большой текст) загружен из ресурсов и есть const char* data() и size_t size(), но нет замыкающего нуля - снова придется делать копию. И это не единственный вариант, когда есть data()/size() - если хотим передать подстроку (внутреннюю часть какой-либо большой строки), то возникнет та же проблема.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;void foo(const char* Param, size_t Size = strlen(Param));&lt;/code&gt;&lt;br /&gt;Уже намного лучше. Правда если строка в другой кодировке или вообще, читается с потока - опять нужно складывать копию в память, а потом только вызывать foo().&lt;br /&gt;&lt;br /&gt;А хорошая альтернатива - это парочка итераторов (a-la STL) или контейнер (a-la Boost):&lt;br /&gt;&lt;code&gt;template &amp;lt;typename T&gt;&amp;gt; void foo(T First, T Last);&lt;br /&gt;template &amp;lt;typename T&gt;&amp;gt; void foo(const &amp;T String);&lt;/code&gt;&lt;br /&gt;Конструкции почти эквивалентны, т.к. у контейнера легко можно взять String.begin() и String.end(), а пару итераторов - скормить boost::make_iterator_range(). Так что скорее вопрос вкуса, какой вариант выбрать - я за контейнер.&lt;br /&gt;&lt;br /&gt;С таким интерфейсом мы можем взять свести копирование к минимуму. Например, пришли данные в urlencode, да еще в win1251:&lt;br /&gt;&lt;code&gt;char* data = "q=%EF%F0%E8%E2%E5%F2&amp;a=%EC%E5%E4%E2%E5%E4";&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Парсим все в указатели:&lt;br /&gt;&lt;code&gt;typedef std::pair&amp;lt;char*, char*&amp;gt; substr;&lt;br /&gt;typedef std::pair&amp;lt;substr, substr&amp;gt; item;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Делаем итератор для декодирования urldecode ("%EF%F0%E8%E2%E5%F2" -&gt; "привет")  - и его operator* и operator++ будут делать всю работу "на ходу". (Тут можно призвать на помощь boost::iterator_facade). Нужно конвертировать в utf-8? - еще один итератор ;) И все это скормим нашей foo().&lt;br /&gt;&lt;br /&gt;PS. Подробности такого подхода - в boost.iterator, boost.range, boost.string algo.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22921254-2438110420693757572?l=dvinogradov.blogspot.com'/&gt;&lt;/div&gt;</content><link rel="replies" type="application/atom+xml" href="http://dvinogradov.blogspot.com/feeds/2438110420693757572/comments/default" title="Комментарии к сообщению" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=22921254&amp;postID=2438110420693757572" title="Комментарии: 1" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/2438110420693757572?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/22921254/posts/default/2438110420693757572?v=2" /><link rel="alternate" type="text/html" href="http://dvinogradov.blogspot.com/2008/08/string-parameters.html" title="String parameters" /><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>sraider@yandex.ru</email><gd:extendedProperty name="OpenSocialUserId" value="17463360102590438590" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">1</thr:total></entry></feed>
