<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-22921254</id><updated>2026-03-21T21:07:01.659+03:00</updated><category term="C++"/><category term="Boost"/><category term="WinAPI"/><category term="COM"/><category term="COM IE"/><category term="SQL"/><category term="optimization"/><title type='text'>Raider&#39;s Programming Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default?redirect=false'/><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'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>147</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-22921254.post-6910353249431943357</id><published>2012-04-13T16:42:00.000+04:00</published><updated>2012-04-13T17:18:45.232+04:00</updated><title type='text'>Boost.Lexical cast</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Знаете ли вы, что boost::lexical_cast не имеет такого важного параметра как локаль? По-моему, это ни в какие ворота. Всем функциям, которые работают со строками в Boost-е, можно указать локаль, а тут - такой вот финт! Всегда используется глобальная локаль.&lt;br /&gt;
&lt;br /&gt;
Например, добавляем в код безобидный оператор:&lt;br /&gt;
&lt;code&gt;std::locale::global(std::locale(&quot;&quot;)); // системная локаль, например русская&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
А теперь представьте, как будет работать код, зарытый где-то глубоко в недрах какой-нибудь библиотеки:&lt;br /&gt;
&lt;code&gt;HttpResponse.AddHeader(&quot;Content-Length&quot;, boost::lexical_cast&amp;lt;std::string&amp;gt;(Length)); // внезапно добавляется разделитель тысячных разрядов&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;std::string&gt;По-моему, использовать boost::lexical_cast в библиотеках надо категорически запретить. А в своем коде - только с BOOST_LEXICAL_CAST_ASSUME_C_LOCALE.&lt;/std::string&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/6910353249431943357/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/6910353249431943357' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/6910353249431943357'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/6910353249431943357'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2012/04/boostlexical-cast.html' title='Boost.Lexical cast'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-7190870918574010868</id><published>2011-03-29T11:27:00.003+04:00</published><updated>2011-03-29T11:32:41.598+04:00</updated><title type='text'>Silent ;)</title><content type='html'>&quot;Ленивые&quot; логические операторы позволяют писать такой забавный код:&lt;br /&gt;&lt;pre&gt;int nArgs = 0;&lt;br /&gt;LPWSTR *Args = CommandLineToArgvW(GetCommandLineW(), &amp;nArgs);&lt;br /&gt;&lt;br /&gt;bool Silent = false;&lt;br /&gt;if ((nArgs == 2) ||&lt;br /&gt;   ((nArgs == 3) &amp;&amp; boost::equals(Args[2], L&quot;/silent&quot;) &amp;&amp; (&lt;b&gt;&lt;i&gt;Silent = true&lt;/i&gt;&lt;/b&gt;)))&lt;br /&gt;        DoSomething(Args[1], Silent);&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/7190870918574010868/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/7190870918574010868' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/7190870918574010868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/7190870918574010868'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2011/03/silent.html' title='Silent ;)'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-4167317888676096209</id><published>2010-11-18T18:51:00.003+03:00</published><updated>2010-11-20T10:57:29.317+03:00</updated><title type='text'>(std::max)</title><content type='html'>Как-то давно я &lt;a href=&quot;http://dvinogradov.blogspot.com/2006/04/define-is-evil.html&quot;&gt;советовал&lt;/a&gt; бороться с макросами min/max из windows.h таким образом:&lt;pre&gt;#include &amp;lt;windows.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#ifdef min&lt;br /&gt;#undef min&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;#ifdef max&lt;br /&gt;#undef max&lt;br /&gt;#endif&lt;/pre&gt;Напомню в чем суть проблемы: после включения windows.h уже нельзя писать &lt;code&gt;std::max(a,b)&lt;/code&gt;. Оказывается все гораздо проще: можно писать &lt;code&gt;(std::max)(a,b)&lt;/code&gt;. Препроцессор обойдет такую запись стороной. Тогда не нужны undef-ы (актуально если потом вдруг нужно включить что-нибудь типа atl*.h).</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/4167317888676096209/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/4167317888676096209' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/4167317888676096209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/4167317888676096209'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2010/11/stdmax.html' title='(std::max)'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-3641558299842901915</id><published>2010-09-16T23:11:00.002+04:00</published><updated>2010-09-16T23:12:04.592+04:00</updated><title type='text'>Local functions in C++</title><content type='html'>В Pascal есть замечательная вещь - локальные функции. Иногда они ну ооочень удобны. Если кто с Паскалем не знаком, на C++ это бы выглядело примерно так:&lt;pre&gt;int foo(int x)&lt;br /&gt;{&lt;br /&gt;    int y = 0;&lt;br /&gt;&lt;br /&gt;    void bar(int z)&lt;br /&gt;    {&lt;br /&gt;        y += x*z;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    bar(1);&lt;br /&gt;    bar(2);&lt;br /&gt;&lt;br /&gt;    return y;&lt;br /&gt;}&lt;/pre&gt;То есть тело такой функции пишется внутри другой функции, и она может ссылать на ее переменные.&lt;br /&gt;В C++ такого счастья нет. Может такого не сделали из-за того, что в Сях стек устроен немного по-другому, нежели в Паскале (в любом случае - не оправдание), может Бьярн счел это &quot;не тру&quot;. Конечно, можно написать локальный класс, у которого будет нужная функция, но которому придется вручную передавать нужные переменные - но это неудобно, да и выглядит ужасно. Вот как-то так:&lt;pre&gt;int foo(int x)&lt;br /&gt;{&lt;br /&gt;    int y = 0;&lt;br /&gt;&lt;br /&gt;    class foobar&lt;br /&gt;    {&lt;br /&gt;    public:&lt;br /&gt;        bar(int &amp;y, int x) : y(y), x(x)&lt;br /&gt;        {&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        void operator ()(int z)&lt;br /&gt;        {&lt;br /&gt;            y += x*z;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    private:&lt;br /&gt;        int &amp;y, x;&lt;br /&gt;    } bar(y, x);&lt;br /&gt;&lt;br /&gt;    bar(1);&lt;br /&gt;    bar(2);&lt;br /&gt;&lt;br /&gt;    return y;&lt;br /&gt;}&lt;/pre&gt;Но с новым стандартом этот способ стал гораздо короче в записи:&lt;pre&gt;int foo(int x)&lt;br /&gt;{&lt;br /&gt;    int y = 0;&lt;br /&gt;&lt;br /&gt;    auto bar = [&amp;](int z)&lt;br /&gt;    { &lt;br /&gt;        y += x*z;&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    bar(1);&lt;br /&gt;    bar(2);&lt;br /&gt;&lt;br /&gt;    return y;&lt;br /&gt;}&lt;/pre&gt;Если сравнить с кодом, приведенным в начале - отличия минимальны! А уж если оно выглядит как утка, плавает как утка и крякает как утка - то чем не &lt;strike&gt;утка&lt;/strike&gt; локальная функция?!</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/3641558299842901915/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/3641558299842901915' title='Комментарии: 6'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/3641558299842901915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/3641558299842901915'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2010/09/local-functions-in-c.html' title='Local functions in C++'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-1436151224305369611</id><published>2010-07-16T16:26:00.005+04:00</published><updated>2010-07-16T16:40:27.421+04:00</updated><title type='text'>Mini HTTP server</title><content type='html'>Понадобился в приложении мини http сервер и http клиент. С клиентом все просто - можно просто воспользоваться функциями WinInet, написав к ним немного C++-оберток.&lt;br /&gt;&lt;br /&gt;Сервер можно довольно просто написать на Boost.Asio, но оказалось все уже изобретено - хорошо подошла &lt;a href=&quot;http://sourceforge.net/projects/cpp-netlib/&quot;&gt;cpp-netlib&lt;/a&gt;, вот пример кода сервера из &lt;a href=&quot;http://cpp-netlib.github.com/html/c___network_library/protocol/http/http_server_example.html&quot;&gt;документации&lt;/a&gt;:&lt;pre&gt;struct hello_world;&lt;br /&gt;typedef http::server&lt;hello_world&gt; server;&lt;br /&gt;&lt;br /&gt;struct hello_world {&lt;br /&gt;    void operator() (server::request const &amp;request,&lt;br /&gt;                     server::response &amp;response) {&lt;br /&gt;        response = server::response::stock_reply(&lt;br /&gt;            server::response::ok, &quot;Hello, World!&quot;);&lt;br /&gt;    }&lt;br /&gt;    void log(...) {&lt;br /&gt;        // do nothing&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;hello_world handler;&lt;br /&gt;http::server&lt;hello_world&gt; server_(&quot;0.0.0.0&quot;, &quot;80&quot;, handler);&lt;br /&gt;server_.run();&lt;/pre&gt;&lt;br /&gt;Библиотека, правда, довольно сырая - пришлось немного допиливать напильником чтобы скомпилировалась под VC++10. Но зато в комплекте идут приятные бонусы типа url_decode, которые вполне пригодились.&lt;br /&gt;&lt;br /&gt;PS. Забавно было когда сделал и запустил простейший сервер - FireFox грузил http://localhost/ очень долго, дольше чем http://www.yandex.ru/. Я уж было стал грешить на cpp-netlib и boost.asio, но оказалось IE открывал localhost мгновенно. Как я понял, FF долго ресолвит адрес localhost (причем каждый раз видимо заново), а http://127.0.0.1/ открывает мгновенно.</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/1436151224305369611/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/1436151224305369611' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/1436151224305369611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/1436151224305369611'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2010/07/mini-http-server.html' title='Mini HTTP server'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-175202597321283050</id><published>2010-06-21T19:57:00.004+04:00</published><updated>2010-06-21T20:15:49.406+04:00</updated><title type='text'>Lexical_cast using Spirit</title><content type='html'>Если хочется быстро заменить lexical_cast на парсеры из Spirit-а (для увеличения скорости парсинга, например):&lt;pre&gt;template &amp;lt;class T&amp;gt;&lt;br /&gt;inline T spirit_cast(std::string const &amp; input) &lt;br /&gt;{&lt;br /&gt;    T value;&lt;br /&gt;   &lt;br /&gt;    std::string::const_iterator begin = input.begin();&lt;br /&gt;    bool result = boost::spirit::qi::parse(begin, input.end(), value);&lt;br /&gt;&lt;br /&gt;    if (!result || begin != input.end())&lt;br /&gt;        throw std::bad_cast();&lt;br /&gt;&lt;br /&gt;    return value;&lt;br /&gt;};&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/175202597321283050/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/175202597321283050' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/175202597321283050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/175202597321283050'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2010/06/lexicalcast-using-spirit.html' title='Lexical_cast using Spirit'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-968464914824122748</id><published>2010-05-11T01:04:00.007+04:00</published><updated>2010-05-11T01:14:44.438+04:00</updated><title type='text'>News: good one and bad one</title><content type='html'>Новая Visual Studio 2010 огорчила: CRT &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ws0swas0.aspx&quot;&gt;поддерживает&lt;/a&gt; операционные системы не ниже WinXP SP2 и Win2003 SP1. Забудьте про Win2000 и даже WinXP без SP2. Это весьма печально.&lt;br /&gt;&lt;br /&gt;А новый Boost 1.43 порадовал: наконец-то добавили &lt;a href=&quot;http://dvinogradov.blogspot.com/2009/03/rangeex.html&quot;&gt;RangeEx&lt;/a&gt;. Это очень радует. Да что уж там - это будет мой следующий любимый релиз после 1.39, где был добавлен ForEach. ;)</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/968464914824122748/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/968464914824122748' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/968464914824122748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/968464914824122748'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2010/05/new-good-one-and-bad-one.html' title='News: good one and bad one'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-2794031316347202891</id><published>2010-05-01T14:51:00.004+04:00</published><updated>2010-05-01T17:58:39.008+04:00</updated><title type='text'>Comma operator returns</title><content type='html'>RAII в C++ - замечательная вещь. Количество кода сокращается на порядок (ну, если мыслить в двоичной системе исчисления, то в 2 раза - это на порядок, в 4 раза - уже на два порядка).&lt;br /&gt;&lt;br /&gt;Например, следующий код дает нам возможность показать курсорчик &quot;думаем...&quot; и убрать его когда мы додумаем:&lt;pre&gt;{&lt;br /&gt;    WTL::CWaitCursor WaitCursor;&lt;br /&gt;    SomeOperation();&lt;br /&gt;}&lt;/pre&gt;Не нужно руками восстанавливать курсор ни в случае если операция завершится успешно, ни в случае если кинет исключение. Все сделает деструктор класса CWaitCursor.&lt;br /&gt;&lt;br /&gt;Но вот такой случай заставляет нас вручную восстанавливать курсор:&lt;pre&gt;{&lt;br /&gt;    WTL::CWaitCursor WaitCursor;&lt;br /&gt;    if (!SomeOperation())&lt;br /&gt;    {&lt;br /&gt;        WaitCursor.Restore();&lt;br /&gt;        MessageBox(&quot;не получилось :(&quot;);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;Обидно, да? А не восстановим - получим MessageBox не с тем курсором.&lt;br /&gt;&lt;br /&gt;Вот тут-то и приходит на помощь старый добрый оператор запятая. Трюк в чем: при вычислении выражения все его аргументы живут до конца вычисления всего выражения. Построим выражение, где у нас будет &quot;думающий&quot; курсор и наша операция:&lt;pre&gt;if (WTL::CWaitCursor(), !SomeOperation())&lt;br /&gt;    MessageBox(&quot;не получилось :(&quot;);&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/2794031316347202891/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/2794031316347202891' title='Комментарии: 6'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/2794031316347202891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/2794031316347202891'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2010/05/comma-operator-returns.html' title='Comma operator returns'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-136259980381785648</id><published>2010-03-09T16:37:00.011+03:00</published><updated>2010-03-09T18:30:19.352+03:00</updated><title type='text'>Boost.Spirit in practice</title><content type='html'>&lt;p&gt;Я заметил, у разработчиков совершенно полярное отношение к библиотеке &lt;a href=&quot;http://boost-spirit.com/home&quot;&gt;Boost.Spirit&lt;/a&gt;: либо она им жутко не нравится, либо они фанатеют от нее. Конечно, описывать грамматику на C++ – занятие на любителя. Таким любителем оказался и я, когда познакомился со Спиритом. Хочу показать, как с помощью Спирита можно довольно просто решать повседневные задачи разбора текста.&lt;/p&gt;&lt;h2&gt;Простая задача – как два пальца&lt;/h2&gt;&lt;p&gt;На Спирите очень удобно писать маленькие парсеры «не отходя от кассы» – прямо в C++ коде. Вот например, как вы поступите если нужно распарсить строку вида «число-число», которая задает диапазон страниц для печати? На Спирите – одна строчка:&lt;/p&gt;&lt;pre&gt;bool ok = parse(First, Last, (uint_ &amp;gt;&amp;gt; L&quot;..&quot; &amp;gt;&amp;gt; uint_), MinMax) &amp;amp;&amp;amp; (First == Last));&lt;/pre&gt;&lt;h2&gt;Посложнее…&lt;/h2&gt;&lt;p&gt;Более того – можно ненамного сложнее создавать и парсеры побольше. В качестве примера рассмотрю &lt;a href=&quot;http://clubs.ya.ru/yandexbar/replies.xml?item_no=3565&quot;&gt;парсер мини-языка&lt;/a&gt;, который я делал для &lt;a href=&quot;http://clubs.ya.ru/yandexbar/replies.xml?item_no=3488&quot;&gt;API&lt;/a&gt; Яндекс.Бара. Задача была такова: для облегчения загрузки плагинов в баре используется XML, который довольно избыточный сам по себе. Но зато XML легче грузить из JavaScript-а, чем парсить произвольный формат (на JS пишутся расширения под FireFox, в том числе и Я.Бар).&lt;/p&gt;&lt;p&gt;Итак, что мне было нужно – имея на входе обычную инфиксную нотацию:&lt;/p&gt;&lt;pre&gt;Hello * Interval * 60 + xpath(&quot;number(//hello[id=&#39;&quot; # id # &quot;&#39;)&quot;, World)&lt;/pre&gt;&lt;p&gt;получить на выходе обычное AST в XML-формате:&lt;/p&gt;&lt;pre&gt;&amp;lt;add&amp;gt;&lt;br /&gt;    &amp;lt;mul&amp;gt;&lt;br /&gt;        &amp;lt;value-of name=&quot;Hello&quot;/&amp;gt;&lt;br /&gt;        &amp;lt;value-of name=&quot;Interval&quot;/&amp;gt;&lt;br /&gt;        &amp;lt;value type=&quot;number&quot;&amp;gt;60&amp;lt;/value&amp;gt;&lt;br /&gt;    &amp;lt;/mul&amp;gt;&lt;br /&gt;    &amp;lt;xpath&amp;gt;&lt;br /&gt;        &amp;lt;concat&amp;gt;&lt;br /&gt;            number(//hello[id=&#39;&amp;lt;value-of name=&quot;id&quot;/&amp;gt;&#39;)&lt;br /&gt;        &amp;lt;/concat&amp;gt;&lt;br /&gt;        &amp;lt;value-of name=&quot;World&quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/xpath&amp;gt;&lt;br /&gt;&amp;lt;/add&amp;gt;&lt;/pre&gt;&lt;p&gt;При этом нужно было оставить возможность кроме собственно формул писать обычный XML. Но все это обилие угловых скобочек, знаков равенства, кавычек и закрывающих тегов вводило меня в уныние и я решил очистить свой язык от этих сущностей. XML я решил записывать в таком виде:&lt;/p&gt;&lt;pre&gt;root&lt;br /&gt;    child1: текст&lt;br /&gt;        @attribute1: text&lt;br /&gt;        @attribute2 = формула&lt;br /&gt;        grandchild&lt;br /&gt;            grand-grand-child&lt;br /&gt;    child2 = формула&lt;/pre&gt;&lt;p&gt;То есть вложенность задается количеством табуляций, далее идет имя XML-ноды (элемента или атрибута). За ним - определенный символ, определяющий что идет далее: текст или формула. Текст нужно передавать на выход в «голом» виде, формулы – в виде AST.&lt;/p&gt;&lt;p&gt;Итого у меня два парсера – один разбирает строку чтобы получить имя ноды и текст или формулу. Второй – разбирает формулы, генерируя AST. Обработку количества табов я провожу снаружи старым добрым std::find_if.&lt;/p&gt;&lt;h2&gt;Парсинг строки. Semantic actions – через Boost.Bind&lt;/h2&gt;&lt;p&gt;Начну с более простого – разбора строк. Строки могут быть такие:&lt;/p&gt;&lt;pre&gt;tag&lt;br /&gt;tag: тест&lt;br /&gt;tag = формула&lt;br /&gt;= формула&lt;br /&gt;name :: (instance|widget) (setting|variable)&lt;br /&gt;name := формула&lt;/pre&gt;&lt;p&gt;Парсер получается очень простой:&lt;/p&gt;&lt;pre&gt;bool parse_definition(string::const_iterator &amp;amp;iter, string::const_iterator end, mini_xml &amp;amp;root)&lt;br /&gt;{&lt;br /&gt;    qi::rule&amp;lt;string::const_iterator, string(), space_type&amp;gt; id, any_string, scope, type;&lt;br /&gt;    id %= raw[lexeme[-char_(&#39;@&#39;) &amp;gt;&amp;gt; alpha &amp;gt;&amp;gt; *(alnum | &#39;_&#39; | &#39;-&#39; | (&#39;:&#39; &amp;gt;&amp;gt; alnum))]];&lt;br /&gt;    any_string %= lexeme[+char_];&lt;br /&gt;    scope %= raw[lit(&quot;widget&quot;) | lit(&quot;instance&quot;)];&lt;br /&gt;    type %= raw[lit(&quot;setting&quot;) | lit(&quot;variable&quot;)];&lt;br /&gt;&lt;br /&gt;    return phrase_parse(iter, end,&lt;br /&gt;        (&lt;br /&gt;            (id &amp;gt;&amp;gt; &quot;::&quot; &amp;gt;&amp;gt; scope &amp;gt;&amp;gt; type) [bind(&amp;amp;add_identifier, ref(root), _1)] |&lt;br /&gt;            (id &amp;gt;&amp;gt; &quot;:=&quot; &amp;gt;&amp;gt; any_string) [bind(&amp;amp;add_definition, ref(root), _1)] |&lt;br /&gt;            (id &amp;gt;&amp;gt; &#39;:&#39;  &amp;gt;&amp;gt; any_string) [bind(&amp;amp;add_raw, ref(root), _1)] |&lt;br /&gt;            (id &amp;gt;&amp;gt; &#39;=&#39;  &amp;gt;&amp;gt; any_string) [bind(&amp;amp;add_calculated, ref(root), _1)] |&lt;br /&gt;            (      &#39;=&#39;  &amp;gt;&amp;gt; any_string) [bind(&amp;amp;add_expression, ref(root), _1)] |&lt;br /&gt;            id [bind(&amp;amp;add_subnode, ref(root), _1)]&lt;br /&gt;        ),&lt;br /&gt;        space) &amp;amp;&amp;amp; (iter == end);&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Использование phrase_parse() вместо parse() позволило мне переложить на Спирит обработку white space (пробелов, табуляций и т.п.) внутри выражений. Это позволит писать как «tag:text», так и «tag : text». Причем мой код, как видно, освобожден от обработки пробелов – все делает phrase_parse(). Мне остается только использовать lexeme[] там, где я хочу отключить такое поведение, и raw[] там, где я хочу получить исходный текст без вырезания пробелов.&lt;/p&gt;&lt;p&gt;Кстати, напомню что синтаксис правил у Spirit-а такой:&lt;/p&gt;&lt;pre&gt;rule [semantic_action]&lt;/pre&gt;&lt;p&gt;То есть после каждого правила можно в квадратных скобках задать действие, которое будет выполняться, если правило «сработало».&lt;/p&gt;&lt;p&gt;В моем случае на каждый тип строки – свое поведение, плюс в самом начале для упрощения последующего кода я ввел отдельные правила типа id, any_string. Код, вызываемый при соответствии строки определенному правилу – указан через лямбда-функции, создаваемые с помощью boost::bind. Синтаксис bind-а очень прост:&lt;/p&gt;&lt;pre&gt;boost::bind(функция, аргумент, аргумент, ...)&lt;/pre&gt;&lt;p&gt;В качестве аргументов можно указывать специальные placeholder-ы (вида _1, _2, …), указывающие куда вставлять аргументы лямбда-функции. На выходе каждого парсера получается одно значение, его и передаем в качестве аргумента нашей функции. Например, парсер&lt;/p&gt;&lt;pre&gt;id &amp;gt;&amp;gt; &#39;=&#39; &amp;gt;&amp;gt; any_string&lt;/pre&gt;&lt;p&gt;сгенерирует на выходе пару строк (в виде boost::fusion::vector&amp;lt;string, string&amp;gt;), которую я передаю в качестве второго параметра моей функции add_calculated, которая имеет такой вот интерфейс:&lt;/p&gt;&lt;pre&gt;void add_calculated(mini_xml &amp;amp;root, fusion::vector&amp;lt;string, string&amp;gt; const &amp;amp;);&lt;/pre&gt;&lt;p&gt;Первый параметр, который мне нужно передать этой функции – это ссылка на root, поэтому вызов boost::bind выглядит так:&lt;/p&gt;&lt;pre&gt;bind(&amp;amp;add_calculated, ref(root), _1)&lt;/pre&gt;&lt;p&gt;Суммируя вместе правило и семантическое действие:&lt;/p&gt;&lt;pre&gt;(id &amp;gt;&amp;gt; &#39;=&#39; &amp;gt;&amp;gt; any_string) [bind(&amp;amp;add_calculated, ref(root), _1)]&lt;/pre&gt;&lt;h2&gt;Парсинг формулы. Semantic actions – через Boost.Phoenix&lt;/h2&gt;&lt;p&gt;Напомню какого вида функции мне нуно парсить:&lt;/p&gt;&lt;pre&gt;Hello * Interval * 60 + xpath(&quot;number(//hello[id=&#39;&quot; # id # &quot;&#39;)&quot;, World)&lt;/pre&gt;&lt;p&gt;При разборе формул могут встретиться:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;числа&lt;/li&gt;&lt;li&gt;булевы константы (true, false)&lt;/li&gt;&lt;li&gt;строки (в кавычках)&lt;/li&gt;&lt;li&gt;идентификаторы&lt;/li&gt;&lt;li&gt;вызовы функций&lt;/li&gt;&lt;li&gt;операции&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Для обработки результатов парсинга я создал один большой функтор и во всех семантических действиях использую его с помощью &lt;a href=&quot;http://www.boost.org/doc/libs/1_42_0/libs/spirit/phoenix/doc/html/index.html&quot;&gt;Booost.Phoenix&lt;/a&gt;. Как и у всех функторов, действия различаются не по именам, а по количеству и типам параметров.&lt;/p&gt;&lt;pre&gt;struct compiler&lt;br /&gt;{&lt;br /&gt;    // метки нужны для того, чтобы отличать друг от друга функции с одинаковыми аргументами&lt;br /&gt;    struct identifier{}; // метка «идентификатор»&lt;br /&gt;    struct function{}; // метка «функция»&lt;br /&gt;    struct parameter{}; // метка «параметр»&lt;br /&gt;    struct assignment{}; // метка «присваивание»&lt;br /&gt;&lt;br /&gt;    void operator()(mini_xml &amp;amp;node, std::string const &amp;amp;id, identifier) const; // идентификатор&lt;br /&gt;    void operator()(mini_xml &amp;amp;node, std::string const &amp;amp;id, function) const; // функция&lt;br /&gt;    void operator()(mini_xml &amp;amp;node, std::string const &amp;amp;id, parameter) const; // параметр функции&lt;br /&gt;    void operator()(mini_xml &amp;amp;node, std::string const &amp;amp;id, assignment) const; // присваивание&lt;br /&gt;    void operator()(mini_xml &amp;amp;node, std::string const &amp;amp;value, char const *type) const; // &amp;lt;value&amp;gt;&lt;br /&gt;    void operator()(mini_xml &amp;amp;node, mini_xml const &amp;amp;subnode) const;&lt;br /&gt;    void operator()(mini_xml &amp;amp;node, mini_xml const &amp;amp;subnode, std::string const &amp;amp;id, bool allow_join = false) const;&lt;br /&gt;};&lt;/pre&gt;&lt;p&gt;Внутри своей грамматики я добавил член класса -  тот самый мой функтор:&lt;/p&gt;&lt;pre&gt;template &amp;lt;typename Iterator&amp;gt;&lt;br /&gt;struct expression_grammar : grammar&amp;lt;Iterator, mini_xml(), space_type&amp;gt;&lt;br /&gt;{&lt;br /&gt;    expression_grammar() : expression_grammar::base_type(expressions)&lt;br /&gt;    {&lt;br /&gt;         expressions = ...;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    rule&amp;lt;Iterator, mini_xml(), space_type&amp;gt; expressions, ...;&lt;br /&gt;    boost::phoenix::function&amp;lt;compiler&amp;gt; op;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;PS. Тип mini_xml – это генерируемый XML.&lt;/p&gt;&lt;p&gt;Правила для парсинга идентификаторов, строк, чисел и булевых констант очень просты:&lt;/p&gt;&lt;pre&gt;id %= raw[lexeme[alpha &amp;gt;&amp;gt; *(alnum | &#39;_&#39; | (&#39;-&#39; &amp;gt;&amp;gt; alnum))]];&lt;br /&gt;quoted_string %= lexeme[&#39;&quot;&#39; &amp;gt;&amp;gt; *(char_ - &#39;&quot;&#39;) &amp;gt;&amp;gt; &#39;&quot;&#39;];&lt;br /&gt;numeric_value %= raw[lexeme[-(char_(&#39;+&#39;) | char_(&#39;-&#39;)) &amp;gt;&amp;gt; +digit &amp;gt;&amp;gt; -(char_(&#39;.&#39;) &amp;gt;&amp;gt; +digit)]];&lt;br /&gt;boolean_value %= raw[lit(&quot;true&quot;) | lit(&quot;false&quot;)];&lt;/pre&gt;&lt;p&gt;Все эти правила на выходе выдают строку (например, название идентификатора). Оператор %= в конструкции “правило %= парсер” позволяет сгенерированное парсером значение передать прямо на выход парсера. Далее можно прямо в других правилах использовать их результаты:&lt;/p&gt;&lt;pre&gt;string = quoted_string [op(_val, _1, &quot;string&quot;)];&lt;br /&gt;number = numeric_value [op(_val, _1, &quot;number&quot;)];&lt;br /&gt;boolean = boolean_value [op(_val, _1, &quot;bool&quot;)];&lt;br /&gt;empty = lit(&quot;empty&quot;) [op(_val, std::string(), &quot;empty&quot;)];&lt;br /&gt;identifier = id [op(_val, _1, compiler::identifier())];&lt;/pre&gt;&lt;p&gt;Как видно, здесь в каждом случае вызывается парсер, например, quoted_string, а далее его значение используется для вызова функтора op. В первом случае (правило string) на вход функтора придет: в качестве первого аргумента – то значение, которое формируется (в моем случае – элемент дерева XML), в качестве второго – результат работы парсера quoted_string, в третьем – срока “string”. И уже функтор сделает все необходимые действия с XML-деревом.&lt;/p&gt;&lt;p&gt;Определение функции не намного сложнее – в частности брагодаря тому, что я генерирую XML. Параметры функции достаточно просто «прикрепить» к xml-узлу функции в качестве «детей»:&lt;/p&gt;&lt;pre&gt;function =&lt;br /&gt;    id [op(_val, _1, compiler::function())]&lt;br /&gt;    &amp;gt;&amp;gt; &#39;(&#39;&lt;br /&gt;    &amp;gt;&amp;gt; -(parameter [op(_val, _1)] % &#39;,&#39;)&lt;br /&gt;    &amp;gt;&amp;gt; &#39;)&#39;;&lt;/pre&gt;&lt;p&gt;Выражение «parameter [op(_val, _1)]» как раз прикрепляет детей к функции: в функтор op передается родитель (узел функции, который только что заполнен с помощью «op(_val, _1, compiler::function())») и «ребенок» (узел параметра, который сгенерировал парсер parameter).&lt;/p&gt;&lt;p&gt;Итого, без учета бинарных и тернарных операций (операций с 2 и 3 аргументами, такие как */+-?:) получается следующее правило:&lt;/p&gt;&lt;pre&gt;factor =&lt;br /&gt;     function [_val = _1]&lt;br /&gt;     | boolean [_val = _1]&lt;br /&gt;     | empty [_val = _1]&lt;br /&gt;     | identifier [_val = _1]&lt;br /&gt;     | string [_val = _1]&lt;br /&gt;     | number [_val = _1]&lt;br /&gt;     | (&#39;(&#39; &amp;gt;&amp;gt; expression [_val = _1] &amp;gt;&amp;gt; &#39;)&#39;)&lt;br /&gt;     | (lit(&#39;!&#39;) [op(_val, &quot;not&quot;, compiler::function())] &amp;gt;&amp;gt; factor [op(_val, _1)])&lt;br /&gt;     | (lit(&#39;-&#39;) [op(_val, &quot;neg&quot;, compiler::function())] &amp;gt;&amp;gt; factor [op(_val, _1)])&lt;br /&gt;     | (&#39;+&#39; &amp;gt;&amp;gt; factor [_val = _1])&lt;br /&gt;     ;&lt;/pre&gt;&lt;p&gt;При обработке операций не следует забывать об их приоритете. Его легко реализовать «вкладывая» определения одной операции в определение другой:&lt;/p&gt;&lt;pre&gt;addition =&lt;br /&gt;    multiplication [_val = _1]&lt;br /&gt;    &amp;gt;&amp;gt; *( (&#39;+&#39; &amp;gt;&amp;gt; multiplication [op(_val, _1, &quot;add&quot;, true)])&lt;br /&gt;        | (&#39;-&#39; &amp;gt;&amp;gt; multiplication [op(_val, _1, &quot;sub&quot;, true)])&lt;br /&gt;        )&lt;br /&gt;    ;&lt;br /&gt;&lt;br /&gt;multiplication =&lt;br /&gt;    factor [_val = _1]&lt;br /&gt;    &amp;gt;&amp;gt; *( (&#39;*&#39; &amp;gt;&amp;gt; factor [op(_val, _1, &quot;mul&quot;, true)])&lt;br /&gt;        | (&#39;/&#39; &amp;gt;&amp;gt; factor [op(_val, _1, &quot;div&quot;, true)])&lt;br /&gt;        )&lt;br /&gt;    ;&lt;/pre&gt;&lt;p&gt;В данном случае функции умножения и деления распарсятся раньше, чем сложение и вычитание, так как умножение «вложено» в сложение. Это произойдет потому, что для сложения нужно разобрать сначала все внутренние правила, в том числе умножение, которое я вложил внутрь. Собственно, что и требовалось.&lt;/p&gt;&lt;h2&gt;Суммируя все вместе&lt;/h2&gt;&lt;p&gt;Весь исходный код можно взять здесь: &lt;a href=&quot;http://download.yandex.ru/bar/tools/easierxb-src.zip&quot;&gt;http://download.yandex.ru/bar/tools/easierxb-src.zip&lt;/a&gt; (внутри архива – проект для сборки под Windows и MacOS).&lt;/p&gt;&lt;p&gt;Пример входного файла: &lt;a href=&quot;http://download.yandex.ru/bar/tools/easierxb-example.zip&quot;&gt;http://download.yandex.ru/bar/tools/easierxb-example.zip&lt;/a&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/136259980381785648/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/136259980381785648' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/136259980381785648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/136259980381785648'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2010/03/boostspirit-in-practice.html' title='Boost.Spirit in practice'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-810085915964241574</id><published>2009-12-26T20:28:00.003+03:00</published><updated>2009-12-26T20:50:53.749+03:00</updated><title type='text'>Fast iterating over xml nodes by MSXML</title><content type='html'>Волею судеб мне приходится иметь дело с MSXML. &lt;a href=&quot;http://dvinogradov.blogspot.com/2009/12/calculating-xpath-expressions-by-msxml.html&quot;&gt;Не самая лучшая&lt;/a&gt; библиотека для работы с xml, но так вот получилось. И будучи любителем конструкции for_each, обход дочерних нод в моем коде выглядит так:&lt;pre&gt;BOOST_FOREACH(IXMLDOMNodePtr Node, ChildNodes(RootNode))&lt;br /&gt;    DoSomething(Node);&lt;/pre&gt;ChildNodes отдает &lt;a href=&quot;http://dvinogradov.blogspot.com/2008/04/accessing-old-school-apis-as-c.html&quot;&gt;пару итераторов&lt;/a&gt;, которые дают доступ по IXMLDOMNodeList через IXMLDOMNodeList::item(). Так вот на обработке большого xml файла заметил, что такая конструкция очень жутко тормозит. Я было хотел списать все это со словами &quot;ну этож MSXML тормозная - ничего не поделаешь...&quot;. Но как оказалось, правдива только первая часть этой фразы, а &quot;поделать&quot; все-таки что-то можно: если перебирать ноды через IXMLDOMNodeList::nextNode(), то вместо исходных 450мс получалось обойти ноды всего за 28:&lt;pre&gt;IXMLDOMNodeListPtr NodeList = RootNode-&gt;childNodes;&lt;br /&gt;for (IXMLDOMNodePtr Node; Node = NodeList-&gt;nextNode(); )&lt;br /&gt;    DoSomething(Node);&lt;/pre&gt;&lt;br /&gt;Видимо про такие случаи Спольски рассказывал &lt;a href=&quot;http://russian.joelonsoftware.com/Articles/BacktoBasics.html&quot;&gt;байку про маляра&lt;/a&gt;, который красил забор. За первый день он покрасил 20 метров забора, за второй - 10, за третий - всего 2 (цифры привожу по памяти, память дырявая). Когда его спросили, почему он так стал тормозить, ответ был великолепен: &quot;Насяльника, так за краской все дальше и дальше ходить!&quot; Видимо IXMLDOMNodeList::item() работает таким же образом, как и тот маляр, так что beware!</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/810085915964241574/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/810085915964241574' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/810085915964241574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/810085915964241574'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2009/12/fast-iterating-over-xml-nodes-by-msxml.html' title='Fast iterating over xml nodes by MSXML'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22921254.post-2346127229313919588</id><published>2009-12-02T17:59:00.003+03:00</published><updated>2009-12-02T18:20:38.213+03:00</updated><title type='text'>Calculating XPath expressions by MSXML</title><content type='html'>Если посчитать выражение XPath, что может получиться?&lt;br /&gt;Согласно &lt;a href=&quot;http://www.w3.org/TR/xpath#section-Introduction&quot;&gt;спецификации XPath&lt;/a&gt;:&lt;br /&gt;- список узлов&lt;br /&gt;- строка&lt;br /&gt;- логическое значение&lt;br /&gt;- число&lt;br /&gt;&lt;br /&gt;В .NET-е можно получить все из вышеперечисленного. А вот MSXML предлагает API только для получения списка узлов - &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms754523%28VS.85%29.aspx&quot;&gt;selectNodes&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;А что, если нужно получать значения других типов? Товарищи в форумах предлагают хитрый трюк - обернуть XPath выражение в XSLT, наложить его на нужный узел или документ, а в результате получить строку. Даже закрывая глаза на производительность такого трюка, таким способом не удается получить всю информацию - а именно информацию о типе результата. Ведь на выходе - всегда строка.&lt;br /&gt;&lt;br /&gt;Если выражение - &quot;наше&quot;, то тип результата сразу знаем. Но если &quot;чужое&quot;, и нам нужно узнать тип результата, то в таком случае мы в тупике - из полученной строки не получится извлечь тип. Вот, 123 - это строка &quot;123&quot; или число 123?&lt;br /&gt;&lt;br /&gt;Я узнал о следующем - в MSXML можно использовать JavaScript внутри XSLT. Тогда все становится достаточно просто:&lt;pre&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;br /&gt;&amp;lt;xsl:stylesheet version=&quot;1.0&quot;&lt;br /&gt;    xmlns:xsl=&quot;http://www.w3.org/1999/XSL/Transform&quot;&lt;br /&gt;    xmlns:msxsl=&quot;urn:schemas-microsoft-com:xslt&quot;&lt;br /&gt;    xmlns:js=&quot;urn:custom-javascript&quot;&lt;br /&gt;    exclude-result-prefixes=&quot;msxsl js&quot; &amp;gt;&lt;br /&gt;    &amp;lt;xsl:template match=&quot;/&quot;&amp;gt;&lt;br /&gt;        &amp;lt;xsl:variable name=&quot;value&quot; select=&quot;выражение&quot;/&amp;gt;&lt;br /&gt;        &amp;lt;result&amp;gt;&lt;br /&gt;            &amp;lt;value&amp;gt;&lt;br /&gt;                &amp;lt;xsl:value-of select=&quot;$value&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;/value&amp;gt;&lt;br /&gt;            &amp;lt;type&amp;gt;&lt;br /&gt;                &amp;lt;xsl:value-of select=&quot;js:TypeOf($value)&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;/type&amp;gt;&lt;br /&gt;        &amp;lt;/result&amp;gt;&lt;br /&gt;    &amp;lt;/xsl:template&amp;gt;&lt;br /&gt;    &amp;lt;msxsl:script language=&quot;JavaScript&quot; implements-prefix=&quot;js&quot;&amp;gt;&lt;br /&gt;        function TypeOf(strText) { return typeof strText; }&lt;br /&gt;    &amp;lt;/msxsl:script&amp;gt; &lt;br /&gt;&amp;lt;/xsl:stylesheet&amp;gt;&lt;/pre&gt;Специальная JavaScript-функция вернет нам тип выражения, остается только не думать какая у всего этого производительность ;)</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/2346127229313919588/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/2346127229313919588' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/2346127229313919588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/2346127229313919588'/><link rel='alternate' type='text/html' href='http://dvinogradov.blogspot.com/2009/12/calculating-xpath-expressions-by-msxml.html' title='Calculating XPath expressions by MSXML'/><author><name>Raider</name><uri>http://www.blogger.com/profile/14942191356667310038</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><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><title type='text'>Unicode: life-hack</title><content type='html'>Мой обычный сценарий при создании новых файлов в проекте:&lt;br /&gt;1. right click в Windows explorer на папке, где нужно создать файл&lt;br /&gt;2. выбор Create/&quot;New Text File.txt&quot;&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=&quot;http://sraider.2x4.ru/raider/blogger/2009/ShellNewUTF8.png&quot;&gt;&lt;br /&gt;Файл создается с BOM для UTF-8.</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='http://www.blogger.com/comment/fullpage/post/22921254/6224091986558861327' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/6224091986558861327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/6224091986558861327'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><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><title type='text'>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&quot;яблоко&quot;);&lt;br /&gt;    v.push_back(L&quot;апельсин&quot;);&lt;br /&gt;    v.push_back(L&quot;банан&quot;);&lt;br /&gt;    v.push_back(L&quot;персик&quot;);&lt;br /&gt;    v.push_back(L&quot;ёжик&quot;);&lt;br /&gt; &lt;br /&gt;    std::locale Locale(&quot;&quot;); // используем текущую локаль системы&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&quot;\n&quot;));&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;Буква &quot;ё&quot; явно пользуется у данной локали большей популярностью, чем все остальные. Ну прям хоть свою локаль пиши...</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='http://www.blogger.com/comment/fullpage/post/22921254/4226808316767578597' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/4226808316767578597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/4226808316767578597'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><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><title type='text'>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;В результате был выбран списоб &quot;г&quot; - вообще не писать сериализацию для tuple. Что я считаю полным бредом.&lt;br /&gt;&lt;br /&gt;PS. Если что - есть готовая &lt;a href=&quot;http://uint32t.blogspot.com/2008/03/update-serializing-boosttuple-using.html&quot;&gt;реализация через препроцессор&lt;/a&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='http://www.blogger.com/comment/fullpage/post/22921254/1242109153950030624' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/1242109153950030624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/1242109153950030624'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><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><title type='text'>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=&quot;http://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D1%80%D1%82%D0%B5%D0%B6&quot;&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;</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='http://www.blogger.com/comment/fullpage/post/22921254/818165887949434612' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/818165887949434612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/818165887949434612'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><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><title type='text'>Static member definition</title><content type='html'>Интересно, что статический член класса нельзя определить прямо в декларации класса:&lt;pre&gt;class foo&lt;br /&gt;{&lt;br /&gt;    static std::string s(&#39;hello world&#39;);&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(&#39;hello world&#39;);&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;, но часто оно того стоит.</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='http://www.blogger.com/comment/fullpage/post/22921254/3257485818160660334' title='Комментарии: 12'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/3257485818160660334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/3257485818160660334'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><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><title type='text'>Fast parsing</title><content type='html'>Чем вы парсите всякую &quot;мелочёвку&quot; типа целочисленных значений и прочее? Самописными парсерами с приправами вроде boost::tokenizer, boost::lexical_cast или даже atoi, etc.? Я обычно использовал boost::regex + lexical_cast. А тут недавно посмотрел презентацию &lt;a href=&quot;http://spirit.sourceforge.net/&quot;&gt;Spirit&lt;/a&gt;-а с BoostCon-а, даже немного проникся. Раньше как-то я к Спириту более равнодушен был.&lt;br /&gt;&lt;br /&gt;Описывать грамматику на C++ и потом давать компилятору [несколько минут] это компилировать - это, конечно, большой изврат. Но весьма забавно. И, говорят, работает потом очень быстро. Даже если нужно просто число распарсить - &lt;a href=&quot;http://groups.google.com/group/boost-list/browse_thread/thread/fb690fe1fb38a7f2/d2fc6f2436492a4a&quot;&gt;говорят&lt;/a&gt; быстрее всех парсит.&lt;pre&gt;std::string buffer(&quot;1234&quot;);&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;</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='http://www.blogger.com/comment/fullpage/post/22921254/458847073921618068' title='Комментарии: 10'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/458847073921618068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/458847073921618068'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><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><title type='text'>Visual Studio debug visualizers</title><content type='html'>Начиная с Visual Studio 2005 можно писать свои правила для отображения значений в отладчике. Более подробнее об этом можно почитать &lt;a href=&quot;http://www.virtualdub.org/blog/pivot/entry.php?id=120&quot;&gt;в блоге virtualdub&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Правила для Boost-овских типов можно найти на &lt;a href=&quot;https://svn.boost.org/trac/boost/wiki/DebuggerVisualizers&quot;&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;</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='http://www.blogger.com/comment/fullpage/post/22921254/2467619801791641093' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/2467619801791641093'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/2467619801791641093'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><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><title type='text'>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. В этой реализации (возможно и в каких-то других) он имеет буфер на небольшое количество символов - чтобы для небольших по размеру строк не лазить на кучу. Однако, размер буфера там не регулируется &quot;снаружи&quot;, да и нет смысла полагаться на конкретную реализацию - в другой стандартной библиотеке может быть все по-другому, да и эта может &quot;изменить&quot; в любой момент.&lt;br /&gt;&lt;br /&gt;В boost-е планируется к review нужный контейнер - &lt;a href=&quot;http://www.cs.aau.dk/~nesotto/boost/auto_buffer.zip&quot;&gt;&lt;b&gt;auto_buffer&lt;/b&gt;&lt;/a&gt;. Его еще немного допилят по интерфейсу, но пользоваться уже можно смело. В добавок он может, как и basic_string, расти в кучу, если нужно.</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='http://www.blogger.com/comment/fullpage/post/22921254/189703349703608512' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/189703349703608512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/189703349703608512'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><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><title type='text'>Presentations from BoostCon 2009</title><content type='html'>Доступны для скачивания &lt;a href=&quot;http://www.boostcon.com/community/wiki/show/private/2009/&quot;&gt;презентации с BoostCon 2009&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://dvinogradov.blogspot.com/feeds/708497786386191462/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/22921254/708497786386191462' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/708497786386191462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/708497786386191462'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><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><title type='text'>Range concatenation</title><content type='html'>В библиотеках для работы с итераторами (Boost.Iterators, Boost.Range, RangeEx) куча полезных вещей, но почему-то нигде не нашел механизма &quot;склеить&quot; два диапазона в один. Такого, чтобы можно было сделать вот так:&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=&quot;http://sraider.2x4.ru/raider/blogger/2009/concat.zip&quot;&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=&quot;http://thread.gmane.org/gmane.comp.lib.boost.user/46411/focus=46436&quot;&gt;вдохновил&lt;/a&gt; разработчиков Boost.Range добавить объединение диапазонов в RangeEx.</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='http://www.blogger.com/comment/fullpage/post/22921254/7793881145739472891' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/7793881145739472891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/7793881145739472891'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><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><title type='text'>Unexpected side effect</title><content type='html'>У каждого windows-процесса есть такая &quot;глобальная переменная&quot; - текущая директория. Её можно прочитать функцией GetCurrentDirectory() и изменить функцией SetCurrentDirectory().&lt;br /&gt;&lt;br /&gt;Вот чего я не ожидал, что её меняют кто попало. Например, она может изменится после вызовов GetSaveFileName(), GetOpenFileName(). В их документации такое поведение описано и дана возможность его отключить. Но другое дело, что она может измениться внутри... функции &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/dd145114(VS.85).aspx&quot;&gt;StartDoc()&lt;/a&gt;. Это такая функция, которая &quot;The StartDoc function starts a print job.&quot;, вобщем начинает печать.&lt;br /&gt;&lt;br /&gt;И вот почему такое может происходить: драйвер принтера Microsoft Office Document Image Writer - поставляется с Microsoft Office и &quot;печатает&quot; в TIFF-файлы. При получении новой задачи он спрашивает куда же нужно сохранить создаваемый файл. Делает он это, я полагаю, с помощью GetSaveFileName(). Та, в свою очередь, меняет текущую директорию. Я бы сказал что врядли рядовой программист будет ожидать такие побочные эффекты от функции StartDoc().&lt;br /&gt;&lt;br /&gt;Мораль (кроме той, что нужно драйвер подправить, добавив флажок OFN_NOCHANGEDIR) - старайтесь не менять глобального окружения кроме случаев, где это будет очевидно.</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='http://www.blogger.com/comment/fullpage/post/22921254/3311772043214824580' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/3311772043214824580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/3311772043214824580'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><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><title type='text'>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=&quot;http://article.gmane.org/gmane.comp.lib.boost.announce/226&quot;&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 &quot;.xml&quot; | wc -l&lt;/pre&gt;&lt;br /&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='http://www.blogger.com/comment/fullpage/post/22921254/1240382669154353328' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/1240382669154353328'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/1240382669154353328'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><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><title type='text'>Прибирайте за собой</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;Интересно, есть ли более элегантное решение?</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='http://www.blogger.com/comment/fullpage/post/22921254/747025629852150374' title='Комментарии: 9'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/747025629852150374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/747025629852150374'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><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><title type='text'>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; &quot;hello, world&quot;;&lt;br /&gt;stream &amp;gt;&amp;gt; variable;&lt;br /&gt;&lt;/pre&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='http://www.blogger.com/comment/fullpage/post/22921254/5233555861295328533' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/5233555861295328533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22921254/posts/default/5233555861295328533'/><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>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>