<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ars Longa, Vita Brevis &#187; C/C++</title>
	<atom:link href="http://blog.sjinks.pro/tag/cpp/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.sjinks.pro</link>
	<description>Quod scripsi, scripsi</description>
	<lastBuildDate>Mon, 06 Feb 2012 17:56:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>HTML Parser для Qt</title>
		<link>http://blog.sjinks.pro/c-cpp/qt/942-html-parser-qt/</link>
		<comments>http://blog.sjinks.pro/c-cpp/qt/942-html-parser-qt/#comments</comments>
		<pubDate>Wed, 07 Sep 2011 08:03:07 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[Qt]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[XML]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=942</guid>
		<description><![CDATA[Использование libxml2 для разбора документов HTML в Qt XML — это, конечно, хорошо, но очень часто требуется разбирать документы HTML, которые могут и не быть валидными. В Qt есть множество классов для работы с XML, но они не подходят для HTML, так как ошибки в HTML для них фатальны. Ниже приведён вариант парсера для HTML, основанный на [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/qt/942-html-parser-qt/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Использование libxml2 для разбора документов <a href="http://blog.sjinks.pro/tag/html/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  HTML">HTML</a> в <a href="http://blog.sjinks.pro/tag/qt/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Qt">Qt</a></em></h2>
<p><a href="http://blog.sjinks.pro/tag/xml/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  XML">XML</a> — это, конечно, хорошо, но очень часто требуется разбирать документы HTML, которые могут и не быть валидными.</p>
<p>В Qt есть множество классов для работы с XML, но они не подходят для HTML, так как ошибки в HTML для них фатальны.</p>
<p>Ниже приведён вариант парсера для HTML, основанный на библиотеке libxml2.<span id="more-942"></span></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p9425">
        <div class="code cpp-qt" id="p942code5">
<span class="co2">#ifndef LIBXML2READER_H</span><br />
<span class="co2">#define LIBXML2READER_H</span><br />
<br />
<span class="co2">#include &lt;QtXml/QXmlReader&gt;</span><br />
<span class="co2">#include &lt;libxml/xmlstring.h&gt;</span><br />
<br />
<span class="kw2">class</span> LibXml2ReaderPrivate<span class="sy0">;</span><br />
<br />
<span class="kw2">class</span> LibXml2Reader <span class="sy0">:</span> <span class="kw2">public</span> <span class="kw5">QXmlReader</span> <span class="br0">&#123;</span><br />
<span class="kw2">public</span><span class="sy0">:</span><br />
&nbsp; &nbsp; LibXml2Reader<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> ~LibXml2Reader<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">bool</span> feature<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> name<span class="sy0">,</span> <span class="kw4">bool</span><span class="sy0">*</span> ok <span class="sy0">=</span> 0<span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span> setFeature<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> name<span class="sy0">,</span> <span class="kw4">bool</span> value<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">bool</span> hasFeature<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> name<span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span><span class="sy0">*</span> property<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> name<span class="sy0">,</span> <span class="kw4">bool</span><span class="sy0">*</span> ok <span class="sy0">=</span> 0<span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span> setProperty<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> name<span class="sy0">,</span> <span class="kw4">void</span><span class="sy0">*</span> value<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">bool</span> hasProperty<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> name<span class="br0">&#41;</span> const<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span> setEntityResolver<span class="br0">&#40;</span><span class="kw5">QXmlEntityResolver</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw5">QXmlEntityResolver</span><span class="sy0">*</span> entityResolver<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span> setDTDHandler<span class="br0">&#40;</span><span class="kw5">QXmlDTDHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw5">QXmlDTDHandler</span><span class="sy0">*</span> DTDHandler<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span> setContentHandler<span class="br0">&#40;</span><span class="kw5">QXmlContentHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw5">QXmlContentHandler</span><span class="sy0">*</span> contentHandler<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span> setErrorHandler<span class="br0">&#40;</span><span class="kw5">QXmlErrorHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw5">QXmlErrorHandler</span><span class="sy0">*</span> errorHandler<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span> setLexicalHandler<span class="br0">&#40;</span><span class="kw5">QXmlLexicalHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw5">QXmlLexicalHandler</span><span class="sy0">*</span> lexicalHandler<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">void</span> setDeclHandler<span class="br0">&#40;</span><span class="kw5">QXmlDeclHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw5">QXmlDeclHandler</span><span class="sy0">*</span> declHandler<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> const<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">bool</span> parse<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QXmlInputSource</span><span class="sy0">&amp;</span> input<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">bool</span> parse<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QXmlInputSource</span><span class="sy0">*</span> input<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
<br />
<span class="kw2">private</span><span class="sy0">:</span><br />
&nbsp; &nbsp; Q_DISABLE_COPY<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><br />
&nbsp; &nbsp; Q_DECLARE_PRIVATE<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><br />
&nbsp; &nbsp; QScopedPointer<span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">&gt;</span> d_ptr<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">friend</span> <span class="kw2">class</span> LibXml2ReaderLocator<span class="sy0">;</span><br />
<span class="br0">&#125;</span><span class="sy0">;</span><br />
<br />
<span class="co2">#endif // LIBXML2READER_H</span>
        </div>
    </div>
</div>

          
<div class="codebox">
    <div class="the_code" style="" id="p9426">
        <div class="code cpp-qt" id="p942code6">
<span class="co2">#include &quot;libxml2reader.h&quot;</span><br />
<span class="co2">#include &lt;cstring&gt;</span><br />
<span class="co2">#include &lt;libxml/tree.h&gt;</span><br />
<span class="co2">#include &lt;libxml/parser.h&gt;</span><br />
<span class="co2">#include &lt;libxml/HTMLparser.h&gt;</span><br />
<br />
<span class="kw2">class</span> LibXml2ReaderLocator <span class="sy0">:</span> <span class="kw2">public</span> <span class="kw5">QXmlLocator</span> <span class="br0">&#123;</span><br />
<span class="kw2">public</span><span class="sy0">:</span><br />
&nbsp; &nbsp; LibXml2ReaderLocator<span class="br0">&#40;</span>LibXml2Reader<span class="sy0">*</span> r<span class="br0">&#41;</span> <span class="sy0">:</span> reader<span class="br0">&#40;</span>r<span class="br0">&#41;</span> <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">int</span> columnNumber<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> const<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw2">virtual</span> <span class="kw4">int</span> lineNumber<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> const<span class="sy0">;</span><br />
<span class="kw2">private</span><span class="sy0">:</span><br />
&nbsp; &nbsp; LibXml2Reader<span class="sy0">*</span> reader<span class="sy0">;</span><br />
<span class="br0">&#125;</span><span class="sy0">;</span><br />
<br />
<span class="kw2">class</span> LibXml2ReaderPrivate <span class="br0">&#123;</span><br />
<span class="kw2">public</span><span class="sy0">:</span><br />
&nbsp; &nbsp; ~LibXml2ReaderPrivate<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
<span class="kw2">private</span><span class="sy0">:</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="br0">&#40;</span>LibXml2Reader<span class="sy0">*</span> reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> startDocument<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> endDocument<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> startElement<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> name<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">**</span> attrs<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> endElement<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> name<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> comment<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> value<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> cdataBlock<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> value<span class="sy0">,</span> <span class="kw4">int</span> len<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> processingInstruction<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> target<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> data<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> characters<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> ch<span class="sy0">,</span> <span class="kw4">int</span> len<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> ignorableWhitespace<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> ch<span class="sy0">,</span> <span class="kw4">int</span> len<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> internalSubset<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> name<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> publicId<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> systemId<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw4">void</span> parse<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QXmlInputSource</span><span class="sy0">*</span> input<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; QScopedPointer<span class="sy0">&lt;</span>LibXml2ReaderLocator<span class="sy0">&gt;</span> locator<span class="sy0">;</span><br />
&nbsp; &nbsp; Q_DECLARE_PUBLIC<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><br />
&nbsp; &nbsp; LibXml2Reader<span class="sy0">*</span> q_ptr<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw5">QXmlEntityResolver</span><span class="sy0">*</span> entityresolver<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw5">QXmlDTDHandler</span><span class="sy0">*</span> &nbsp; &nbsp; dtdhandler<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw5">QXmlContentHandler</span><span class="sy0">*</span> contenthandler<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw5">QXmlErrorHandler</span><span class="sy0">*</span> &nbsp; errorhandler<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw5">QXmlLexicalHandler</span><span class="sy0">*</span> lexicalhandler<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw5">QXmlDeclHandler</span><span class="sy0">*</span> &nbsp; &nbsp;declhandler<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; xmlParserCtxt<span class="sy0">*</span> context<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">friend</span> <span class="kw2">class</span> LibXml2ReaderLocator<span class="sy0">;</span><br />
<span class="br0">&#125;</span><span class="sy0">;</span><br />
<br />
LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">LibXml2ReaderPrivate</span><span class="br0">&#40;</span>LibXml2Reader<span class="sy0">*</span> reader<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">:</span> q_ptr<span class="br0">&#40;</span>reader<span class="br0">&#41;</span><span class="sy0">,</span> entityresolver<span class="br0">&#40;</span>0<span class="br0">&#41;</span><span class="sy0">,</span> dtdhandler<span class="br0">&#40;</span>0<span class="br0">&#41;</span><span class="sy0">,</span> contenthandler<span class="br0">&#40;</span>0<span class="br0">&#41;</span><span class="sy0">,</span> errorhandler<span class="br0">&#40;</span>0<span class="br0">&#41;</span><span class="sy0">,</span> lexicalhandler<span class="br0">&#40;</span>0<span class="br0">&#41;</span><span class="sy0">,</span> declhandler<span class="br0">&#40;</span>0<span class="br0">&#41;</span><span class="sy0">,</span> context<span class="br0">&#40;</span>0<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; this<span class="sy0">-&gt;</span><span class="me3">locator</span>.<span class="me1">reset</span><span class="br0">&#40;</span><span class="kw1">new</span> LibXml2ReaderLocator<span class="br0">&#40;</span>reader<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">parse</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QXmlInputSource</span><span class="sy0">*</span> input<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; htmlSAXHandler handler<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw5">QByteArray</span> arr <span class="sy0">=</span> input<span class="sy0">-&gt;</span><span class="me3">data</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">toLocal8Bit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*</span> data <span class="sy0">=</span> arr.<span class="me1">data</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; std<span class="sy0">::</span><span class="kw3">memset</span><span class="br0">&#40;</span><span class="sy0">&amp;</span>handler<span class="sy0">,</span> 0<span class="sy0">,</span> <span class="kw3">sizeof</span><span class="br0">&#40;</span>handler<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">startDocument</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">startDocument</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">endDocument</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">endDocument</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">startElement</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">startElement</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">endElement</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">endElement</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">comment</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">comment</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">cdataBlock</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">cdataBlock</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">processingInstruction</span> <span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">processingInstruction</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">characters</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">characters</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">ignorableWhitespace</span> &nbsp; <span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">ignorableWhitespace</span><span class="sy0">;</span><br />
&nbsp; &nbsp; handler.<span class="me1">internalSubset</span> &nbsp; &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="sy0">&amp;</span>LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">internalSubset</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; this<span class="sy0">-&gt;</span><span class="me3">context</span> <span class="sy0">=</span> htmlCreatePushParserCtxt<span class="br0">&#40;</span><span class="sy0">&amp;</span>handler<span class="sy0">,</span> <span class="kw1">this</span><span class="sy0">,</span> data<span class="sy0">,</span> xmlStrlen<span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> xmlChar<span class="sy0">*&gt;</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="st0">&quot;&quot;</span><span class="sy0">,</span> XML_CHAR_ENCODING_NONE<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; htmlParseChunk<span class="br0">&#40;</span>this<span class="sy0">-&gt;</span><span class="me3">context</span><span class="sy0">,</span> <span class="kw2">NULL</span><span class="sy0">,</span> 0<span class="sy0">,</span> 1<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; htmlFreeParserCtxt<span class="br0">&#40;</span>this<span class="sy0">-&gt;</span><span class="me3">context</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; xmlCleanupParser<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">startDocument</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">startDocument</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">endDocument</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">endDocument</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">startElement</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> name<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">**</span> attrs<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QXmlAttributes</span> a<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> i <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>attrs<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span>attrs<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*</span> name &nbsp;<span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>attrs<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*</span> value <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>attrs<span class="br0">&#91;</span>i<span class="sy0">+</span>1<span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; i <span class="sy0">+=</span> <span class="nu0">2</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a.<span class="me1">append</span><span class="br0">&#40;</span>name<span class="sy0">,</span> <span class="st0">&quot;&quot;</span><span class="sy0">,</span> <span class="st0">&quot;&quot;</span><span class="sy0">,</span> value ? value <span class="sy0">:</span> name<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QString</span> uri <span class="sy0">=</span> <span class="st0">&quot;&quot;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QString</span> localName <span class="sy0">=</span> <span class="st0">&quot;&quot;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QString</span> qName <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>name<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">startElement</span><span class="br0">&#40;</span>uri<span class="sy0">,</span> localName<span class="sy0">,</span> qName<span class="sy0">,</span> a<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">endElement</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> name<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">endElement</span><span class="br0">&#40;</span><span class="kw5">QString</span><span class="br0">&#40;</span><span class="st0">&quot;&quot;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw5">QString</span><span class="br0">&#40;</span><span class="st0">&quot;&quot;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw5">QString</span><span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>name<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">comment</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> value<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="sy0">-&gt;</span><span class="me3">comment</span><span class="br0">&#40;</span><span class="kw5">QString</span><span class="sy0">::</span><span class="me2">fromLocal8Bit</span><span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>value<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">cdataBlock</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> value<span class="sy0">,</span> <span class="kw4">int</span> len<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="sy0">-&gt;</span><span class="me3">startCDATA</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QByteArray</span> arr<span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>value<span class="br0">&#41;</span><span class="sy0">,</span> len<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">characters</span><span class="br0">&#40;</span>arr<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="sy0">-&gt;</span><span class="me3">endCDATA</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">processingInstruction</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> target<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> data<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">processingInstruction</span><span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>target<span class="br0">&#41;</span><span class="sy0">,</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">characters</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> ch<span class="sy0">,</span> <span class="kw4">int</span> len<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">characters</span><span class="br0">&#40;</span><span class="kw5">QString</span><span class="sy0">::</span><span class="me2">fromLocal8Bit</span><span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>ch<span class="br0">&#41;</span><span class="sy0">,</span> len<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">ignorableWhitespace</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> ch<span class="sy0">,</span> <span class="kw4">int</span> len<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">ignorableWhitespace</span><span class="br0">&#40;</span><span class="kw5">QString</span><span class="sy0">::</span><span class="me2">fromLocal8Bit</span><span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>ch<span class="br0">&#41;</span><span class="sy0">,</span> len<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2ReaderPrivate<span class="sy0">::</span><span class="me2">internalSubset</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="sy0">*</span> c<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> name<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> publicId<span class="sy0">,</span> <span class="kw4">const</span> xmlChar<span class="sy0">*</span> systemId<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; LibXml2ReaderPrivate<span class="sy0">*</span> r <span class="sy0">=</span> <span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span>LibXml2ReaderPrivate<span class="sy0">*&gt;</span><span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>r<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QString</span> n<span class="br0">&#40;</span><span class="kw5">QString</span><span class="sy0">::</span><span class="me2">fromLocal8Bit</span><span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>name<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QString</span> p<span class="br0">&#40;</span><span class="kw5">QString</span><span class="sy0">::</span><span class="me2">fromLocal8Bit</span><span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>publicId<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QString</span> s<span class="br0">&#40;</span><span class="kw5">QString</span><span class="sy0">::</span><span class="me2">fromLocal8Bit</span><span class="br0">&#40;</span><span class="kw2">reinterpret_cast</span><span class="sy0">&lt;</span><span class="kw4">const</span> <span class="kw4">char</span><span class="sy0">*&gt;</span><span class="br0">&#40;</span>systemId<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="sy0">-&gt;</span><span class="me3">startDTD</span><span class="br0">&#40;</span>n<span class="sy0">,</span> p<span class="sy0">,</span> s<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; r<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="sy0">-&gt;</span><span class="me3">endDTD</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
<span class="br0">&#125;</span><br />
<br />
LibXml2Reader<span class="sy0">::</span><span class="me2">LibXml2Reader</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="sy0">:</span> d_ptr<span class="br0">&#40;</span><span class="kw1">new</span> LibXml2ReaderPrivate<span class="br0">&#40;</span><span class="kw1">this</span><span class="br0">&#41;</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
<span class="br0">&#125;</span><br />
<br />
LibXml2Reader<span class="sy0">::</span><span class="me2">~LibXml2Reader</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">bool</span> LibXml2Reader<span class="sy0">::</span><span class="me2">feature</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;,</span> <span class="kw4">bool</span><span class="sy0">*</span> ok<span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>ok<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">*</span>ok <span class="sy0">=</span> false<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> false<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2Reader<span class="sy0">::</span><span class="me2">setFeature</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;,</span> <span class="kw4">bool</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">bool</span> LibXml2Reader<span class="sy0">::</span><span class="me2">hasFeature</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> false<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span><span class="sy0">*</span> LibXml2Reader<span class="sy0">::</span><span class="me2">property</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;,</span> <span class="kw4">bool</span><span class="sy0">*</span> ok<span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>ok<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">*</span>ok <span class="sy0">=</span> false<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2Reader<span class="sy0">::</span><span class="me2">setProperty</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;,</span> <span class="kw4">void</span><span class="sy0">*</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">bool</span> LibXml2Reader<span class="sy0">::</span><span class="me2">hasProperty</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> false<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2Reader<span class="sy0">::</span><span class="me2">setEntityResolver</span><span class="br0">&#40;</span><span class="kw5">QXmlEntityResolver</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Q_D<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; d<span class="sy0">-&gt;</span><span class="me3">entityresolver</span> <span class="sy0">=</span> handler<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw5">QXmlEntityResolver</span><span class="sy0">*</span> LibXml2Reader<span class="sy0">::</span><span class="me2">entityResolver</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">const</span> LibXml2ReaderPrivate<span class="sy0">*</span> d <span class="sy0">=</span> this<span class="sy0">-&gt;</span><span class="me3">d_func</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> d<span class="sy0">-&gt;</span><span class="me3">entityresolver</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2Reader<span class="sy0">::</span><span class="me2">setDTDHandler</span><span class="br0">&#40;</span><span class="kw5">QXmlDTDHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Q_D<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; d<span class="sy0">-&gt;</span><span class="me3">dtdhandler</span> <span class="sy0">=</span> handler<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw5">QXmlDTDHandler</span><span class="sy0">*</span> LibXml2Reader<span class="sy0">::</span><span class="me2">DTDHandler</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">const</span> LibXml2ReaderPrivate<span class="sy0">*</span> d <span class="sy0">=</span> this<span class="sy0">-&gt;</span><span class="me3">d_func</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> d<span class="sy0">-&gt;</span><span class="me3">dtdhandler</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2Reader<span class="sy0">::</span><span class="me2">setContentHandler</span><span class="br0">&#40;</span><span class="kw5">QXmlContentHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Q_D<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; d<span class="sy0">-&gt;</span><span class="me3">contenthandler</span> <span class="sy0">=</span> handler<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw5">QXmlContentHandler</span><span class="sy0">*</span> LibXml2Reader<span class="sy0">::</span><span class="me2">contentHandler</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">const</span> LibXml2ReaderPrivate<span class="sy0">*</span> d <span class="sy0">=</span> this<span class="sy0">-&gt;</span><span class="me3">d_func</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> d<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2Reader<span class="sy0">::</span><span class="me2">setErrorHandler</span><span class="br0">&#40;</span><span class="kw5">QXmlErrorHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Q_D<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; d<span class="sy0">-&gt;</span><span class="me3">errorhandler</span> <span class="sy0">=</span> handler<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw5">QXmlErrorHandler</span><span class="sy0">*</span> LibXml2Reader<span class="sy0">::</span><span class="me2">errorHandler</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">const</span> LibXml2ReaderPrivate<span class="sy0">*</span> d <span class="sy0">=</span> this<span class="sy0">-&gt;</span><span class="me3">d_func</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> d<span class="sy0">-&gt;</span><span class="me3">errorhandler</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2Reader<span class="sy0">::</span><span class="me2">setLexicalHandler</span><span class="br0">&#40;</span><span class="kw5">QXmlLexicalHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Q_D<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; d<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span> <span class="sy0">=</span> handler<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw5">QXmlLexicalHandler</span><span class="sy0">*</span> LibXml2Reader<span class="sy0">::</span><span class="me2">lexicalHandler</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">const</span> LibXml2ReaderPrivate<span class="sy0">*</span> d <span class="sy0">=</span> this<span class="sy0">-&gt;</span><span class="me3">d_func</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> d<span class="sy0">-&gt;</span><span class="me3">lexicalhandler</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">void</span> LibXml2Reader<span class="sy0">::</span><span class="me2">setDeclHandler</span><span class="br0">&#40;</span><span class="kw5">QXmlDeclHandler</span><span class="sy0">*</span> handler<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Q_D<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; d<span class="sy0">-&gt;</span><span class="me3">declhandler</span> <span class="sy0">=</span> handler<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw5">QXmlDeclHandler</span><span class="sy0">*</span> LibXml2Reader<span class="sy0">::</span><span class="me2">declHandler</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">const</span> LibXml2ReaderPrivate<span class="sy0">*</span> d <span class="sy0">=</span> this<span class="sy0">-&gt;</span><span class="me3">d_func</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> d<span class="sy0">-&gt;</span><span class="me3">declhandler</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">bool</span> LibXml2Reader<span class="sy0">::</span><span class="me2">parse</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QXmlInputSource</span><span class="sy0">&amp;</span> input<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> this<span class="sy0">-&gt;</span><span class="me3">parse</span><span class="br0">&#40;</span><span class="sy0">&amp;</span>input<span class="br0">&#41;</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">bool</span> LibXml2Reader<span class="sy0">::</span><span class="me2">parse</span><span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QXmlInputSource</span><span class="sy0">*</span> input<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; Q_D<span class="br0">&#40;</span>LibXml2Reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>d<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; d<span class="sy0">-&gt;</span><span class="me3">contenthandler</span><span class="sy0">-&gt;</span><span class="me3">setDocumentLocator</span><span class="br0">&#40;</span>d<span class="sy0">-&gt;</span><span class="me3">locator</span>.<span class="me1">data</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; d<span class="sy0">-&gt;</span><span class="me3">parse</span><span class="br0">&#40;</span>input<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> true<span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">int</span> LibXml2ReaderLocator<span class="sy0">::</span><span class="me2">columnNumber</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> this<span class="sy0">-&gt;</span><span class="me3">reader</span><span class="sy0">-&gt;</span><span class="me3">d_func</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me3">context</span><span class="sy0">-&gt;</span><span class="me3">input</span><span class="sy0">-&gt;</span><span class="me3">col</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">int</span> LibXml2ReaderLocator<span class="sy0">::</span><span class="me2">lineNumber</span><span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> this<span class="sy0">-&gt;</span><span class="me3">reader</span><span class="sy0">-&gt;</span><span class="me3">d_func</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me3">context</span><span class="sy0">-&gt;</span><span class="me3">input</span><span class="sy0">-&gt;</span><span class="me3">line</span><span class="sy0">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Использование:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p9427">
        <div class="code cpp-qt" id="p942code7">
&nbsp; &nbsp; <span class="kw5">QByteArray</span> data<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw5">QXmlInputSource</span> src<span class="sy0">;</span><br />
&nbsp; &nbsp; LibXml2Reader reader<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="kw5">QDomDocument</span> doc<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="coMULTI">/* здесь читаем данные в data */</span><br />
<br />
&nbsp; &nbsp; src.<span class="me1">setData</span><span class="br0">&#40;</span>data<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; doc.<span class="me1">setContent</span><span class="br0">&#40;</span><span class="sy0">&amp;</span>src<span class="sy0">,</span> <span class="sy0">&amp;</span>reader<span class="br0">&#41;</span><span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="coMULTI">/* doc будет содержать дерево DOM, построенное из документа HTML */</span>
        </div>
    </div>
</div>

<p>В файл проекта нужно будет добавить две строки:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p9428">
        <div class="code text" id="p942code8">
INCLUDEPATH += /usr/include/libxml2<br />
LIBS &nbsp; &nbsp; &nbsp; &nbsp;+= -lxml2
        </div>
    </div>
</div>

<p>В <code>INCLUDEPATH</code> помещается путь к заголовочным файлам libxml2, в <code>LIBS</code> — опции компилятора для подключения библиотеки libxml2.</p>
<p>Скачать:</p>
<ul>
<li><a href='http://static.sjinks.info/wp-content/uploads/2011/09/libxml2reader.h'>libxml2reader.h</a></li>
<li><a href='http://static.sjinks.info/wp-content/uploads/2011/09/libxml2reader.cpp'>libxml2reader.cpp</a></li>
</ul>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/qt/942-html-parser-qt/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/c-cpp/qt/942-html-parser-qt/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>OpenMP, параллельный режим и утечки памяти в GNU Standard C++ Library</title>
		<link>http://blog.sjinks.pro/c-cpp/913-openmp-parallel-mode-memory-leak-libstdc/</link>
		<comments>http://blog.sjinks.pro/c-cpp/913-openmp-parallel-mode-memory-leak-libstdc/#comments</comments>
		<pubDate>Sat, 14 May 2011 04:23:09 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[OpenMP]]></category>
		<category><![CDATA[valgrind]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=913</guid>
		<description><![CDATA[Скрестив ужа и ежа, Мичурин получил колючую проволоку Стандартнуя библиотека GNU языка C++ поддерживает так называемый параллельный режим, который предоставляет экспериментальную поддержку параллельных алгоритмов из &#60;algorithm&#62; и &#60;numeric&#62;. Для включения данного режима исходный код нужно компилировать с опеределённым макросом _GLIBCXX_PARALLEL, либо явно подключать заголовочные файлы из &#60;parallel/&#62; и использовать пространство имён __gnu_parallel. В параллельном режиме [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/913-openmp-parallel-mode-memory-leak-libstdc/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Скрестив ужа и ежа, Мичурин получил колючую проволоку</em></h2>
<p>Стандартнуя библиотека GNU языка C++ поддерживает так называемый <dfn><a href="http://gcc.gnu.org/onlinedocs/libstdc++/manual/parallel_mode.html">параллельный режим</a></dfn>, который предоставляет экспериментальную поддержку параллельных алгоритмов из <code>&lt;algorithm&gt;</code> и <code>&lt;numeric&gt;</code>. Для включения данного режима исходный код нужно компилировать с опеределённым макросом <code>_GLIBCXX_PARALLEL</code>, либо явно подключать заголовочные файлы из <code>&lt;parallel/&gt;</code> и использовать пространство имён <code>__gnu_parallel</code>.</p>
<p>В параллельном режиме для реализации параллелизма используется <a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a>.</p>
<p>Как оказалось, при использовании в своей программе OpenMP и параллельного режима <code>libstdc++</code> можно столкнуться с очень неприятными и недокументированными особенностями реализации и получить незабываемое «удовольствие» при отладке.<span id="more-913"></span></p>
<p>Есть очень простая тестовая программа, использующая OpenMP:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91318">
        <div class="code cpp" id="p913code18">
<span class="co2">#include &lt;algorithm&gt;</span><br />
<span class="co2">#include &lt;vector&gt;</span><br />
<span class="co2">#include &lt;string&gt;</span><br />
<span class="co2">#include &lt;cstdlib&gt;</span><br />
<span class="co2">#include &lt;sstream&gt;</span><br />
<br />
<span class="kw4">typedef</span> std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span>std<span class="sy4">::</span><span class="me2">string</span><span class="sy1">&gt;</span> vector_t<span class="sy4">;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> T<span class="sy1">&gt;</span><br />
std<span class="sy4">::</span><span class="me2">string</span> toString<span class="br0">&#40;</span><span class="kw4">const</span> T<span class="sy3">&amp;</span> x<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">stringstream</span> converter<span class="sy4">;</span><br />
&nbsp; &nbsp; converter <span class="sy1">&lt;&lt;</span> x<span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> converter.<span class="me1">str</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> T<span class="sy1">&gt;</span><br />
T gen<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> std<span class="sy4">::</span><span class="kw3">rand</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;&gt;</span><br />
std<span class="sy4">::</span><span class="me2">string</span> gen<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> toString<span class="br0">&#40;</span>std<span class="sy4">::</span><span class="kw3">rand</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">int</span> main<span class="br0">&#40;</span><span class="kw4">int</span> argc, <span class="kw4">char</span><span class="sy2">**</span> argv<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; vector_t v1<span class="br0">&#40;</span>65536<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; vector_t v2<span class="br0">&#40;</span>65536<span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; <span class="co2">#pragma omp parallel shared(v1, v2)</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp single nowait</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp task shared(v1)</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">generate</span><span class="br0">&#40;</span>v1.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, v1.<span class="me1">end</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, gen<span class="sy1">&lt;</span>vector_t<span class="sy4">::</span><span class="me2">value_type</span><span class="sy1">&gt;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">generate</span><span class="br0">&#40;</span>v2.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, v2.<span class="me1">end</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, gen<span class="sy1">&lt;</span>vector_t<span class="sy4">::</span><span class="me2">value_type</span><span class="sy1">&gt;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Программа использует два потока для заполнения двух векторов случайными строками. При использовании параллельного режима теоретически может использоваться более двух потоков — <code>libstdc++</code> имеет параллельную версию алгоритма <code>std::generate</code>.</p>
<p>Сборка программы выполняется так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91319">
        <div class="code bash" id="p913code19">
<span class="kw2">g++</span> <span class="re5">-fopenmp</span> <span class="re5">-O1</span> <span class="re5">-g3</span> test.cpp <span class="re5">-o</span> test1<br />
<span class="kw2">g++</span> <span class="re5">-fopenmp</span> <span class="re5">-O1</span> <span class="re5">-g3</span> -D_GLIBCXX_PARALLEL test.cpp <span class="re5">-o</span> test2
        </div>
    </div>
</div>

<p><code>test1</code> не использует параллельный режим, <code>test2</code> использует.</p>
<p>Самое интересное начинается при запуске обеих программ под <a href="http://blog.sjinks.pro/tag/valgrind/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  valgrind">Valgrind</a> — с целью нахождения утечек памяти.<br />
Запуск осуществляется так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91320">
        <div class="code bash" id="p913code20">
<span class="kw2">valgrind</span> <span class="re5">--num-callers</span>=20 <span class="re5">--leak-check</span>=<span class="kw2">yes</span> <span class="re5">--leak-resolution</span>=high <span class="re5">--show-reachable</span>=<span class="kw2">yes</span> .<span class="sy0">/</span>test1<br />
<span class="kw2">valgrind</span> <span class="re5">--num-callers</span>=20 <span class="re5">--leak-check</span>=<span class="kw2">yes</span> <span class="re5">--leak-resolution</span>=high <span class="re5">--show-reachable</span>=<span class="kw2">yes</span> .<span class="sy0">/</span>test2
        </div>
    </div>
</div>

<p>Получились такие результаты:</p>
<p><strong>Без использования параллельного режима:</strong></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91321">
        <div class="code text" id="p913code21">
==10569== HEAP SUMMARY:<br />
==10569== &nbsp; &nbsp; in use at exit: 2,936 bytes in 6 blocks<br />
==10569== &nbsp; total heap usage: 262,152 allocs, 262,146 frees, 75,956,839 bytes allocated<br />
…<br />
==10569== LEAK SUMMARY:<br />
==10569== &nbsp; &nbsp;definitely lost: 0 bytes in 0 blocks<br />
==10569== &nbsp; &nbsp;indirectly lost: 0 bytes in 0 blocks<br />
==10569== &nbsp; &nbsp; &nbsp;possibly lost: 912 bytes in 3 blocks<br />
==10569== &nbsp; &nbsp;still reachable: 2,024 bytes in 3 blocks<br />
==10569== &nbsp; &nbsp; &nbsp; &nbsp; suppressed: 0 bytes in 0 blocks
        </div>
    </div>
</div>

<p>Явных утечек памяти нет; по поводу <code>still reachable</code> разработчики говорят, что <a href="http://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg224364.html">это не ошибка</a> (я с ними согласен).</p>
<p>Таким образом, при использовании OpenMP и стандартного режима libstdc++ всё выглядит довольно-таки хорошо.</p>
<p><strong>С использованием параллельного режима:</strong></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91322">
        <div class="code text" id="p913code22">
==10628== HEAP SUMMARY:<br />
==10628== &nbsp; &nbsp; in use at exit: 4,522,599 bytes in 131,078 blocks<br />
==10628== &nbsp; total heap usage: 262,402 allocs, 131,324 frees, 77,022,766 bytes allocated<br />
…<br />
==10628== LEAK SUMMARY:<br />
==10628== &nbsp; &nbsp;definitely lost: 4,519,595 bytes in 131,070 blocks<br />
==10628== &nbsp; &nbsp;indirectly lost: 0 bytes in 0 blocks<br />
==10628== &nbsp; &nbsp; &nbsp;possibly lost: 980 bytes in 5 blocks<br />
==10628== &nbsp; &nbsp;still reachable: 2,024 bytes in 3 blocks<br />
==10628== &nbsp; &nbsp; &nbsp; &nbsp; suppressed: 0 bytes in 0 blocks
        </div>
    </div>
</div>

<p>Сразу бросается в глаза: <strong>definitely lost: 4,519,595 bytes in 131,070 blocks</strong> — это те самые два вектора (2×65536) плюс еще чуть-чуть.</p>
<p>Трасса вызовов выглядит так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91323">
        <div class="code text" id="p913code23">
==10628== 34 bytes in 1 blocks are possibly lost in loss record 2 of 10<br />
==10628== &nbsp; &nbsp;at 0x4C28B42: operator new(unsigned long) (vg_replace_malloc.c:261)<br />
==10628== &nbsp; &nbsp;by 0x4ECBE6C: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator&lt;char&gt; const&amp;) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.14)<br />
==10628== &nbsp; &nbsp;by 0x402FBB: char* std::string::_S_construct&lt;char*&gt;(char*, char*, std::allocator&lt;char&gt; const&amp;, std::forward_iterator_tag) (basic_string.tcc:138)<br />
==10628== &nbsp; &nbsp;by 0x403002: std::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt;::basic_string&lt;char*&gt;(char*, char*, std::allocator&lt;char&gt; const&amp;) (basic_string.h:1649)<br />
==10628== &nbsp; &nbsp;by 0x403234: std::string toString&lt;int&gt;(int const&amp;) (sstream:127)<br />
==10628== &nbsp; &nbsp;by 0x401CBD: std::string gen&lt;std::string&gt;() (test.cpp:27)<br />
==10628== &nbsp; &nbsp;by 0x402373: _ZN14__gnu_parallel46__for_each_template_random_access_workstealingIN9__gnu_cxx17__normal_iteratorIPSsNSt9__cxx19986vectorISsSaISsEEEEEPFSsvENS_19__generate_selectorIS8_EENS_12_DummyReductEbEET0_T_SF_SE_RT1_T2_T3_RSJ_NSt15iterator_traitsISF_E15difference_typeE.omp_fn.4 (for_each_selectors.h:77)<br />
==10628== &nbsp; &nbsp;by 0x40D68B: std::string (*__gnu_parallel::__for_each_template_random_access_workstealing&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), __gnu_parallel::__generate_selector&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;, __gnu_parallel::_DummyReduct, bool&gt;(__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, __gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), __gnu_parallel::__generate_selector&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;&amp;, __gnu_parallel::_DummyReduct, bool, bool&amp;, std::iterator_traits&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;::difference_type))() (workstealing.h:297)<br />
==10628== &nbsp; &nbsp;by 0x40D77C: std::string (*__gnu_parallel::__for_each_template_random_access&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), __gnu_parallel::__generate_selector&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;, __gnu_parallel::_DummyReduct, bool&gt;(__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, __gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), __gnu_parallel::__generate_selector&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;&amp;, __gnu_parallel::_DummyReduct, bool, bool&amp;, std::iterator_traits&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;::difference_type, __gnu_parallel::_Parallelism))() (for_each.h:86)<br />
==10628== &nbsp; &nbsp;by 0x40D814: void std::__parallel::__generate_switch&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)()&gt;(__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, __gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), std::random_access_iterator_tag, __gnu_parallel::_Parallelism) (algo.h:1556)<br />
==10628== &nbsp; &nbsp;by 0x40258C: main.omp_fn.1 (algo.h:1584)<br />
==10628== &nbsp; &nbsp;by 0x53C1A12: ??? (in /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0)<br />
==10628== &nbsp; &nbsp;by 0x53C3748: ??? (in /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0)<br />
==10628== &nbsp; &nbsp;by 0x53C1FBD: ??? (in /usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0)<br />
==10628== &nbsp; &nbsp;by 0x57E4D8B: start_thread (pthread_create.c:304)<br />
==10628== &nbsp; &nbsp;by 0x5AE204C: clone (clone.S:112)
        </div>
    </div>
</div>

          
<div class="codebox">
    <div class="the_code" style="" id="p91324">
        <div class="code text" id="p913code24">
==10628== 2,259,752 bytes in 65,534 blocks are definitely lost in loss record 9 of 10<br />
==10628== &nbsp; &nbsp;at 0x4C28B42: operator new(unsigned long) (vg_replace_malloc.c:261)<br />
==10628== &nbsp; &nbsp;by 0x4ECBE6C: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator&lt;char&gt; const&amp;) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.14)<br />
==10628== &nbsp; &nbsp;by 0x402FBB: char* std::string::_S_construct&lt;char*&gt;(char*, char*, std::allocator&lt;char&gt; const&amp;, std::forward_iterator_tag) (basic_string.tcc:138)<br />
==10628== &nbsp; &nbsp;by 0x403002: std::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt;::basic_string&lt;char*&gt;(char*, char*, std::allocator&lt;char&gt; const&amp;) (basic_string.h:1649)<br />
==10628== &nbsp; &nbsp;by 0x403234: std::string toString&lt;int&gt;(int const&amp;) (sstream:127)<br />
==10628== &nbsp; &nbsp;by 0x401CBD: std::string gen&lt;std::string&gt;() (test.cpp:27)<br />
==10628== &nbsp; &nbsp;by 0x402373: _ZN14__gnu_parallel46__for_each_template_random_access_workstealingIN9__gnu_cxx17__normal_iteratorIPSsNSt9__cxx19986vectorISsSaISsEEEEEPFSsvENS_19__generate_selectorIS8_EENS_12_DummyReductEbEET0_T_SF_SE_RT1_T2_T3_RSJ_NSt15iterator_traitsISF_E15difference_typeE.omp_fn.4 (for_each_selectors.h:77)<br />
==10628== &nbsp; &nbsp;by 0x40D68B: std::string (*__gnu_parallel::__for_each_template_random_access_workstealing&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), __gnu_parallel::__generate_selector&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;, __gnu_parallel::_DummyReduct, bool&gt;(__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, __gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), __gnu_parallel::__generate_selector&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;&amp;, __gnu_parallel::_DummyReduct, bool, bool&amp;, std::iterator_traits&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;::difference_type))() (workstealing.h:297)<br />
==10628== &nbsp; &nbsp;by 0x40D77C: std::string (*__gnu_parallel::__for_each_template_random_access&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), __gnu_parallel::__generate_selector&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;, __gnu_parallel::_DummyReduct, bool&gt;(__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, __gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), __gnu_parallel::__generate_selector&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;&amp;, __gnu_parallel::_DummyReduct, bool, bool&amp;, std::iterator_traits&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt; &gt;::difference_type, __gnu_parallel::_Parallelism))() (for_each.h:86)<br />
==10628== &nbsp; &nbsp;by 0x40D814: void std::__parallel::__generate_switch&lt;__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)()&gt;(__gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, __gnu_cxx::__normal_iterator&lt;std::string*, std::__cxx1998::vector&lt;std::string, std::allocator&lt;std::string&gt; &gt; &gt;, std::string (*)(), std::random_access_iterator_tag, __gnu_parallel::_Parallelism) (algo.h:1556)<br />
==10628== &nbsp; &nbsp;by 0x402566: main.omp_fn.0 (algo.h:1584)<br />
==10628== &nbsp; &nbsp;by 0x40297B: main (test.cpp:35)
        </div>
    </div>
</div>

<p>Ключ к пониманию даёт <code>workstealing.h</code> (адрес 0x40D68B в трассе). В коде есть такая строка:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91325">
        <div class="code cpp" id="p913code25">
<ol class="cpp" style="font-family:monospace;" start="139"><li class="li1"><div class="de1"><span class="co2"># &nbsp; &nbsp; pragma omp parallel shared(__busy) num_threads(__num_threads)</span></div></li>
</ol>
        </div>
    </div>
</div>

<p>Что получается: грубо говоря, следующее:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91326">
        <div class="code cpp" id="p913code26">
<span class="co2">#pragma omp parallel</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2">#pragma omp single nowait</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp task</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// …</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co2">#pragma omp parallel num_threads(__num_threads)</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// …</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Есть предположение, что виноват <code>num_threads</code> во вложенном <code>omp parallel</code> (по крайней мере, если убрать <code>num_threads</code>, то утечки исчезают — но последствия изменений я не проверял).</p>
<p>Вроде бы в <code>num_threads</code> криминала нет — вложенный блок <code>omp parallel</code> создаёт новую группу потоков (при условии, что вложенность разрешена — либо через <code>OMP_NESTED=TRUE</code>, либо через вызов <code>omp_set_nested(true)</code>), а не изменяет количество потоков в существующей группе, но где-то что-то не до конца продумано.</p>
<p>Мораль на сегодняшний день такова: при использовании OpenMP параллельный режим глобально лучше не включать; при необходимости использования параллельных алгоритмов использовать алгоритм из пространства имён <code>__gnu_parallel</code> (и ни в коем случае не делать этого внутри блоков OpenMP).</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/913-openmp-parallel-mode-memory-leak-libstdc/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/c-cpp/913-openmp-parallel-mode-memory-leak-libstdc/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>k-путевое слияние на C++</title>
		<link>http://blog.sjinks.pro/c-cpp/911-k-way-merge-sort-cpp/</link>
		<comments>http://blog.sjinks.pro/c-cpp/911-k-way-merge-sort-cpp/#comments</comments>
		<pubDate>Fri, 06 May 2011 11:40:48 +0000</pubDate>
		<dc:creator>Wandering Soul</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[алгоритмы]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=911</guid>
		<description><![CDATA[Пример простейшей реализации k-путевго слиянием Алгоритмы слияния — семейство алгоритмов, последовательно обрабатывающие отсортированные списки и генерирующие один или более отсортированных списков на выходе. Слияние используется в алгоритме сортировки слиянием (например, функция bitonic_merge() в алгоритме битонной сортировки). k-путевое слияние использует k отсортированных списков; в данном примере генерируется один выходной список, размер которого равен размеру всех входных списков. k-путевое [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/911-k-way-merge-sort-cpp/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Пример простейшей реализации k-путевго слиянием</em></h2>
<p>Алгоритмы слияния — семейство алгоритмов, последовательно обрабатывающие отсортированные списки и генерирующие один или более отсортированных списков на выходе. Слияние используется в алгоритме сортировки слиянием (например, функция <code>bitonic_merge()</code> в <strong><a href="http://blog.sjinks.pro/c-cpp/908-bitonic-mergesort-cpp/">алгоритме битонной сортировки</a></strong>).</p>
<p>k-путевое слияние использует <var>k</var> отсортированных списков; в данном примере генерируется один выходной список, размер которого равен размеру всех входных списков.</p>
<p>k-путевое слияние может быть очень эффективно при сортировке очень больших объёмов данных: большой исходный файл разбивается на несколько файлов меньшего размера, каждый из которых можно отсортировать в оперативной памяти; после сортировки получается <var>n</var> отсортированных файлов. Над отсортированными файлами выполняется <a href="http://blog.sjinks.pro/tag/sort/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  сортировка">сортировка</a> многопутевого слияния (возможно, в несколько этапов), на выходе получается отсортированный исходный файл.</p>
<p>Как сортировку, так и слияние можно выполнять параллельно (например, при использовании быстрой сортировки можно использовать конструкцию <code>task</code> из <a href="http://blog.sjinks.pro/tag/openmp/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  OpenMP">OpenMP</a> 3.0; слияния же можно проводить в несколько потоков потоков — например, 100 файлов можно отсортировать в 4 потока, используя 25-путевое слияние — правда, это создаст дополнительную нагрузку на диск).<span id="more-911"></span></p>
<p>Ниже приведена простейшая реализация k-путевого слияния на C++.</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91129">
        <div class="code cpp" id="p911code29">
<span class="co2">#include &lt;vector&gt;</span><br />
<span class="co2">#include &lt;algorithm&gt;</span><br />
<span class="co2">#include &lt;utility&gt;</span><br />
<span class="co2">#include &lt;functional&gt;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> InputIterator, <span class="kw2">typename</span> OutputIterator, <span class="kw2">typename</span> Compare<span class="sy1">&gt;</span><br />
OutputIterator n_way_merge<span class="br0">&#40;</span><span class="kw4">const</span> std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span>std<span class="sy4">::</span><span class="me2">pair</span><span class="sy1">&lt;</span>InputIterator, InputIterator<span class="sy1">&gt;</span> <span class="sy1">&gt;</span><span class="sy3">&amp;</span> in, OutputIterator out, Compare comp<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; OutputIterator res <span class="sy1">=</span> out<span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw4">typedef</span> <span class="kw2">typename</span> std<span class="sy4">::</span><span class="me2">iterator_traits</span><span class="sy1">&lt;</span>InputIterator<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">value_type</span> value_t<span class="sy4">;</span><br />
&nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span>value_t<span class="sy1">&gt;</span> l<span class="br0">&#40;</span>in.<span class="me1">size</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span>std<span class="sy4">::</span><span class="me2">pair</span><span class="sy1">&lt;</span>InputIterator, InputIterator<span class="sy1">&gt;</span> <span class="sy1">&gt;</span> f<span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw4">typedef</span> <span class="kw2">typename</span> std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span>std<span class="sy4">::</span><span class="me2">pair</span><span class="sy1">&lt;</span>InputIterator, InputIterator<span class="sy1">&gt;</span> <span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">iterator</span> iterator_t<span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; f.<span class="me1">assign</span><span class="br0">&#40;</span>in.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, in.<span class="me1">end</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; iterator_t b <span class="sy1">=</span> f.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; std<span class="sy4">::</span><span class="kw4">size_t</span> i <span class="sy1">=</span> <span class="nu0">0</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span>b <span class="sy3">!</span><span class="sy1">=</span> f.<span class="me1">end</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">pair</span><span class="sy1">&lt;</span>InputIterator, InputIterator<span class="sy1">&gt;</span><span class="sy3">&amp;</span> x <span class="sy1">=</span> <span class="sy2">*</span>b<span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>x.<span class="me1">first</span> <span class="sy1">==</span> x.<span class="me1">second</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; b <span class="sy1">=</span> f.<span class="me1">erase</span><span class="br0">&#40;</span>b<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l.<span class="me1">erase</span><span class="br0">&#40;</span>l.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy2">+</span> i<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l<span class="br0">&#91;</span>i<span class="br0">&#93;</span> <span class="sy1">=</span> <span class="sy2">*</span>x.<span class="me1">first</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy2">++</span>b<span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy2">++</span>i<span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span><span class="sy3">!</span>f.<span class="me1">empty</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">typename</span> std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span>value_t<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">iterator</span> p <span class="sy1">=</span> std<span class="sy4">::</span><span class="me2">min_element</span><span class="br0">&#40;</span>l.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, l.<span class="me1">end</span><span class="br0">&#40;</span><span class="br0">&#41;</span>, comp<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy2">*</span>out <span class="sy1">=</span> <span class="sy2">*</span>p<span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy2">++</span>out<span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; i <span class="sy1">=</span> p <span class="sy2">-</span> l.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy2">++</span>f<span class="br0">&#91;</span>i<span class="br0">&#93;</span>.<span class="me1">first</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>f<span class="br0">&#91;</span>i<span class="br0">&#93;</span>.<span class="me1">first</span> <span class="sy1">==</span> f<span class="br0">&#91;</span>i<span class="br0">&#93;</span>.<span class="me1">second</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f.<span class="me1">erase</span><span class="br0">&#40;</span>f.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy2">+</span> i<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l.<span class="me1">erase</span><span class="br0">&#40;</span>p<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; l<span class="br0">&#91;</span>i<span class="br0">&#93;</span> <span class="sy1">=</span> <span class="sy2">*</span>f<span class="br0">&#91;</span>i<span class="br0">&#93;</span>.<span class="me1">first</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> out<span class="sy4">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> InputIterator, <span class="kw2">typename</span> OutputIterator<span class="sy1">&gt;</span><br />
OutputIterator n_way_merge<span class="br0">&#40;</span><span class="kw4">const</span> std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span>std<span class="sy4">::</span><span class="me2">pair</span><span class="sy1">&lt;</span>InputIterator, InputIterator<span class="sy1">&gt;</span> <span class="sy1">&gt;</span><span class="sy3">&amp;</span> in, OutputIterator out<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">typedef</span> <span class="kw2">typename</span> std<span class="sy4">::</span><span class="me2">iterator_traits</span><span class="sy1">&lt;</span>InputIterator<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">value_type</span> value_t<span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> n_way_merge<span class="br0">&#40;</span>in, out, std<span class="sy4">::</span><span class="me2">less</span><span class="sy1">&lt;</span>value_t<span class="sy1">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Пример использования:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p91130">
        <div class="code cpp" id="p911code30">
&nbsp; &nbsp; <span class="kw4">int</span> a<span class="br0">&#91;</span>5<span class="br0">&#93;</span> <span class="sy1">=</span> <span class="br0">&#123;</span> 10, 20, 30, 40, 50 <span class="br0">&#125;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw4">int</span> b<span class="br0">&#91;</span>8<span class="br0">&#93;</span> <span class="sy1">=</span> <span class="br0">&#123;</span> 5, 7, 9, 11, 13, 15, 17, 19 <span class="br0">&#125;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw4">int</span> c<span class="br0">&#91;</span>3<span class="br0">&#93;</span> <span class="sy1">=</span> <span class="br0">&#123;</span> 0, 1, 2 <span class="br0">&#125;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw4">int</span> d<span class="br0">&#91;</span>4<span class="br0">&#93;</span> <span class="sy1">=</span> <span class="br0">&#123;</span> 6, 12, 18, 24 <span class="br0">&#125;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span>std<span class="sy4">::</span><span class="me2">pair</span><span class="sy1">&lt;</span><span class="kw4">int</span><span class="sy2">*</span>, <span class="kw4">int</span><span class="sy2">*</span><span class="sy1">&gt;</span> <span class="sy1">&gt;</span> in<span class="br0">&#40;</span>4<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; in.<span class="me1">push_back</span><span class="br0">&#40;</span>std<span class="sy4">::</span><span class="me2">make_pair</span><span class="br0">&#40;</span>a, a <span class="sy2">+</span> 5<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; in.<span class="me1">push_back</span><span class="br0">&#40;</span>std<span class="sy4">::</span><span class="me2">make_pair</span><span class="br0">&#40;</span>b, b <span class="sy2">+</span> 8<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; in.<span class="me1">push_back</span><span class="br0">&#40;</span>std<span class="sy4">::</span><span class="me2">make_pair</span><span class="br0">&#40;</span>c, c <span class="sy2">+</span> 3<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; in.<span class="me1">push_back</span><span class="br0">&#40;</span>std<span class="sy4">::</span><span class="me2">make_pair</span><span class="br0">&#40;</span>d, d <span class="sy2">+</span> 4<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">vector</span><span class="sy1">&lt;</span><span class="kw4">int</span><span class="sy1">&gt;</span> out<span class="br0">&#40;</span>5<span class="sy2">+</span>8<span class="sy2">+</span>3<span class="sy2">+</span>4<span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; n_way_merge<span class="br0">&#40;</span>in, out.<span class="me1">begin</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span>std<span class="sy4">::</span><span class="kw4">size_t</span> i<span class="sy1">=</span><span class="nu0">0</span><span class="sy4">;</span> i<span class="sy1">&lt;</span>out.<span class="me1">size</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span> <span class="sy2">++</span>i<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy4">::</span><span class="kw3">cout</span> <span class="sy1">&lt;&lt;</span> out<span class="br0">&#91;</span>i<span class="br0">&#93;</span> <span class="sy1">&lt;&lt;</span> <span class="st0">&quot; &quot;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; std<span class="sy4">::</span><span class="kw3">cout</span> <span class="sy1">&lt;&lt;</span> std<span class="sy4">::</span><span class="me2">endl</span><span class="sy4">;</span>
        </div>
    </div>
</div>

<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/911-k-way-merge-sort-cpp/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/c-cpp/911-k-way-merge-sort-cpp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Bitonic Mergesort на C++</title>
		<link>http://blog.sjinks.pro/c-cpp/908-bitonic-mergesort-cpp/</link>
		<comments>http://blog.sjinks.pro/c-cpp/908-bitonic-mergesort-cpp/#comments</comments>
		<pubDate>Sat, 16 Apr 2011 11:16:53 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[алгоритмы]]></category>
		<category><![CDATA[сортировка]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=908</guid>
		<description><![CDATA[Битонная сортировка в стиле STL Битонная сортировка слиянием (bitonic mergesort) — параллельный алгоритм сортировки, рекурсивно разделяющий входную последовательность на сортированные последовательности меньшего размера. Алгоритм битонной сортировки может выполняться параллельно, потому что каждая операция разделения независима от всех других операций. #include &#60;iterator&#62; #include &#60;functional&#62; #include &#60;algorithm&#62; namespace { template&#60;typename Iterator, typename Comp&#62; void bitonic_merge(bool flag, Iterator [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/908-bitonic-mergesort-cpp/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Битонная <a href="http://blog.sjinks.pro/tag/sort/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  сортировка">сортировка</a> в стиле STL</em></h2>
<p>Битонная сортировка слиянием (<a href="http://en.wikipedia.org/wiki/Bitonic_sort">bitonic mergesort</a>) — параллельный алгоритм сортировки, рекурсивно разделяющий входную последовательность на сортированные последовательности меньшего размера.</p>
<p>Алгоритм битонной сортировки может выполняться параллельно, потому что каждая операция разделения независима от всех других операций.<br />
<span id="more-908"></span></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p90832">
        <div class="code cpp" id="p908code32">
<span class="co2">#include &lt;iterator&gt;</span><br />
<span class="co2">#include &lt;functional&gt;</span><br />
<span class="co2">#include &lt;algorithm&gt;</span><br />
<br />
<span class="kw2">namespace</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> Iterator, <span class="kw2">typename</span> Comp<span class="sy1">&gt;</span><br />
&nbsp; &nbsp; <span class="kw4">void</span> bitonic_merge<span class="br0">&#40;</span><span class="kw4">bool</span> flag, Iterator begin, Iterator end, Iterator middle, Comp c<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Iterator x<span class="br0">&#40;</span>begin<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Iterator y<span class="br0">&#40;</span>middle<span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span> <span class="sy4">;</span> y <span class="sy3">!</span><span class="sy1">=</span> end<span class="sy4">;</span> <span class="sy2">++</span>x, <span class="sy2">++</span>y<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>flag <span class="sy3">!</span><span class="sy1">=</span> c<span class="br0">&#40;</span><span class="sy2">*</span>x, <span class="sy2">*</span>y<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">swap</span><span class="br0">&#40;</span><span class="sy2">*</span>x, <span class="sy2">*</span>y<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; x <span class="sy1">=</span> begin<span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; y <span class="sy1">=</span> middle<span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">typename</span> std<span class="sy4">::</span><span class="me2">iterator_traits</span><span class="sy1">&lt;</span>Iterator<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">difference_type</span> halfsize <span class="sy1">=</span> std<span class="sy4">::</span><span class="me2">distance</span><span class="br0">&#40;</span>begin, middle<span class="br0">&#41;</span> <span class="sy1">&gt;&gt;</span> <span class="nu0">1</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>halfsize<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">advance</span><span class="br0">&#40;</span>x, halfsize<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">advance</span><span class="br0">&#40;</span>y, halfsize<span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bitonic_merge<span class="br0">&#40;</span>flag, begin, middle, x, c<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bitonic_merge<span class="br0">&#40;</span>flag, middle, end, y, c<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> Iterator, <span class="kw2">typename</span> Comp<span class="sy1">&gt;</span><br />
&nbsp; &nbsp; <span class="kw4">void</span> bitonic_sort_private<span class="br0">&#40;</span><span class="kw4">bool</span> flag, Iterator begin, Iterator end, Comp c<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">typename</span> std<span class="sy4">::</span><span class="me2">iterator_traits</span><span class="sy1">&lt;</span>Iterator<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">difference_type</span> size <span class="sy1">=</span> std<span class="sy4">::</span><span class="me2">distance</span><span class="br0">&#40;</span>begin, end<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>size <span class="sy1">&lt;=</span> 1<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; Iterator m<span class="br0">&#40;</span>begin<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">advance</span><span class="br0">&#40;</span>m, size <span class="sy1">&gt;&gt;</span> 1<span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; bitonic_sort_private<span class="br0">&#40;</span>flag, begin, m, c<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; bitonic_sort_private<span class="br0">&#40;</span><span class="sy3">!</span>flag, m, end, c<span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; bitonic_merge<span class="br0">&#40;</span>flag, begin, end, m, c<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
<span class="br0">&#125;</span><span class="sy4">;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> Iterator<span class="sy1">&gt;</span><br />
<span class="kw4">void</span> bitonic_sort<span class="br0">&#40;</span>Iterator begin, Iterator end<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">typedef</span> <span class="kw2">typename</span> std<span class="sy4">::</span><span class="me2">iterator_traits</span><span class="sy1">&lt;</span>Iterator<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">value_type</span> value_t<span class="sy4">;</span><br />
&nbsp; &nbsp; bitonic_sort_private<span class="br0">&#40;</span><span class="kw2">true</span>, begin, end, std<span class="sy4">::</span><span class="me2">less</span><span class="sy1">&lt;</span>value_t<span class="sy1">&gt;</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> Iterator, <span class="kw2">typename</span> Comp<span class="sy1">&gt;</span><br />
<span class="kw4">void</span> bitonic_sort<span class="br0">&#40;</span>Iterator begin, Iterator end, Comp c<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; bitonic_sort_private<span class="br0">&#40;</span><span class="kw2">true</span>, begin, end, c<span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/908-bitonic-mergesort-cpp/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/c-cpp/908-bitonic-mergesort-cpp/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Пример рабочей реализации QDomDocument::elementById</title>
		<link>http://blog.sjinks.pro/c-cpp/qt/906-working-qdomdocument-elementbyid/</link>
		<comments>http://blog.sjinks.pro/c-cpp/qt/906-working-qdomdocument-elementbyid/#comments</comments>
		<pubDate>Sat, 19 Mar 2011 13:52:03 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[Qt]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[XML]]></category>
		<category><![CDATA[XPath]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=906</guid>
		<description><![CDATA[Избавляемся от ошибки «elementById() is not implemented and will always return a null node» Так сложилось, что в Qt реализация метода QDomDocument::elementById() нерабочая: при попытке использования данного метода выдаётся предупреждение elementById() is not implemented and will always return a null node и возвращается пустой элемент DOM. Временами это очень неудобно: например, вместо использования XPath из [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/qt/906-working-qdomdocument-elementbyid/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Избавляемся от ошибки «elementById() is not implemented and will always return a null node»</em></h2>
<p>Так <a href="http://doc.trolltech.com/4.7/qdomdocument.html#elementById">сложилось</a>, что в <a href="http://blog.sjinks.pro/tag/qt/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Qt">Qt</a> реализация метода <code>QDomDocument::elementById()</code> нерабочая: при попытке использования данного метода выдаётся предупреждение <strong>elementById() is not implemented and will always return a null node</strong> и возвращается пустой элемент <a href="http://blog.sjinks.pro/tag/dom/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  DOM">DOM</a>.</p>
<p>Временами это очень неудобно: например, вместо использования <a href="http://blog.sjinks.pro/tag/xpath/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  XPath">XPath</a> из QXmlPatterns может быть проще получить элемент DOM по его <code>id</code> и пройтись по его потомкам. А при использовании XPath функцию <code>id()</code> использовать не получится в силу тех же причин.<span id="more-906"></span></p>
<p>Ниже приведён пример рабочей реализации <code>QDomDocument::elementById()</code>.</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p90634">
        <div class="code cpp-qt" id="p906code34">
<span class="co2">#include &lt;QtXml/QDomDocument&gt;</span><br />
<span class="co2">#include &lt;QMap&gt;</span><br />
<br />
<span class="kw2">class</span> QMyDomDocument <span class="sy0">:</span> <span class="kw2">public</span> <span class="kw5">QDomDocument</span> <span class="br0">&#123;</span><br />
<span class="kw2">public</span><span class="sy0">:</span><br />
&nbsp; &nbsp; QMyDomDocument<span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw5">QDomDocument</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; QMyDomDocument<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> name<span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw5">QDomDocument</span><span class="br0">&#40;</span>name<span class="br0">&#41;</span> <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
&nbsp; &nbsp; QMyDomDocument<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QDomDocumentType</span><span class="sy0">&amp;</span> doctype<span class="br0">&#41;</span> <span class="sy0">:</span> <span class="kw5">QDomDocument</span><span class="br0">&#40;</span>doctype<span class="br0">&#41;</span> <span class="br0">&#123;</span><span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw5">QDomElement</span> elementById<span class="br0">&#40;</span><span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> id<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>map.<span class="me1">contains</span><span class="br0">&#40;</span>id<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QDomElement</span> e <span class="sy0">=</span> map<span class="br0">&#91;</span>id<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>e.<span class="me1">parentNode</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">nodeType</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy0">!=</span> <span class="kw5">QDomNode</span><span class="sy0">::</span><span class="me2">BaseNode</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> e<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; map.<span class="kw3">remove</span><span class="br0">&#40;</span>id<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">bool</span> res <span class="sy0">=</span> this<span class="sy0">-&gt;</span><span class="me3">find</span><span class="br0">&#40;</span>this<span class="sy0">-&gt;</span><span class="me3">documentElement</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> id<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>res<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> map<span class="br0">&#91;</span>id<span class="br0">&#93;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw5">QDomElement</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
<span class="kw2">private</span><span class="sy0">:</span><br />
&nbsp; &nbsp; <span class="kw5">QMap</span><span class="sy0">&lt;</span><span class="kw5">QString</span><span class="sy0">,</span> <span class="kw5">QDomElement</span><span class="sy0">&gt;</span> map<span class="sy0">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw4">bool</span> find<span class="br0">&#40;</span><span class="kw5">QDomElement</span> node<span class="sy0">,</span> <span class="kw4">const</span> <span class="kw5">QString</span><span class="sy0">&amp;</span> id<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>node.<span class="me1">hasAttribute</span><span class="br0">&#40;</span><span class="st0">&quot;id&quot;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QString</span> value <span class="sy0">=</span> node.<span class="me1">attribute</span><span class="br0">&#40;</span><span class="st0">&quot;id&quot;</span><span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this<span class="sy0">-&gt;</span><span class="me3">map</span><span class="br0">&#91;</span>value<span class="br0">&#93;</span> <span class="sy0">=</span> node<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>value <span class="sy0">==</span> id<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> true<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">int</span> i<span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> i<span class="sy0">&lt;</span>node.<span class="me1">childNodes</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">length</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="sy0">++</span>i<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw5">QDomNode</span> n <span class="sy0">=</span> node.<span class="me1">childNodes</span><span class="br0">&#40;</span><span class="br0">&#41;</span>.<span class="me1">at</span><span class="br0">&#40;</span>i<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>n.<span class="me1">isElement</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">bool</span> res <span class="sy0">=</span> this<span class="sy0">-&gt;</span><span class="me3">find</span><span class="br0">&#40;</span>n.<span class="me1">toElement</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">,</span> id<span class="br0">&#41;</span><span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>res<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> true<span class="sy0">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> false<span class="sy0">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><span class="sy0">;</span>
        </div>
    </div>
</div>

<p>Один из тонких моментов реализации — определение принадлежности вершины документу. Так как для ускорения поиска используется отображение <span class="codebox"><code class="text">&lt;id, элемент&gt;</code></span>, может возникнуть ситуация, когда запрашиваемый элемент уже удалён из документа (в результате в отображении остаются неактуальные данные).</p>
<p>Если удалить элемент из документа, он «лишается» родителя — в результате его родительская (псевдо)вершина имеет тип <code>QDomNode::BaseNode</code>. Это свойство и используется для определения актуальности элемента.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/qt/906-working-qdomdocument-elementbyid/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/c-cpp/qt/906-working-qdomdocument-elementbyid/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Scope Guard средствами C++0x: часть 2</title>
		<link>http://blog.sjinks.pro/c-cpp/800-scope-guard-cpp-0x-part-2/</link>
		<comments>http://blog.sjinks.pro/c-cpp/800-scope-guard-cpp-0x-part-2/#comments</comments>
		<pubDate>Mon, 26 Apr 2010 18:40:44 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[C++0x]]></category>
		<category><![CDATA[Scope Guard]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=800</guid>
		<description><![CDATA[Более глубокое погружение в C++0x В прошлой части была рассмотрена реализация Scope Guard средствами C++0x. Благодаря шаблонам с переменным количеством параметров (variadic templates), реализация на C++0x получилась несколько проще, чем в оригинале, так как один и тот же шаблонный класс может использоваться для создания Scope Guard с различным количеством параметров. Но, как было отмечено, предыдущая [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/800-scope-guard-cpp-0x-part-2/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Более глубокое погружение в <a href="http://blog.sjinks.pro/tag/c0x/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  C++0x">C++0x</a></em></h2>
<p><a href="http://blog.sjinks.pro/c-cpp/796-scope-guard-by-means-of-cpp-0x-part-1/">В прошлой части</a> была рассмотрена реализация <a href="http://www.drdobbs.com/cpp/184403758">Scope Guard</a> средствами C++0x. Благодаря шаблонам с переменным количеством параметров (variadic templates), реализация на C++0x получилась несколько проще, чем в оригинале, так как один и тот же шаблонный класс может использоваться для создания <a href="http://blog.sjinks.pro/tag/scope-guard/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Scope Guard">Scope Guard</a> с различным количеством параметров.</p>
<p>Но, как было отмечено, предыдущая реализация не являлась оптимальной в плане количества строк. Можно сделать проще и короче.<span id="more-800"></span></p>
<p>Одна из проблем <a href="http://blog.sjinks.pro/c-cpp/796-scope-guard-by-means-of-cpp-0x-part-1/">предыдущей реализации</a> — необходимость наличия отдельного класса, если Scope Guard потребуется вызывать метод какого-либо класса.</p>
<p>Иными словами, требуется две реализации: одна для Scope Guard, работающего с обычными функциями, другая — для Scope Guard, работающего с методами. Как оказалось, данная проблема решаема.</p>
<p>Основную (на мой взгляд) неочевидность в коде создавал метод развёртки <span class="codebox"><code class="cpp">std<span class="sy4">::</span><span class="me2">tuple</span></code></span> (<code>Apply_Helper</code>). </p>
<p>Хорошая новость заключается в том, что благодаря <span class="codebox"><code class="cpp">std<span class="sy4">::</span><span class="me2">bind</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> обе проблемы очень легко решаются.</p>
<p>Реализация <code>XGuard</code> теперь сильно упрощается:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80038">
        <div class="code cpp" id="p800code38">
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> Function<span class="sy1">&gt;</span><br />
<span class="kw2">class</span> XGuard <span class="br0">&#123;</span><br />
<span class="kw2">public</span><span class="sy4">:</span><br />
&nbsp; &nbsp; XGuard<span class="br0">&#40;</span>Function aFunction<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy4">:</span> m_fSucceeded<span class="br0">&#123;</span><span class="kw2">false</span><span class="br0">&#125;</span>, m_Function<span class="br0">&#40;</span>aFunction<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; ~XGuard<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy3">!</span>this<span class="sy2">-</span><span class="sy1">&gt;</span>m_fSucceeded<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">try</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this<span class="sy2">-</span><span class="sy1">&gt;</span>m_Function<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">catch</span><span class="br0">&#40;</span>...<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; XGuard<span class="br0">&#40;</span><span class="kw4">const</span> XGuard<span class="sy3">&amp;&amp;</span> other<span class="br0">&#41;</span> <span class="sy4">:</span> m_fSucceeded<span class="br0">&#40;</span>other.<span class="me1">m_fSucceeded</span><span class="br0">&#41;</span>, m_Function<span class="br0">&#40;</span>other.<span class="me1">m_Function</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; other.<span class="me1">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw4">void</span> commit<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span> <span class="kw1">throw</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; this<span class="sy2">-</span><span class="sy1">&gt;</span>m_fSucceeded <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
<span class="kw2">private</span><span class="sy4">:</span><br />
&nbsp; &nbsp; mutable <span class="kw4">bool</span> m_fSucceeded<span class="sy4">;</span><br />
&nbsp; &nbsp; Function m_Function<span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; XGuard<span class="br0">&#40;</span><span class="kw4">const</span> XGuard<span class="sy3">&amp;</span><span class="br0">&#41;</span> <span class="sy1">=</span> <span class="kw3">delete</span><span class="sy4">;</span><br />
&nbsp; &nbsp; XGuard<span class="sy3">&amp;</span> operator<span class="sy1">=</span><span class="br0">&#40;</span><span class="kw4">const</span> XGuard<span class="sy3">&amp;</span><span class="br0">&#41;</span> <span class="sy1">=</span> <span class="kw3">delete</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span><span class="sy4">;</span>
        </div>
    </div>
</div>

<p>А благодаря <a href="http://en.wikipedia.org/wiki/C%2B%2B0x#Type_inference">type inference</a> очень легко написать функцию, которая будет создавать <code>XGuard</code>:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80039">
        <div class="code cpp" id="p800code39">
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> F, <span class="kw2">typename</span>... <span class="me1">A</span><span class="sy1">&gt;</span><br />
<span class="kw4">auto</span> makeXGuard<span class="br0">&#40;</span>F f, <span class="kw4">const</span> A<span class="sy3">&amp;&amp;</span>... <span class="me1">p</span><span class="br0">&#41;</span> <span class="sy2">-</span><span class="sy1">&gt;</span> XGuard<span class="sy1">&lt;</span>decltype<span class="br0">&#40;</span>std<span class="sy4">::</span><span class="me2">bind</span><span class="br0">&#40;</span>f, p...<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">&gt;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">auto</span> func <span class="sy1">=</span> std<span class="sy4">::</span><span class="me2">bind</span><span class="br0">&#40;</span>f, p...<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> XGuard<span class="sy1">&lt;</span>decltype<span class="br0">&#40;</span>func<span class="br0">&#41;</span><span class="sy1">&gt;</span><span class="br0">&#40;</span>func<span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Фокус в том, что передавая в функцию parameter pack (который <span class="codebox"><code class="cpp"><span class="kw4">const</span> A<span class="sy3">&amp;&amp;</span>... <span class="me1">p</span></code></span>), мы не можем априорно знать, какой результат вернёт <code>std::bind()</code> для таких аргументов. Использование вывода типов и <a href="http://en.wikipedia.org/wiki/C%2B%2B0x#Alternative_function_syntax">альтернативный синтаксис функций</a> приходят на помощь, в результате чего мы получили такой код.</p>
<p>Для возможности работы с методами классов нужно создать еще один вариант <code>makeXGuard()</code>:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p80040">
        <div class="code cpp" id="p800code40">
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> C, <span class="kw2">typename</span> M, <span class="kw2">typename</span>... <span class="me1">A</span><span class="sy1">&gt;</span><br />
<span class="kw4">auto</span> makeXGuard<span class="br0">&#40;</span>C<span class="sy3">&amp;</span> c, M m, <span class="kw4">const</span> A<span class="sy3">&amp;&amp;</span>... <span class="me1">p</span><span class="br0">&#41;</span> <span class="sy2">-</span><span class="sy1">&gt;</span> XGuard<span class="sy1">&lt;</span>decltype<span class="br0">&#40;</span>std<span class="sy4">::</span><span class="me2">bind</span><span class="br0">&#40;</span>m, c, p...<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">&gt;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw4">auto</span> func <span class="sy1">=</span> std<span class="sy4">::</span><span class="me2">bind</span><span class="br0">&#40;</span>m, c, p...<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> XGuard<span class="sy1">&lt;</span>decltype<span class="br0">&#40;</span>func<span class="br0">&#41;</span><span class="sy1">&gt;</span><span class="br0">&#40;</span>func<span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>Вот так всё просто.</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/800-scope-guard-cpp-0x-part-2/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/c-cpp/800-scope-guard-cpp-0x-part-2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Scope Guard средствами C++0x: часть 1</title>
		<link>http://blog.sjinks.pro/c-cpp/796-scope-guard-by-means-of-cpp-0x-part-1/</link>
		<comments>http://blog.sjinks.pro/c-cpp/796-scope-guard-by-means-of-cpp-0x-part-1/#comments</comments>
		<pubDate>Fri, 23 Apr 2010 08:26:51 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[C++0x]]></category>
		<category><![CDATA[Scope Guard]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=796</guid>
		<description><![CDATA[Часть первая: решение в лоб Scope Guard — одно из средств автоматического освобождения ресурсов при выходе за пределы видимости переменной, с ними связанной. Scope Guard предоставляет базовую гарантию безопасности исключений. Авторами этой идеи (по-видимому) являются Andrei Alexandrescu и Petru Marginean. Если вы с этой статьёй еще не знакомы, то очень рекомендую к прочтению. Реализация Scope Guard довольно [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/796-scope-guard-by-means-of-cpp-0x-part-1/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Часть первая: решение в лоб</em></h2>
<p><a href="http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Scope_Guard">Scope Guard</a> — одно из средств автоматического освобождения ресурсов при выходе за пределы видимости переменной, с ними связанной. <a href="http://blog.sjinks.pro/tag/scope-guard/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Scope Guard">Scope Guard</a> предоставляет базовую гарантию безопасности исключений. Авторами этой идеи (по-видимому) являются <a href="http://www.drdobbs.com/cpp/184403758">Andrei Alexandrescu и Petru Marginean</a>. Если вы с этой статьёй еще не знакомы, то очень рекомендую к <a href="http://www.drdobbs.com/cpp/184403758">прочтению</a>.</p>
<p>Реализация Scope Guard довольно простая, но из-за того, что C++ не поддерживал шаблоны с переменным количеством параметров, приходилось создавать несколько шаблонов — в зависимости от того, сколько аргументов принимает функция, выполняющая освобождение ресурсов.<span id="more-796"></span></p>
<p>В <a href="http://blog.sjinks.pro/tag/c0x/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  C++0x">C++0x</a> добавлена поддержка шаблонов с переменным количеством параметров (<dfn>variadic templates</dfn>). Мне было интересно переписать Scope Guard одним шаблоном — хорошее погружение в новые возможности языка.</p>
<p>Получился примерно такой код (он не самый оптимальный в плане количества строк, можно написать проще, но об этом в следующей части):</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p79646">
        <div class="code cpp" id="p796code46">
<span class="co2">#ifndef SJ_XGUARD_H_</span><br />
<span class="co2">#define SJ_XGUARD_H_</span><br />
<br />
<span class="co2">#include &lt;tuple&gt;</span><br />
<br />
<span class="kw2">namespace</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw4">unsigned</span> <span class="kw4">int</span> N<span class="sy1">&gt;</span><br />
&nbsp; &nbsp; <span class="kw4">struct</span> Apply_Helper <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> F, <span class="kw2">typename</span>... <span class="me1">ArgsT</span>, <span class="kw2">typename</span>... <span class="me1">Args</span><span class="sy1">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> apply<span class="br0">&#40;</span><span class="kw4">const</span> F<span class="sy3">&amp;</span> f, <span class="kw4">const</span> std<span class="sy4">::</span><span class="me2">tuple</span><span class="sy1">&lt;</span>ArgsT...<span class="sy1">&gt;</span><span class="sy3">&amp;&amp;</span> t, Args<span class="sy3">&amp;&amp;</span>... <span class="me1">args</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Apply_Helper<span class="sy1">&lt;</span>N<span class="sy2">-</span>1<span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">apply</span><span class="br0">&#40;</span>f, t, std<span class="sy4">::</span><span class="me2">get</span><span class="sy1">&lt;</span>N<span class="sy2">-</span>1<span class="sy1">&gt;</span><span class="br0">&#40;</span>t<span class="br0">&#41;</span>, args...<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">template</span><span class="sy1">&lt;&gt;</span><br />
&nbsp; &nbsp; <span class="kw4">struct</span> Apply_Helper<span class="sy1">&lt;</span>0<span class="sy1">&gt;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> F, <span class="kw2">typename</span>... <span class="me1">ArgsT</span>, <span class="kw2">typename</span>... <span class="me1">Args</span><span class="sy1">&gt;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">static</span> <span class="kw4">void</span> apply<span class="br0">&#40;</span><span class="kw4">const</span> F<span class="sy3">&amp;</span> f, <span class="kw4">const</span> std<span class="sy4">::</span><span class="me2">tuple</span><span class="sy1">&lt;</span>ArgsT...<span class="sy1">&gt;</span><span class="sy3">&amp;&amp;</span>, Args<span class="sy3">&amp;&amp;</span>... <span class="me1">args</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; f<span class="br0">&#40;</span>args...<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; <span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> F, <span class="kw2">typename</span>... <span class="me1">ArgsT</span><span class="sy1">&gt;</span><br />
&nbsp; &nbsp; <span class="kw4">void</span> apply<span class="br0">&#40;</span><span class="kw4">const</span> F<span class="sy3">&amp;</span> f, std<span class="sy4">::</span><span class="me2">tuple</span><span class="sy1">&lt;</span>ArgsT...<span class="sy1">&gt;</span> <span class="kw4">const</span><span class="sy3">&amp;&amp;</span> t<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; Apply_Helper<span class="sy1">&lt;</span><span class="kw3">sizeof</span>...<span class="br0">&#40;</span>ArgsT<span class="br0">&#41;</span><span class="sy1">&gt;</span><span class="sy4">::</span><span class="me2">apply</span><span class="br0">&#40;</span>f, t<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><span class="sy4">;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> Function, <span class="kw2">typename</span>... <span class="me1">Args</span><span class="sy1">&gt;</span><br />
<span class="kw2">class</span> XGuard <span class="br0">&#123;</span><br />
<span class="kw2">public</span><span class="sy4">:</span><br />
&nbsp; &nbsp; XGuard<span class="br0">&#40;</span><span class="kw4">const</span> Function<span class="sy3">&amp;</span> aFunction, std<span class="sy4">::</span><span class="me2">tuple</span><span class="sy1">&lt;</span>Args...<span class="sy1">&gt;</span><span class="sy3">&amp;&amp;</span> aParams<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy4">:</span> m_fSucceeded<span class="br0">&#40;</span><span class="kw2">false</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m_Function<span class="br0">&#40;</span>aFunction<span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m_Params<span class="br0">&#40;</span>aParams<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; ~XGuard<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw1">throw</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy3">!</span>this<span class="sy2">-</span><span class="sy1">&gt;</span>m_fSucceeded<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">try</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; apply<span class="br0">&#40;</span>this<span class="sy2">-</span><span class="sy1">&gt;</span>m_Function, this<span class="sy2">-</span><span class="sy1">&gt;</span>m_Params<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">catch</span><span class="br0">&#40;</span>...<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; XGuard<span class="br0">&#40;</span>XGuard<span class="sy3">&amp;&amp;</span> other<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy4">:</span> m_fSucceeded<span class="br0">&#40;</span>other.<span class="me1">m_fSucceeded</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m_Function<span class="br0">&#40;</span>other.<span class="me1">m_Function</span><span class="br0">&#41;</span>,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m_Params<span class="br0">&#40;</span>other.<span class="me1">m_Params</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; other.<span class="me1">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw4">void</span> commit<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="kw4">const</span> <span class="kw1">throw</span><span class="br0">&#40;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; this<span class="sy2">-</span><span class="sy1">&gt;</span>m_fSucceeded <span class="sy1">=</span> <span class="kw2">true</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; XGuard<span class="sy3">&amp;</span> operator<span class="sy1">=</span><span class="br0">&#40;</span>XGuard<span class="sy3">&amp;&amp;</span> other<span class="br0">&#41;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="sy3">&amp;</span>other <span class="sy3">!</span><span class="sy1">=</span> <span class="kw3">this</span><span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this<span class="sy2">-</span><span class="sy1">&gt;</span>m_fSucceeded <span class="sy1">=</span> other.<span class="me1">m_fSucceeded</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this<span class="sy2">-</span><span class="sy1">&gt;</span>m_Function &nbsp; <span class="sy1">=</span> other.<span class="me1">m_Function</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this<span class="sy2">-</span><span class="sy1">&gt;</span>m_Params &nbsp; &nbsp; <span class="sy1">=</span> std<span class="sy4">::</span><span class="me2">move</span><span class="br0">&#40;</span>other.<span class="me1">m_Params</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; other.<span class="me1">m_Function</span> <span class="sy1">=</span> <span class="kw2">NULL</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; other.<span class="me1">commit</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="sy2">*</span><span class="kw3">this</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; XGuard<span class="br0">&#40;</span><span class="kw4">const</span> XGuard<span class="sy3">&amp;</span> other<span class="br0">&#41;</span> <span class="sy1">=</span> <span class="kw3">delete</span><span class="sy4">;</span><br />
&nbsp; &nbsp; XGuard<span class="sy3">&amp;</span> operator<span class="sy1">=</span><span class="br0">&#40;</span><span class="kw4">const</span> XGuard<span class="sy3">&amp;</span> other<span class="br0">&#41;</span> <span class="sy1">=</span> <span class="kw3">delete</span><span class="sy4">;</span><br />
<span class="kw2">private</span><span class="sy4">:</span><br />
&nbsp; &nbsp; mutable <span class="kw4">bool</span> m_fSucceeded<span class="sy4">;</span><br />
&nbsp; &nbsp; Function m_Function<span class="sy4">;</span><br />
&nbsp; &nbsp; std<span class="sy4">::</span><span class="me2">tuple</span><span class="sy1">&lt;</span>Args...<span class="sy1">&gt;</span><span class="sy3">&amp;&amp;</span> m_Params<span class="sy4">;</span><br />
<span class="br0">&#125;</span><span class="sy4">;</span><br />
<br />
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> F, <span class="kw2">typename</span>... <span class="me1">A</span><span class="sy1">&gt;</span><br />
<span class="kw2">inline</span> XGuard<span class="sy1">&lt;</span>F, A...<span class="sy1">&gt;</span> makeXGuard<span class="br0">&#40;</span><span class="kw4">const</span> F<span class="sy3">&amp;</span> f, std<span class="sy4">::</span><span class="me2">tuple</span><span class="sy1">&lt;</span>A...<span class="sy1">&gt;</span><span class="sy3">&amp;&amp;</span> p<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> XGuard<span class="sy1">&lt;</span>F, A...<span class="sy1">&gt;</span><span class="br0">&#40;</span>f, p<span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="co2">#define MAKEXGUARD(f, ...) \<br />
&nbsp; &nbsp; makeXGuard((f), std::make_tuple(__VA_ARGS__))</span><br />
<br />
<span class="co2">#endif /* SJ_XGUARD_H_ */</span>
        </div>
    </div>
</div>

<p>Название <code>XGuard</code> чисто историческое, я в университете занимался «велосипедостроительством», поэтому просто переделал код многолетней давности.</p>
<p>Основной трудностью для меня было развернуть <code>std::tuple</code> в список параметров. Отвык программировать на <code>Prolog</code> <img src='http://static.sjinks.info/wp-includes/images/smilies/icon_sad.gif' alt=':-(' class='wp-smiley' />  Развёрткой <code>std::tuple</code> в список параметров занимается <code>Apply_Helper</code> в анонимном пространстве имён. Для программировавших на чем-нибудь типа Prolog принцип очень простой:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p79647">
        <div class="code text" id="p796code47">
Apply_Helper&lt;N&gt;::apply([tuple_head|tuple_tail], [params]) =&gt; Apply_Helper&lt;N-1&gt;::apply([tuple_tail], [tuple_head|params])
        </div>
    </div>
</div>

<p>Другой подводный камень — макро <code>MAKEXGUARD</code>. Дело в том, что такая реализация:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p79648">
        <div class="code cpp" id="p796code48">
<span class="kw2">template</span><span class="sy1">&lt;</span><span class="kw2">typename</span> F, <span class="kw2">typename</span>... <span class="me1">A</span><span class="sy1">&gt;</span><br />
<span class="kw2">inline</span> XGuard<span class="sy1">&lt;</span>F, A...<span class="sy1">&gt;</span> makeXGuard<span class="br0">&#40;</span><span class="kw4">const</span> F<span class="sy3">&amp;</span> f, <span class="kw4">const</span> A<span class="sy3">&amp;&amp;</span> p<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">return</span> XGuard<span class="sy1">&lt;</span>F, A...<span class="sy1">&gt;</span><span class="br0">&#40;</span>f, std<span class="sy4">::</span><span class="me2">make_tuple</span><span class="br0">&#40;</span>p<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>не воспринимает <span class="codebox"><code class="cpp">std<span class="sy4">::</span><span class="me2">ref</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>/<span class="codebox"><code class="cpp">std<span class="sy4">::</span><span class="me2">cref</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> в списках параметров — у компилятора не получается определить корректный шаблон на основе вывода параметров.</p>
<p>Как я уже говорил, приведённый код не является оптимальным в плане количества строк (можно сделать проще), но не генерирует лишнего машинного кода. Вся свёртка/развёртка параметров производится на этапе компиляции.</p>
<p>Например, такой код:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p79649">
        <div class="code cpp" id="p796code49">
<span class="co2">#include &lt;cstdlib&gt;</span><br />
<span class="co1">//#include &lt;iostream&gt;</span><br />
<span class="co2">#include &lt;functional&gt;</span><br />
<span class="co2">#include &quot;xguard.h&quot;</span><br />
<br />
<span class="kw4">static</span> <span class="kw4">void</span> my_free<span class="br0">&#40;</span><span class="kw4">void</span><span class="sy2">*</span><span class="sy3">&amp;</span> p<span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>p<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">free</span><span class="br0">&#40;</span>p<span class="br0">&#41;</span><span class="sy4">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; p <span class="sy1">=</span> <span class="kw2">NULL</span><span class="sy4">;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="kw4">int</span> main<span class="br0">&#40;</span><span class="kw4">int</span>, <span class="kw4">char</span><span class="sy2">**</span><span class="br0">&#41;</span><br />
<span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">void</span><span class="sy2">*</span> p <span class="sy1">=</span> <span class="br0">&#40;</span><span class="kw4">void</span><span class="sy2">*</span><span class="br0">&#41;</span>std<span class="sy4">::</span><span class="kw3">malloc</span><span class="br0">&#40;</span>100<span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="co1">//&nbsp; &nbsp; &nbsp; std::cout &lt;&lt; p &lt;&lt; std::endl;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">auto</span> guard <span class="sy1">=</span> MAKEXGUARD<span class="br0">&#40;</span><span class="sy3">&amp;</span>my_free, std<span class="sy4">::</span><span class="me2">ref</span><span class="br0">&#40;</span>p<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy4">;</span><br />
<span class="co1">//&nbsp; &nbsp; &nbsp; std::cout &lt;&lt; p &lt;&lt; std::endl;</span><br />
&nbsp; &nbsp; <span class="br0">&#125;</span><br />
<br />
&nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span><span class="sy4">;</span><br />
<span class="br0">&#125;</span>
        </div>
    </div>
</div>

<p>транслируется в такой:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p79650">
        <div class="code asm" id="p796code50">
main<span class="sy0">:</span><br />
&nbsp; &nbsp; <span class="kw1">sub</span> rsp<span class="sy0">,</span> 24<br />
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw3">edi</span><span class="sy0">,</span> 100<br />
&nbsp; &nbsp; <span class="kw1">call</span>&nbsp; &nbsp; malloc&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">; void* p = (void*)std::malloc(100);</span><br />
&nbsp; &nbsp; <span class="kw1">lea</span> rdi<span class="sy0">,</span> <span class="br0">&#91;</span>rsp<span class="sy0">+</span>8<span class="br0">&#93;</span><br />
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw5">QWORD</span> <span class="kw4">PTR</span> <span class="br0">&#91;</span>rsp<span class="sy0">+</span>8<span class="br0">&#93;</span><span class="sy0">,</span> rax<br />
&nbsp; &nbsp; <span class="kw1">call</span>&nbsp; &nbsp; _ZL7my_freeRPv&nbsp; &nbsp; &nbsp; <span class="co1">; my_free(p);</span><br />
&nbsp; &nbsp; <span class="kw1">xor</span> <span class="kw3">eax</span><span class="sy0">,</span> <span class="kw3">eax</span><br />
&nbsp; &nbsp; <span class="kw1">add</span> rsp<span class="sy0">,</span> 24<br />
&nbsp; &nbsp; <span class="kw1">ret</span><br />
<br />
_ZL7my_freeRPv<span class="sy0">:</span><br />
&nbsp; &nbsp; <span class="kw1">push</span>&nbsp; &nbsp; rbx<br />
&nbsp; &nbsp; <span class="kw1">mov</span> rbx<span class="sy0">,</span> rdi<br />
&nbsp; &nbsp; <span class="kw1">mov</span> rdi<span class="sy0">,</span> <span class="kw5">QWORD</span> <span class="kw4">PTR</span> <span class="br0">&#91;</span>rdi<span class="br0">&#93;</span><br />
&nbsp; &nbsp; <span class="kw1">test</span>&nbsp; &nbsp; rdi<span class="sy0">,</span> rdi<br />
&nbsp; &nbsp; <span class="kw1">je</span>&nbsp; <span class="sy0">.</span>L5<br />
&nbsp; &nbsp; <span class="kw1">call</span>&nbsp; &nbsp; free<br />
&nbsp; &nbsp; <span class="kw1">mov</span> <span class="kw5">QWORD</span> <span class="kw4">PTR</span> <span class="br0">&#91;</span>rbx<span class="br0">&#93;</span><span class="sy0">,</span> 0<br />
<span class="sy0">.</span>L5<span class="sy0">:</span><br />
&nbsp; &nbsp; <span class="kw1">pop</span> rbx<br />
&nbsp; &nbsp; <span class="kw1">ret</span>
        </div>
    </div>
</div>

<p>Таким образом, вызов функции <span class="codebox"><code class="cpp">makeXGuard<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> и конструктора <span class="codebox"><code class="cpp">XGuard<span class="sy4">::</span><span class="me2">XGuard</span><span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> <strong>вообще не генерирует никакого кода</strong>! А при выходе <code>p</code> из области видимости автоматически генерируется вызов <code>my_free(p)</code> (то есть то же самое, что и написал бы программист, не использующий Scope Guard). Иными словами, <strong>накладных расходов нет</strong>. Красота!</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/c-cpp/796-scope-guard-by-means-of-cpp-0x-part-1/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/c-cpp/796-scope-guard-by-means-of-cpp-0x-part-1/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Патч для php-cgi, позволяющий конфигурировать значение listen backlog</title>
		<link>http://blog.sjinks.pro/php/747-php-cgi-patch-to-configure-listen-backlog/</link>
		<comments>http://blog.sjinks.pro/php/747-php-cgi-patch-to-configure-listen-backlog/#comments</comments>
		<pubDate>Sun, 03 Jan 2010 06:19:20 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[FastCGI]]></category>
		<category><![CDATA[патч]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=747</guid>
		<description><![CDATA[Задание listen backlog через переменную окружения PHP_FCGI_LISTEN_BACKLOG По умолчанию значение listen backlog в php-cgi жёстко задано — 128. Это означает, что PHP не сможет обработать более 128 запросов одновременно (точнее, что очередь запросов не сможет превысить данного значения). Я столкнулся с тем, что 128 — это мало. Для тех, кто столкнулся с данной проблемой, но по каким-либо причинам не хочет/не может [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/747-php-cgi-patch-to-configure-listen-backlog/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Задание listen backlog через переменную окружения <a href="http://blog.sjinks.pro/tag/php/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  PHP">PHP</a>_FCGI_LISTEN_BACKLOG</em></h2>
<p>По умолчанию значение listen backlog в php-cgi жёстко задано — 128. Это означает, что PHP не сможет обработать более 128 запросов одновременно (точнее, что очередь запросов не сможет превысить данного значения). Я столкнулся с тем, что 128 — это мало.</p>
<p>Для тех, кто столкнулся с данной проблемой, но по каким-либо причинам не хочет/не может поставить php-fpm (в котором backlog конфигурируется), я сделал небольшой <a href="http://blog.sjinks.pro/tag/patch/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  патч">патч</a>.<span id="more-747"></span></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p74755">
        <div class="code diff" id="p747code55">
--- php.orig/sapi/cgi/cgi_main.c.orig &nbsp; 2009-06-09 13:29:39.000000000 +0000<br />
<span class="re4">+++ php/sapi/cgi/cgi_main.c <span class="nu0">2010</span>-01-03 04:<span class="nu0">44</span>:<span class="nu0">34.000000000</span> +0000</span><br />
<span class="re6">@@ -<span class="nu0">1523</span>,<span class="nu0">7</span> +<span class="nu0">1523</span>,<span class="nu0">15</span> @@</span><br />
&nbsp;<br />
&nbsp;#if PHP_FASTCGI<br />
&nbsp; &nbsp; if <span class="br0">&#40;</span>bindpath<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
<span class="re7">- &nbsp; &nbsp; &nbsp; fcgi_fd = fcgi_listen<span class="br0">&#40;</span>bindpath, 128<span class="br0">&#41;</span>;</span><br />
<span class="re8">+ &nbsp; &nbsp; &nbsp; int backlog = <span class="nu0">128</span>;</span><br />
<span class="re8">+ &nbsp; &nbsp; &nbsp; if <span class="br0">&#40;</span>getenv<span class="br0">&#40;</span>&quot;PHP_FCGI_LISTEN_BACKLOG&quot;<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></span><br />
<span class="re8">+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; backlog = atoi<span class="br0">&#40;</span>getenv<span class="br0">&#40;</span>&quot;PHP_FCGI_LISTEN_BACKLOG&quot;<span class="br0">&#41;</span><span class="br0">&#41;</span>;</span><br />
<span class="re8">+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if <span class="br0">&#40;</span>backlog &lt; <span class="nu0">0</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></span><br />
<span class="re8">+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; backlog = <span class="nu0">128</span>;</span><br />
<span class="re8">+ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></span><br />
<span class="re8">+ &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span></span><br />
<span class="re8">+</span><br />
<span class="re8">+ &nbsp; &nbsp; &nbsp; fcgi_fd = fcgi_listen<span class="br0">&#40;</span>bindpath, backlog<span class="br0">&#41;</span>;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; if <span class="br0">&#40;</span>fcgi_fd &lt; 0<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fprintf<span class="br0">&#40;</span>stderr, &quot;Couldn't create FastCGI listen socket on port %s\n&quot;, bindpath<span class="br0">&#41;</span>;<br />
&nbsp;#ifdef ZTS
        </div>
    </div>
</div>

<p>Применять к файлу <code>sapi/cgi/cgi_main.c</code>.</p>
<p><strong>Что даёт:</strong> значение backlog можно задавать в переменной окружения <code>PHP_FCGI_LISTEN_BACKLOG</code>.</p>
<p>Проверялось на PHP 5.2.10-2ubuntu6.3 (Ubuntu 9.10).</p>
<p><strong>Установка в Debian/Ubuntu:</strong></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p74756">
        <div class="code bash" id="p747code56">
<span class="kw2">mkdir</span> php <span class="sy0">&amp;&amp;</span> <span class="kw3">cd</span> php<br />
<span class="kw2">apt-get</span> <span class="kw3">source</span> php5-cgi<br />
<span class="kw2">sudo</span> <span class="kw2">apt-get</span> <span class="kw2">install</span> devscripts build-essential <span class="kw2">wget</span><br />
<span class="kw2">sudo</span> <span class="kw2">apt-get</span> build-dep php5-cgi<br />
<span class="kw3">cd</span> php5-<span class="sy0">*</span><br />
<span class="re2">QUILT_PATCHES</span>=debian<span class="sy0">/</span>patches quilt <span class="re5">--quiltrc</span> <span class="sy0">/</span>dev<span class="sy0">/</span>null push <span class="re5">-a</span><br />
<span class="kw2">wget</span> http:<span class="sy0">//</span>d.sjinks.pro<span class="sy0">/</span>listen-backlog-env-var.patch <span class="re5">-O</span> debian<span class="sy0">/</span>patches<span class="sy0">/</span>listen-backlog-env-var.patch<br />
<span class="re2">QUILT_PATCHES</span>=debian<span class="sy0">/</span>patches quilt <span class="re5">--quiltrc</span> <span class="sy0">/</span>dev<span class="sy0">/</span>null import debian<span class="sy0">/</span>patches<span class="sy0">/</span>listen-backlog-env-var.patch<br />
<span class="re2">QUILT_PATCHES</span>=debian<span class="sy0">/</span>patches quilt <span class="re5">--quiltrc</span> <span class="sy0">/</span>dev<span class="sy0">/</span>null push <span class="re5">-a</span><br />
<span class="re2">QUILT_PATCHES</span>=debian<span class="sy0">/</span>patches quilt <span class="re5">--quiltrc</span> <span class="sy0">/</span>dev<span class="sy0">/</span>null pop <span class="re5">-a</span><br />
<span class="kw2">debuild</span> clean<br />
<span class="kw2">debuild</span> <span class="re5">-i</span> <span class="re5">-us</span> <span class="re5">-uc</span> <span class="re5">-b</span>
        </div>
    </div>
</div>

<p><strong>Практические результаты:</strong> при помощи <code>ab</code> генерировалась сильная нагрузка (DoS) на сервер (10,000 запросов в 200 параллельных потоков):</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p74757">
        <div class="code bash" id="p747code57">
ab <span class="re5">-n</span> 10000 <span class="re5">-c</span> <span class="nu0">200</span> http:<span class="sy0">//</span>example.com<span class="sy0">/</span>
        </div>
    </div>
</div>

<p><strong>Стандартный PHP</strong> смог обработать <strong>2,275 запросов из 10,000</strong> — остальные запросы были отвергнуты (<strong>502 Bad Gateway</strong>).</p>
<p>После применения патча и</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p74758">
        <div class="code bash" id="p747code58">
<span class="kw3">echo</span> 2048 <span class="sy0">&gt;</span> <span class="sy0">/</span>proc<span class="sy0">/</span>sys<span class="sy0">/</span>net<span class="sy0">/</span>core<span class="sy0">/</span>somaxconn<br />
<span class="kw3">export</span> <span class="re2">PHP_FCGI_LISTEN_BACKLOG</span>=<span class="nu0">1024</span>
        </div>
    </div>
</div>

<p>и перезапуска, PHP смог обработать <strong>все запросы</strong>.</p>
<p>Изменилось всего-то 9 строк кода, а какой результат!</p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/php/747-php-cgi-patch-to-configure-listen-backlog/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/php/747-php-cgi-patch-to-configure-listen-backlog/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Модуль поддержки tcpwrappers для nginx</title>
		<link>http://blog.sjinks.pro/security/654-tcpwrappers-support-for-nginx/</link>
		<comments>http://blog.sjinks.pro/security/654-tcpwrappers-support-for-nginx/#comments</comments>
		<pubDate>Sat, 10 Oct 2009 01:15:47 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[Администрирование]]></category>
		<category><![CDATA[Безопасность]]></category>
		<category><![CDATA[libwrap]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[ngx_tcpwrappers]]></category>
		<category><![CDATA[TCP Wrappers]]></category>
		<category><![CDATA[безопасность]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=654</guid>
		<description><![CDATA[Использование libwrap для контроля доступа TCP Wrappers — система контроля доступа, используемая для ограничения доступа к серверам на UNIX-подобных операционных системах (Linux, BSD). Контроль доступа может осуществляться, например, по имени хоста (полному или частичному), адресу, подсети. Подробности приведены здесь. TCP Wrappers очень удобно использовать с программами для защиты от червей (BlackHosts, DenyHosts, Fail2ban), в частности, для [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/security/654-tcpwrappers-support-for-nginx/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Использование <a href="http://blog.sjinks.pro/tag/libwrap/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  libwrap">libwrap</a> для контроля доступа</em></h2>
<p><a href="http://blog.sjinks.pro/tag/tcp-wrappers/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  TCP Wrappers">TCP Wrappers</a> — система контроля доступа, используемая для ограничения доступа к серверам на UNIX-подобных операционных системах (<a href="http://blog.sjinks.pro/tag/linux/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Linux">Linux</a>, BSD). Контроль доступа может осуществляться, например, по имени хоста (полному или частичному), адресу, подсети. Подробности приведены <a href="http://linux.die.net/man/5/hosts_access">здесь</a>.</p>
<p>TCP Wrappers очень удобно использовать с программами для защиты от червей (BlackHosts, DenyHosts, Fail2ban), в частности, для защиты от <a href="http://blog.sjinks.pro/security/361-minimizing-consequences-of-http-scans/">HTTP-сканирования</a>.</p>
<p>Огромным достоинством TCP Wrappers является возможность динамической конфигурации списков контроля доступа (что избавляет от необходимости перезапускать защищаемый сервис) и простота файлов конфигурации (это субъективно).</p>
<p>К сожалению, <a href="http://blog.sjinks.pro/tag/nginx/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  nginx">nginx</a> не поддерживает TCP Wrappers из коробки. К счастью, это можно исправить.<span id="more-654"></span></p>
<p>Заказчик, для которого писался данный модуль, не возражал против выкладывания исходного кода модуля в общий доступ, чем я и воспользовался.</p>
<p>Прежде, чем перейти непосредственно к модулю, отмечу, что у TCP Wrappers есть несколько особенностей дизайна, о которых нужно знать:</p>
<ul>
<li>самое большое моё разочарование заключается в том, что <code>libwrap</code> (библиотека, реализующая функциональность TCP Wrappers) не является библиотекой с потоковой безопасностью (thread safety). Иными словами, если два потока одновременно обратятся к <code>libwrap</code>, результат может получиться весьма странным. Это связано с тем, что <code>libwrap</code> использует нереентера́бельные (какое слово! но по-английски звучит лучше) функции, например, <span class="codebox"><code class="c">strtok<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>, <span class="codebox"><code class="c">gethostbyname<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span>, <span class="codebox"><code class="c">gethostbyaddr<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> и т.п. Если nginx собран с поддержкой многопоточности, то на нагруженных серверах использование <code>libwrap</code> может привести к потерям производительности (из-за того, что доступ к функциям <code>libwrap</code> должен быть последовательным — приходится использовать мьютекс). Если же nginx собирался без поддержки многопоточности (как, например, в Linux), то такая проблема не возникает;</li>
<li>динамическая конфигурация списков контроля доступа имеет свою цену: при каждом запросе будут читаться и разбираться файлы <code>/etc/hosts.allow</code> и <code>/etc/hosts.deny</code>, что на высоконагруженных серверах может быть неприемлемо.</li>
</ul>
<h3>Сборка модуля</h3>
<p>Переходим непосредственно к модулю. Так как nginx не поддерживает динамические модули, nginx придётся пересобирать из исходного кода. Предполагая, что исходный код nginx находится в <code>~/nginx</code>, исходный код модуля <a href="http://blog.sjinks.pro/tag/ngx_tcpwrappers/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  ngx_tcpwrappers">ngx_tcpwrappers</a> — в <code>~/nginx/ngx_tcpwrappers</code>, выглядеть это всё будет примерно так:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p65460">
        <div class="code bash" id="p654code60">
<span class="kw3">cd</span> ~<span class="sy0">/</span>nginx<br />
.<span class="sy0">/</span>configure \<br />
&nbsp; &nbsp; <span class="re5">--conf-path</span>=<span class="sy0">/</span>etc<span class="sy0">/</span>nginx<span class="sy0">/</span>nginx.conf \<br />
&nbsp; &nbsp; <span class="re5">--error-log-path</span>=<span class="sy0">/</span>var<span class="sy0">/</span>log<span class="sy0">/</span>nginx<span class="sy0">/</span>error.log \<br />
&nbsp; &nbsp; <span class="co0"># здесь идут остальные параметры, передаваемые configure \</span><br />
&nbsp; &nbsp; <span class="re5">--add-module</span>=.<span class="sy0">/</span>ngx_tcpwrappers<br />
<span class="kw2">make</span><br />
<span class="kw2">sudo</span> <span class="kw2">make</span> <span class="kw2">install</span>
        </div>
    </div>
</div>

<h3>Конфигурация модуля</h3>
<p>Директивы конфигурации:</p>
<ul>
<li><a href="#tcpwrappers"><code>tcpwrappers</code></a></li>
<li><a href="#tcpwrappers_daemon"><code>tcpwrappers_daemon</code></a></li>
<li><a href="#tcpwrappers_thorough"><code>tcpwrappers_thorough</code></a></li>
</ul>
<hr/>
<div id="tcpwrappers"></div>
<p><strong>Синтаксис:</strong> <code>tcpwrappers [on|off]</code><br />
<strong>По умолчанию:</strong> <code>tcpwrappers off</code><br />
<strong>Контекст:</strong> <code>http</code>, <code>server</code>, <code>location</code>, <code>limit_except</code><br />
<strong>Описание:</strong> данная директива разрешает либо запрещает использование TCP Wrappers для контроля доступа к ресурсу. <code>tcpwrappers off</code> полностью отключает использование TCP Wrappers, что позволяет избежать потерь производительности.</p>
<hr/>
<div id="tcpwrappers_daemon"></div>
<p><strong>Синтаксис:</strong> <code>tcpwrappers_daemon name</code><br />
<strong>По умолчанию:</strong> <code>tcpwrappers_daemon nginx</code><br />
<strong>Контекст:</strong> <code>http</code>, <code>server</code>, <code>location</code>, <code>limit_except</code><br />
<strong>Описание:</strong> данная директива позволяет задать имя демона, которое используется в файлах <code>/etc/hosts.{allow,deny}</code> для идентификации процесса.</p>
<hr/>
<div id="tcpwrappers_thorough"></div>
<p><strong>Синтаксис:</strong> <code>tcpwrappers_thorough [on|off]</code><br />
<strong>По умолчанию:</strong> <code>tcpwrappers_thorough off</code><br />
<strong>Контекст:</strong> <code>http</code>, <code>server</code>, <code>location</code>, <code>limit_except</code><br />
<strong>Описание:</strong> данная директива управляет тщательностью проверки клиента.</p>
<p>При <code>tcpwrappers_thorough off</code> используется функция <a href="http://linux.die.net/man/3/hosts_access"><code>hosts_ctl(3)</code></a>; контроль осуществляется исключительно по IP-адресу клиента (в том плане, что <code>libwrap</code> передаётся только IP-адрес).</p>
<p>При <code>tcpwrappers_thorough on</code> используется функция <a href="http://linux.die.net/man/3/hosts_access"><code>hosts_access(3)</code></a>; контроль может осуществляться по адресу и имени клиента и сервера. Данная проверка является более тщательной, но имеет свою цену: необходимость выполнения DNS-запросов для получения имени клиента и сервера.</p>
<hr/>
<h3>Исходный код</h3>
<p><strong><a href='http://bazaar.launchpad.net/~sjinks/ngx-tcpwrappers/trunk/files'>Исходный код модуля ngx_tcpwrappers</a></strong></p>
<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/security/654-tcpwrappers-support-for-nginx/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/security/654-tcpwrappers-support-for-nginx/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>ngx_drop_privs: принцип минимальных привилегий в nginx</title>
		<link>http://blog.sjinks.pro/security/648-ngx_drop_privs-minimum-privilege-principle-nginx/</link>
		<comments>http://blog.sjinks.pro/security/648-ngx_drop_privs-minimum-privilege-principle-nginx/#comments</comments>
		<pubDate>Wed, 23 Sep 2009 02:35:11 +0000</pubDate>
		<dc:creator>Vladimir</dc:creator>
				<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[Безопасность]]></category>
		<category><![CDATA[ngx_drop_privs]]></category>
		<category><![CDATA[безопасность]]></category>

		<guid isPermaLink="false">http://blog.sjinks.pro/?p=648</guid>
		<description><![CDATA[Забираем у nginx лишние привилегии nginx — замечательный web-сервер, но, как и практически любой программный продукт, не свободен от ошибок, временами весьма критических. Архитектура nginx такова, что обычно имеется один привилегированный процесс (запускаемый от всемогущего root) и один или более рабочих процессов (обычно работающих от имени непривилегированного пользователя). Тем не менее, я видел конфигурации, в которых все [...]<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/security/648-ngx_drop_privs-minimum-privilege-principle-nginx/">источник</a> обязательно.</p>]]></description>
			<content:encoded><![CDATA[<h2><em>Забираем у <a href="http://blog.sjinks.pro/tag/nginx/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  nginx">nginx</a> лишние привилегии</em></h2>
<p>nginx — замечательный web-сервер, но, как и практически любой программный продукт, не свободен от ошибок, временами весьма <a href="http://www.kb.cert.org/vuls/id/180065">критических</a>.</p>
<p>Архитектура nginx такова, что обычно имеется один привилегированный процесс (запускаемый от всемогущего <code>root</code>) и один или более рабочих процессов (обычно работающих от имени непривилегированного пользователя). Тем не менее, я видел конфигурации, в которых все процессы nginx работают под привилегированным пользователем. Один из примеров — многопользовательский сервер, Document Root сайтов на котором имеет права вида 0700 (запускать несколько nginx и настраивать проксирование — тоже не лучший выход).</p>
<p>Кроме того, за последние неполные пять лет в nginx найдено 112 ошибок (segmentation fault), некоторые из которых теоретически могут дать возможность выполнения произвольного кода в системе (<span class="codebox"><code class="bash"><span class="kw2">wget</span> http:<span class="sy0">//</span>sysoev.ru<span class="sy0">/</span>nginx<span class="sy0">/</span>changes.html <span class="re5">-q</span> <span class="re5">-O</span> - <span class="sy0">|</span> <span class="kw2">grep</span> segmentation <span class="sy0">|</span> <span class="kw2">wc</span> <span class="re5">-l</span></code></span>).</p>
<p>Я не знаю, все ли ошибки затрагивали только рабочие процессы, либо были ошибки в главном процессе — рисковать не хочется. Так и родилась идея написать модуль для nginx — <code><a href="http://blog.sjinks.pro/tag/ngx_drop_privs/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  ngx_drop_privs">ngx_drop_privs</a></code>.<span id="more-648"></span></p>
<p>Данный модуль будет работать, скорее всего, только под <a href="http://blog.sjinks.pro/tag/linux/" class="st_tag internal_tag" rel="tag" title="Записи, помеченные с  Linux">Linux</a>, при этом для работы требуется наличие библиотеки <code>libcap</code> и ядра, собранного с поддержкой capabilities (я давно не видел ядра без такой поддержки).</p>
<p>Идея заключается в том, что процессу, который порождает потомков, меняет их UID/GID и периодически меняет владельца log-файлов, привилегий <code>root</code> слишком <a href="http://linux.die.net/man/7/capabilities">много</a>. Вполне достаточно <code>CAP_SETUID</code>, <code>CAP_SETGID</code> и <code>CAP_CHOWN</code> (если нужна ротация логов).</p>
<p><code>CAP_CHOWN</code> — весьма опасная возможность, и если есть возможность её не давать, лучше её не давать <img src='http://static.sjinks.info/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Модуль при инициализации проверяет, запущен ли главный процесс от имени <code>root</code>. Если да, то <code>capabilities</code> сбрасываются на указанные в директиве <code>dropprivs_set_caps</code>.</p>
<p>Директива <code>dropprivs_set_caps</code> является глобальной (как, например, <code>daemon</code>, <code>user</code> и <code>pid</code>) и имеет формат</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p64865">
        <div class="code nginx" id="p648code65">
dropprivs_set_caps <span class="st0">&quot;строка&quot;</span>;
        </div>
    </div>
</div>

<p>где <code>строка</code> — привилегии в формате, который понимает функция <a href="http://linux.die.net/man/3/cap_from_text"><code>cap_from_text()</code></a>. Никто не говорил, что будет легко.</p>
<p>На своей тестовой машине я использую такое значение:</p>
          
<div class="codebox">
    <div class="the_code" style="" id="p64866">
        <div class="code nginx" id="p648code66">
dropprivs_set_caps <span class="st0">&quot;cap_setuid,cap_setgid=ep&quot;</span>;
        </div>
    </div>
</div>

<p>Это равносильно сбросу всех привилегий и установке <code>CAP_SETUID</code> и <code>CAP_SETGID</code> в действующем (<strong>e</strong>ffective) и разрешённом (<strong>p</strong>ermitted) наборе привилегий.</p>
<p>Те, кому это всё интересно, могут <strong><a href="http://d.sjinks.pro/ngx_drop_privs.tar.bz2">скачать исходный код модуля <code>ngx_drop_privs</code></a></strong>. Код пока ещё сыроват (да, написан за полтора часа этой ночью) и будет совершенствоваться. Любые предложения/патчи are always welcome. <del datetime="2010-02-16T02:47:48+00:00">Планируется поддержка <code>chroot</code>, что позволит меньше беспокоиться о <code>CAP_CHOWN</code> и <code>CAP_SETUID</code>.</del></p>
<p><strong>UPDATE 16.02.2010:</strong> добавлена экспериментальная поддержка <code>chroot</code>. Реализуется глобальной директивой </p>
          
<div class="codebox">
    <div class="the_code" style="" id="p64867">
        <div class="code nginx" id="p648code67">
dropprivs_chroot <span class="st0">&quot;каталог&quot;</span>;
        </div>
    </div>
</div>

<p>Выполняется <span class="codebox"><code class="c">chroot<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> <strong>до</strong> сброса привилегий, поэтому явным образом указывать <code>CAP_SYS_CHROOT</code> в списке разрешённых привилегий не нужно (и вообще не рекомендуется). Перед вызовом <span class="codebox"><code class="c">chroot<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> выполняется <span class="codebox"><code class="c">chdir<span class="br0">&#40;</span><span class="br0">&#41;</span></code></span> в указанный каталог, поэтому если привилегии настроены нормально, покинуть <code>chroot</code> демон не сможет. Тем не менее, это накладывает свои ограничения:</p>
<ul>
<li>пути в виртуальных хостах должны быть относительны нового корня;</li>
<li>в новом корне должен существовать как минимум <code>/dev/zero</code>;</li>
<li>PID-файл создаётся относительно нового корня;</li>
<li>что происходит с лог-файлами, пока сказать не могу, но есть подозрение что ротация логов без танцев с бубном работать не будет.</li>
</ul>
<p><strong>Сборка модуля:</strong></p>
          
<div class="codebox">
    <div class="the_code" style="" id="p64868">
        <div class="code bash" id="p648code68">
<span class="kw3">cd</span> <span class="sy0">/</span>path<span class="sy0">/</span>to<span class="sy0">/</span>nginx<span class="sy0">/</span><span class="kw3">source</span><br />
.<span class="sy0">/</span>configure \ <span class="co0"># Здесь идут обычные опции configure</span><br />
&nbsp; &nbsp; <span class="re5">--add-module</span>=<span class="sy0">/</span>path<span class="sy0">/</span>to<span class="sy0">/</span>ngx_drop_privs<br />
<span class="kw2">make</span>
        </div>
    </div>
</div>

<p>© 2012 <a href="http://blog.sjinks.pro">Ars Longa, Vita Brevis</a>. Все права защищены. Перепубликация материалов без разрешения автора запрещена.</p>
<p>При использовании материалов блога наличие активной не закрытой от индексирования ссылки на <a href="http://blog.sjinks.pro/security/648-ngx_drop_privs-minimum-privilege-principle-nginx/">источник</a> обязательно.</p>]]></content:encoded>
			<wfw:commentRss>http://blog.sjinks.pro/security/648-ngx_drop_privs-minimum-privilege-principle-nginx/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

