<?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>Programátorské techniky nejen v C++</title>
	<atom:link href="http://novacisko.blog.root.cz/feed/" rel="self" type="application/rss+xml" />
	<link>http://novacisko.blog.root.cz</link>
	<description>Just another blog.root.cz weblog</description>
	<lastBuildDate>Sun, 06 May 2012 23:13:38 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Skrytá úskalí vícenásobné dědičnosti v C++</title>
		<link>http://novacisko.blog.root.cz/2012/05/06/skryta-uskali-vicenasobne-dedicnosti-v-c/</link>
		<comments>http://novacisko.blog.root.cz/2012/05/06/skryta-uskali-vicenasobne-dedicnosti-v-c/#comments</comments>
		<pubDate>Sun, 06 May 2012 22:34:06 +0000</pubDate>
		<dc:creator>Ondřej Novák</dc:creator>
				<category><![CDATA[Nezařazené]]></category>

		<guid isPermaLink="false">http://novacisko.blog.root.cz/?p=73</guid>
		<description><![CDATA[Upozorňuji dopředu, že nehodlám zde probírat to, co všichni programátoři v C++ určitě znají a co si mohou přečíst na milionech stránkách, které nabídne google. Myslím tím zejména ony problémy s diamantovým děděním a jak správně pracovat s virtuální dědičností. Při práci s generikou, kdy se hojně používají "prázdné třídy" časem narazíme na další komplikaci, [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Upozorňuji dopředu, že nehodlám zde probírat to, co všichni programátoři v C++ určitě znají a co si mohou přečíst na milionech stránkách, které nabídne google. Myslím tím zejména ony problémy s diamantovým děděním a jak správně pracovat s virtuální dědičností. Při práci s generikou, kdy se hojně používají "prázdné třídy" časem narazíme na další komplikaci, které i zkušeného programátora překvapí a možná mu trošku zkomplikují život. </strong></p>
<h2>Prázdná třída</h2>
<p>V generickém programování (to jsou takové ty programy, ve kterých se hojně vyskytuje klíčové slovo <strong>template</strong>) postupem času asi každý programátor nalezne zalíbení ve vytváření prázdných tříd. To jsou třídy, které obsahují deklarace, nebo definice metod, avšak neobsahují žádné proměnné, ani virtuální metody. Objekt takové třídy by ve skutečnosti neobsadil žádnou paměť, protože jeho instance vlastně nic neobsahuje. Velmi často používáme prázdné třídy k vytváření alokátorů nebo porovnávačů</p>
<pre>class CmpItem {
public:
    bool operator()(const Item &amp;a, const Item &amp;b) const;
};
typedef std::set&lt;Item, CmpItem&gt; MujSet;</pre>
<p>Kdo se s tím ještě nesetkal, toho překvapí, že dotaz na<strong> sizeof(CmpItem)</strong> vrátí hodnotu<strong> 1</strong>, ačkoliv třída je prázdná a neobsahuje jedinou proměnnou. Je to přitom záměr. Představte si, že by taková třída propadla zkrz parametr šablony do nějakého pole. Pokud by velikost třídy byla nastavena na nulu, všechny hodnoty v poli by měly stejnou adresu a pole by také mělo velikost 0 bajtů. Převod ukazatele na index pak nutně vede na dělení nulou. Tyto důvody a dalších N podobných komplikací (například, že dvě různé instance stejné třídy nesmějí sdílet stejnou adresu v paměti) vedly autory norem k pravidlu, že instance každé třídy musí alokovat vždy minimálně 1 bajt paměti. Stejně se například chová malloc(0) ... alokuje 1 bajt a vrátí jeho adresu.</p>
<h2>Prázdná třída a dědění</h2>
<p>Pokud použijeme prázdnou třídu jako členskou proměnnou, zabere 1 bajt v rozvržení instance třídy. Pokud je taková proměnná umístěna mezi vícebajtové proměnné, které je třeba zarovnat na dělitelnou adresu, pak překladač za takovou proměnnou umístí povinný <em>padding. </em>Na 32bitové platformě to znamená, nikoliv 1 bajt, ale 4 bajty! Dost vyplýtvaného místa na prázdnou třídu nemyslíte?</p>
<p>Tomuto plýtvání lze zabránit tak, že namísto proměnné budeme prázdnou třídu dědit.</p>
<pre>class A {};
class B {
    A a;
    int x;
};
 class C: public A {
   int x;
};</pre>
<p>GCC vám řekne toto:</p>
<pre>sizeof(A): 1
sizeof(B): 8
sizeof(C): 4</pre>
<p>Račte si to vyzkoušet:  <a href="http://codepad.org/mUADjrZ1">(codepad)</a>. Příklad krásně demonstruje to co jsem napsal. Proměnná <strong>a</strong> má skutečně velikost 1 a protože <strong>x</strong> musí být zarovnáno, je za proměnnou <strong>a</strong> doplněn padding. Instance třídy pak zabere 8 bajtů. Pokud však <strong>třídu A</strong> podědíme, zmenšíme velikost instance na poloviční velikost. Za <strong>třídu A</strong> si lze dosadit třídu dodanou šablonou, například výše uvedený příklad CmpItem, který může (ale nemusí) být deklarován prázdný (například při řazení indexů v databázi bude obsahovat odkaz na kontejner dat, jelikož index obsahuje pouze indexy ... což jsou vlastně jen čísla, která bez odkazu na data nelze řadit).</p>
<h2>Vícenásobná dědičnost.</h2>
<p>Pokud máme možnost do šablony dodat jen jednu třídu, která může být (běžně) prázdná  (například CmpItem), lze to obejít děděním. Co když těch tříd máme víc? Můžeme použít vícenásobné dědění? Zdálo by se logické, že to bude fungovat obdobně.</p>
<p>Ve skutečnosti to je problém, protože to nezafunguje tak, jak bychom si představovali. Navíc se implementace liší podle platformy a překladače. K demonstraci jsem si připravil následující příklad:</p>
<pre>#include &lt;iostream&gt;

class A1 {};

class A: public A1 {};

class B1 {};

class B: public A1, public B1 {};

class C1 {};

class C: public A1, public B1, public C1 {};

class D1 {};

class D: public A1, public B1, public C1, public D1 {};

class E1: public A1, public B1 {};

class E: public E1 {};

class F1: public A1, public B1 {};

class F: public E1, public F1 {};

class G1: public A {};

class G: public E1, public G1 {};

class H: public C, public D {};

class I: public H, public G {};

int main(int argc, char* argv[])
{
 std::cout &lt;&lt; "sizeof(A): " &lt;&lt; sizeof(A) &lt;&lt; std::endl;
 std::cout &lt;&lt; "sizeof(B): " &lt;&lt; sizeof(B) &lt;&lt; std::endl;
 std::cout &lt;&lt; "sizeof(C): " &lt;&lt; sizeof(C) &lt;&lt; std::endl;
 std::cout &lt;&lt; "sizeof(D): " &lt;&lt; sizeof(D) &lt;&lt; std::endl;
 std::cout &lt;&lt; "sizeof(E): " &lt;&lt; sizeof(E) &lt;&lt; std::endl;
 std::cout &lt;&lt; "sizeof(F): " &lt;&lt; sizeof(F) &lt;&lt; std::endl;
 std::cout &lt;&lt; "sizeof(G): " &lt;&lt; sizeof(G) &lt;&lt; std::endl;
 std::cout &lt;&lt; "sizeof(H): " &lt;&lt; sizeof(H) &lt;&lt; std::endl;
 std::cout &lt;&lt; "sizeof(I): " &lt;&lt; sizeof(I) &lt;&lt; std::endl;
}</pre>
<p>Výsledky, které vrací překladač od Microsoftu, Visual C++ 2008:</p>
<pre>sizeof(A): 1
sizeof(B): 1
sizeof(C): 2
sizeof(D): 3
sizeof(E): 1
sizeof(F): 3
sizeof(G): 2
sizeof(H): 6
sizeof(I): 9</pre>
<p>Jiná čísla vrací GCC</p>
<pre>sizeof(A): 1
sizeof(B): 1
sizeof(C): 1
sizeof(D): 1
sizeof(E): 1
sizeof(F): 2
sizeof(G): 2
sizeof(H): 2
sizeof(I): 4</pre>
<p>Rozebereme si nejprve výsledek od <strong>Microsoftu</strong>. Vzhledem k tomu, že jsem nikde nenašel oficiální popis, budu vycházet z mého pozorování. Podle všeho Visual C++ započítává každou další třídu, ze které se dědí, jako členskou proměnnou o jednom bajtě. To je krásně vidět na výsledcích pro třídy A - D. Zatímco třída A má velikost 1 "z povinnosti", velikost třídy B je opravdu 1 bajt, protože pro dědění z B1 je tento bajt alokován. Přitom jde evidentně o zbytečnou operaci, protože obě podtřídy budou mít ukazatel stejné hodnoty. V případě třídy C je nutné započítat už dva bajty za dvě třídy navíc. Podobně je na tom třída D. Z další analýzy se zdá, že každá třída navíc navyšuje počet alokovaných adres pro prázdnou třídu. Aby jsme si udělali přehled, je nutné provést tranzitivní uzávěr celé hierarchie a označit si třídy, které jsou ve vícenásobné dědičnosti navíc. Rozepíšeme si třídu I:</p>
<p>I,H,<strong>G</strong>,C,<strong>D</strong>,E1,<strong>G1</strong>,A1,<strong>B1</strong>,<strong>C1</strong>,A1,<strong>B1</strong>,<strong>C1</strong>,<strong>D1</strong>,A1,<strong>B1</strong>,A,A1</p>
<p>Tučně jsem označil třídy, které se nachází na druhém a dalším místě v deklaraci vícenásobné dědičnosti. Vidíte, že jich je devět, a z toho vyplývá devět alokovaných bajtů pro celou třídu I</p>
<p>Proč tohle Visual C++ dělá opravdu netuším. Ale podle mne se to možná vyjasní, pokud se podíváme na výsledek GCC. Podle mého názoru se Visual C++ snaží zabránit situaci, kdy některé třídy, které se v rozložené hierarchii opakují, by mohly sdílet stejnou adresu, přestože jde o různé instance... a jak víme, různé instance nesmějí mít stejnou adresu, viz první kapitola. Nicméně způsob, jakým se to řeší, není úplně nejšťastnější.</p>
<p>To <strong>GCC </strong>není vícenásobnou dědičností nijak rozhozeno a i pro třídu D vrací velikost 1.  Teprve třída F, ve které se <strong>dvakrát </strong>vyskytují třídy A1 a B1 alokuje dvě adresy. Opět se podíváme na třídu I:</p>
<p>I,H,G,C,D,E1,G1,<span style="text-decoration: underline">A1</span>,<strong>B1</strong>,<em>C1</em>,<span style="text-decoration: underline">A1</span>,<strong>B1</strong>,<em>C1</em>,D1<span style="text-decoration: underline">,A1</span>,<strong>B1</strong>,A,<span style="text-decoration: underline">A1</span></p>
<p>Tady jsem podtrhl třídu A1, tučně označil třídu B1 a kurzívou třídu C1. Vidíte, že po rozbalení třídy I zde máme 4x třídu A1. Protože se nejedná o virtuální dědění, žádný diamant se zde nekoná, a třída A1 je zde zastoupena čtyřmi instancemi, kde každá instance by měla ležet na jiné adrese. Zajímavé je, že GCCčku není proti srsti, že stejnou adresu jako A1 bude pravděpodobně sdílet i B1. Jde vlastně o objekt A1,B1,C1, který dohromady může alokovat pouze jednu adresu. Jiný objekt A1, B1 okupuje druhou adresu, a tak dále. Rozložení podtříd do adres můžeme zobrazit třeba takto.</p>
<pre>+0 I,G,G1,A,A1
+1 E1,A1,B1
+2 H,D,A1,B1,C1,D1
+3 C,A1,B1,C1</pre>
<p>Neověřoval jsem to přesně, ale cílem je, aby se žádná třída neopakovala na jednom řádku. Samozřejmě, že celá problematika bude složitější v tom, že nestačí jen dodržet pravidlo nesdílení adresy pro stejné podtřídy, ale zároveň každá potřída musí fungovat samostatně, takže počet alokovaných adres může být i vyšší. Například, třída G alokuje 2 adresy, tedy musí alokovat offset 0 a offset 1. Třída H také alokuje 2 adresy, zbývají pro ně offset 2 a offset 3.  Ostatní třídy alokují jednu adresu a tak se musí naskládat ke svým nadtřídám na každém řádku. Celé to pak tvoří třídu I, která začíná na offsetu 0 a zabírá 4 bajty.</p>
<p>Linuxovou verzi si můžete opět vyzkoušet na <a href="http://codepad.org/ZfDaqmn9">codepadu</a></p>
<h2>Závěr</h2>
<p>No na závěr bych napsal toto: Nepřehánět to s vícenásobnou dědičnosti. Pokud jsem si doposud šablony skládat přes vícenásobnou dědičnost do jedné souhrnné šablony, tak jsem si neuvědomoval tento problém a žil jsem v domění, že to překladač nějak dobře zoptimalizuje. Nedělo se tak. A tak se jednoho krásného dne stalo, že jsem objevil podivný padding u třídy ConstStrA, která by se dala zkráceně zapsat takto:</p>
<pre>class ConstStrA {
public:
      const char *str;
      unsigned int length;
};</pre>
<p>A ačkoliv z tohoto pohledu má mít třída pouhých 8 bajtů, ve skutečnosti má bajtů 16, protože není takto jednoduše deklarovaná, ale jedná se o kombinaci několik šablon (například lze změnit char za jiný typ, přes další šablonu se tam importuje rozhrani pro práci s řetězci). Výsledná třída má před první proměnnou 4 bajtový padding, a stejný padding se nachází na konci. To vše vytvořilo vícenásobné dědění prázdných tříd různě po cestě navěšených (a aby to nebylo všechno, při předávání instance třídy se všechny bajty, včetně nepoužívaného paddingu, poctivě kopírují na zásobník, Visual C++)</p>
<ul>
<li>Pro představu složitosti, fragment třídy ConstStrA (ConstString&lt;T&gt;) jsem umístil na pasteBin: <a href="http://pastebin.com/i3gLRZrr">http://pastebin.com/i3gLRZrr</a>. Už tady najdete důvod toho druhého zbytečného paddingu. A to nechtějte vidět, jak vypadá třída ArrayRef&lt;&gt; a kolik podtříd je v ní schováno.</li>
</ul>
<p>Je hlavně podivné, jak vícenásobnou dědičnost řeší Microsoft Visual C++. Předpokládám, že v normě C++ není jasně definován layout tříd, které používají vícenásobnou dědičnost v souvislosti s prázdnými třídami. I z toho důvodu musí člověk být opatrný, pakliže chce prázdné třídy používat jako nástroj generického programování. Čas od času se mu může důsledek použitého návrhu šablony zásadně promítnout do výsledného kódu.</p>
]]></content:encoded>
			<wfw:commentRss>http://novacisko.blog.root.cz/2012/05/06/skryta-uskali-vicenasobne-dedicnosti-v-c/feed/</wfw:commentRss>
		<slash:comments>37</slash:comments>
		</item>
		<item>
		<title>Tvoříme &#8220;vláčky&#8221; nejen v C++</title>
		<link>http://novacisko.blog.root.cz/2012/01/25/tvorime-vlacky-nejen-v-c/</link>
		<comments>http://novacisko.blog.root.cz/2012/01/25/tvorime-vlacky-nejen-v-c/#comments</comments>
		<pubDate>Wed, 25 Jan 2012 00:00:53 +0000</pubDate>
		<dc:creator>Ondřej Novák</dc:creator>
				<category><![CDATA[Nezařazené]]></category>

		<guid isPermaLink="false">http://novacisko.blog.root.cz/?p=50</guid>
		<description><![CDATA[Dnes bych se chtěl ve svém krátkém blogpostu podělit o programovací techniku, kterou nazývám "vláček". Myslím, že to nebude nic neznámého, je to věc, která se dá uplatnit nejen C++, ale většinou u všech objektových jazyků, kde je volání prováděno zkrze zápis objekt.metoda(), jako třeba Java. V některých jazycích najdeme vláčky v základu (smalltalk - [...]]]></description>
			<content:encoded><![CDATA[<p>Dnes bych se chtěl ve svém krátkém blogpostu podělit o programovací techniku, kterou nazývám "vláček". Myslím, že to nebude nic neznámého, je to věc, která se dá uplatnit nejen C++, ale většinou u všech objektových jazyků, kde je volání prováděno zkrze zápis objekt.metoda(), jako třeba Java. V některých jazycích najdeme vláčky v základu (smalltalk - říká se tomu "message chain").</p>
<p>Asi nejznámější vláček v C++ pozná každý, je to zápis do std::cout.</p>
<pre>std::cout &lt;&lt; "Součet " &lt;&lt; a &lt;&lt; " a " &lt;&lt; b &lt;&lt; " rovná se " &lt;&lt; c &lt;&lt; std::endl;</pre>
<p>Vláček poznáme snadno, většinou je na začátku lokomotiva, díky níž je vláček vláčkem. Kdyby tam lokomotiva nebyla, překladač by nám vynadal. Pak následuje větší množství vagonků, které jsou pospojované stejným způsobem, a na konci někdy bývá speciální ukončující vagón, tedy něco, co vláček ukončuje a tím způsobí nějakou akci. Nemusí nutně vždycky, záleží na tom, zda chceme do vláčku zapojovat další vagonky. A nebo, pokud si objekt nepřeje vytvářet neuzavřené vláčky, spustí se akce na prvním středníku (zpravidla díky destruktoru u pomocného objektu vytvořeném lokomotivou, který tvoří spojení mezi vagony).</p>
<p>Vláčky lze psát i jinak, než s použitím přetížených operátorů. Můžeme použít normální funkce a zápis může být flexibilnější a čitelnější. Například v minulém blogpostu jsem psal o jedné třídě na spouštění procesů. Ukážu vám příklad jejího použití právě ve schopnosti tvořit vláčky.</p>
<pre>Process proc("cmd.exe");
ProcessEnv env = Process::getEnv();
<span>///.... zde celkem nevýznamný kód pracující s env.
integer retval = proc
    .setEnv(&amp;env)
    .arg("/C")
    .arg(fname)
    .arg(localBase)
    .workDir(localBase)
    .stdOut(execOutput)
    .stdErr(execOutput)
    .exec();</span></pre>
<p>Nenechte se zmást, že vláček je rozepsán na víc řádků. Opět je na začátku lokomotiva, zde proměnná <strong>proc</strong>, za lokomtivou jsou napojeny vagónky různých typů. Najdeme zde vagónek nesoucí environment, pak následují argumenty procesu, nastavení pracovní složky, přesměrování výstupu do souboru a tím pomyslným ukončujícím vozem je zde metoda exec(), která nakonec určuje, jak se s vláčkem bude zacházet. Když se podíváte na začátek, najdete tam, přiřazení do proměnné retval. I toto specifikuje metoda exec().</p>
<p>Ukážeme si ještě další vláčky, třeba vláček ve vláčku:</p>
<pre>PFactory_t f = connection.createJSON();
PNode_t r = f-&gt;newArray();</pre>
<pre>r-&gt;add(f-&gt;newObject()
   -&gt;add(f,"url",x.url)
   -&gt;add(f,"title",x.title)
   -&gt;add("lastVisit",f-&gt;newArray()
    -&gt;add(f,x.lastVisit.wYear)
    -&gt;add(f,x.lastVisit.wMonth)
    -&gt;add(f,x.lastVisit.wDay)
    -&gt;add(f,x.lastVisit.wHour)
    -&gt;add(f,x.lastVisit.wMinute)
    -&gt;add(f,x.lastVisit.wSecond)));</pre>
<p>Výše uvedený příklad představuje pole objektů obsahující atributy url, title a lastVisit, přičemž poslední proměnná je pole. Výsledkem je objekt uložený v JSONu.</p>
<p>Vláčky se nám hodí tam, kde potřebujeme k objektům nastavovat mnoho atributů, případně atributy nelze jednoduše nastavit konstruktorem. Příkladem může být třeba funkce na otevření souboru: <strong>openFile("foo.txt").create().truncate().append().lockExclusive().chmod(0644);</strong></p>
<h2>Jak deklarovat vláčkový styl.</h2>
<p>Deklarovat vláčkový styl je celkem snadné. Stačí, aby každá metoda, která má vystupovat jako vagónek, vracela referenci na objekt, který měnila (jednoduše na this). Pouze poslední "vagón" buď obsahuje void, nebo vrací něco jiného.</p>
<pre>class Prcoess {
public:
  Process &amp;arg(const char *arg);
  Process &amp;arg(natural number, natural base=10);
  Process &amp;arg(integer number, natural base=10);
  Process &amp;arg(float number);
  Process &amp;arg(double number);
  Process &amp;stdOut(const SeqFileOutput &amp;stream);
  Process &amp;stdErr(const SeqFileOutput &amp;stream);
  Process &amp;stdIn(const SeqFileInput &amp;stream);
  Process &amp;setEnv(const ProcessEnv *env);
  <span style="color: #010001"><span style="color: #010001">Process</span></span><span> &amp;</span><span style="color: #010001"><span style="color: #010001">workDir</span></span><span>(</span><span style="color: #010001"><span style="color: #010001">ConstStrW</span></span><span> </span><span style="color: #010001"><span style="color: #010001">path</span></span><span>);
  </span><span><span>int</span></span><span> </span><span style="color: #010001"><span style="color: #010001">exec</span></span><span>();
  ...
  ...
};</span></pre>
<p>Pakliže potřebujeme, aby se akce automaticky provedla na prvním středníku, musí lokomotiva vrátit namísto sebe sama pomocný objekt, který obsahuje deklarace vagonku. Vagonky si nadale přenášejí pouze referenci na pomocný objekt. Tento objekt má také destruktor, který ve svém těle vyvolá předem danou akci. </p>
<h2>Ještě jede příklad - bezpečný SQL dotaz</h2>
<p>Vláčkový styl jsem použil i pro sestavování SQL dotazů:</p>
<pre><span style="color: #010001"><span style="color: #010001">MySQL</span></span><span>::</span><span style="color: #010001"><span style="color: #010001">Query_t</span></span><span> </span><span style="color: #010001"><span style="color: #010001">q(...);

<span style="color: #010001"><span style="color: #010001">q</span></span><span>(</span><span style="color: #a31515"><span style="color: #a31515">"REPLACE stats </span></span><span style="color: #a31515"><span style="color: #a31515">(`datum`,`counter`,`product_ver`,`partnerId`,`value`) "
  "</span></span><span style="color: #a31515"><span style="color: #a31515">VALUES </span></span><span style="color: #a31515"><span style="color: #a31515">(CURDATE() - INTERVAL %4 DAY,%1,%2,%3,%5)"</span></span><span>)
   .<span style="color: #010001"><span style="color: #010001">arg</span></span><span>(</span><span style="color: #010001"><span style="color: #010001">iter</span></span><span>-&gt;</span><span style="color: #010001"><span style="color: #010001">first</span></span><span>.</span><span style="color: #010001"><span style="color: #010001">code</span></span><span>)
   <span>.</span><span style="color: #010001"><span style="color: #010001">arg</span></span><span>(</span><span style="color: #010001"><span style="color: #010001">iter</span></span><span>-&gt;</span><span style="color: #010001"><span style="color: #010001">first</span></span><span>.</span><span style="color: #010001"><span style="color: #010001">versionId</span></span><span>)
   <span>.</span><span style="color: #010001"><span style="color: #010001">arg</span></span><span>(</span><span style="color: #010001"><span style="color: #010001">iter</span></span><span>-&gt;</span><span style="color: #010001"><span style="color: #010001">first</span></span><span>.</span><span style="color: #010001"><span style="color: #010001">partnerId</span></span><span>)
   <span>.</span><span style="color: #010001"><span style="color: #010001">arg</span></span><span>(</span><span style="color: #010001"><span style="color: #010001">iter</span></span><span>-&gt;</span><span style="color: #010001"><span style="color: #010001">first</span></span><span>.</span><span style="color: #010001"><span style="color: #010001">dateModif</span></span><span>+</span><span style="color: #010001"><span style="color: #010001">age</span></span><span>)
   </span></span></span></span></span><span><span><span><span><span><span>.</span><span style="color: #010001"><span style="color: #010001">arg</span></span><span>(</span><span style="color: #010001"><span style="color: #010001">iter</span></span><span>-&gt;</span><span style="color: #010001"><span style="color: #010001">second</span></span><span>.</span><span style="color: #010001"><span style="color: #010001">value</span></span><span>)
   .exec();</span></span></span></span></span></span></span></span></pre>
<p><span style="color: #010001"><span style="color: #010001"><span>Tady máme trochu komplikovanou lokomotivu, protože obsahuje vlastní SQL dotaz. Funkce <strong>arg</strong> jsou patřičně přetížené, takže řetězce například escapují tak, aby se zabránilo SQL-Injecting.</span></span></span></p>
<p><span style="color: #010001"><span style="color: #010001"><span> PS: kdyby to někdo hledal, oficiálně se tomu říká <a href="http://en.wikipedia.org/wiki/Method_chaining" target="_blank">Method Chaining</a></span></span></span></p>
<p><span style="color: #010001"><span style="color: #010001"><span> </span></span></span></p>
]]></content:encoded>
			<wfw:commentRss>http://novacisko.blog.root.cz/2012/01/25/tvorime-vlacky-nejen-v-c/feed/</wfw:commentRss>
		<slash:comments>29</slash:comments>
		</item>
		<item>
		<title>Temná strana linuxu &#8211; spouštění procesů</title>
		<link>http://novacisko.blog.root.cz/2012/01/21/temna-strana-linuxu-spousteni-procesu/</link>
		<comments>http://novacisko.blog.root.cz/2012/01/21/temna-strana-linuxu-spousteni-procesu/#comments</comments>
		<pubDate>Sat, 21 Jan 2012 00:38:32 +0000</pubDate>
		<dc:creator>Ondřej Novák</dc:creator>
				<category><![CDATA[Nezařazené]]></category>

		<guid isPermaLink="false">http://novacisko.blog.root.cz/?p=25</guid>
		<description><![CDATA[Jednoho dlouhého zimního večera jsem se rozhodl, že si napíšu v C++ třídu, která bude multiplatformě řešit spouštění procesů třetích stran z aplikace. Slovem multiplatformní mám na myslí hlavně Linux a Windows verzi. Nakonec se to povedlo, ale bylo to náročné, skoro jsem se začínal obávat, že univerzální rozhraní není možné napsat... tak aby tam [...]]]></description>
			<content:encoded><![CDATA[<p>Jednoho dlouhého zimního večera jsem se rozhodl, že si napíšu v C++ třídu, která bude multiplatformě řešit spouštění procesů třetích stran z aplikace. Slovem multiplatformní mám na myslí hlavně Linux a Windows verzi. Nakonec se to povedlo, ale bylo to náročné, skoro jsem se začínal obávat, že univerzální rozhraní není možné napsat... tak aby tam byly roury, přesměrování, čekání na proces, nebo jeho paralelní běh. Nechci teď řešit kolik knihoven a v kterých balících se tohle řeší. Já si prostě chtěl napsat vlastní v rámci již mé existující knihovny, tak aby byla konformní s existujícím API.</p>
<p>Nebudu teď řešit rozdíly mezi Linuxem a Windows, ale podělím se se zkušenostmi vývoje Linuxové verze a zejména popíšu jaké pasti, pastičky a bezpečnostní nástrahy běžného vývojáře čekají a na co všechno je třeba si dát pozor, co není z počátku vidět.</p>
<h2>1. Něco na zahřátí - příkaz system()</h2>
<p>Pro začátečníky a na jednorázovky úplně postačí, ale musí se počítat s tím, že to funguje jak v DOSu. Process se spustí a ten kdo jej spouštěl musí čekat. Pokud by někdo chtěl řešit souběžný běh dvou procesů (mého a cizího) přes příkaz <strong>fork</strong> a <strong>system</strong>, tak to nedělejte. Vyrobíte totiž dva procesy na místo jednoho. (příkaz system() si sám zavola <strong>fork</strong>).</p>
<h2>2. Fork / exec - Linuxová specialita</h2>
<p>Tohle Windowsák prostě nepochopí. V Linuxu neexistuje příkaz na vytvoření nového procesu. Windowsovská varianta CreateProcess, tady prostě není. Procesy se zde spouští tak jaksi přes hlavu. Jako kdyby sekretářka vytvářela wordowské dokumenty tak, že z jednoho dokumentu vyrobí kopii, kterou následně otevře ve Wordu, obsah smaže a napíše dokument nový.</p>
<p>Přesně takhle to ale funguje v Linuxu. Zajímavý ne? No kdyby to byl jediný problém, tak se to dá celkem vydržet.</p>
<h2>3. Stovky variant execů</h2>
<p><strong>execl, execlp, execle, execv, execvp, execvpe</strong> - jen si vyberte, který vám vyhovuje, podle toho, jak se zadávají argumenty, nebo prostředí. No klid, nic nevybírejte, nebudete to potřebovat, jediný příkaz, který má smysl používat, je <strong>execve</strong>, protože ten jediný zvládne spustit i bashový skripty, tak jak jsme zvyklí ze shellu. A já jako programátor nechci řešit, zda na druhé straně je binárka, shell-skript, python, nebo nedejbože péhápko.</p>
<h2>4. Když se exec nezdaří</h2>
<p>Protože budeme spouštět proces přes <strong>fork </strong>/ <strong>exec</strong>, je třeba zajistit ošetření situace, kdy se exec nezdaří. Nemusí se zdařit, třeba proto, že v příkazové řádce je uvedena chybně cesta na binárku nebo, že binárka není přeložena pro aktuální platformu nebo, že nemáme práva ji spouštět. Zvlášť pokud pouštíme proces, který poběží v pozadí a naše aplikace s ním bude spojena rourou.</p>
<p>Proces spouštíme přes fork. V tom případě se rodič ještě nedozví o tom, zda se v dítěti nezdařil execve. Pokud rodič chce pokračovat až po tom, co si ověřil, že dítě úspěšně spustilo proces, bude muset použít dosti komplikovaný způsob synchronizace ... a to mě zrovna teď nenapdá, jak by se to udělalo. Jakým způsobem předám výsledek volání funkce execve v tomto kódu?</p>
<pre>int i = fork();
if (i &lt; 0) chyba();
if (i == 0) {
    execve(....);
    _exit(errno);
}</pre>
<p>Pokud se execve zdaří, dítě už nikdy nedostane šanci informovat rodiče o úspěchu. Proto taky nemusím testovat návratovou hodnotu. Ta bude vždycky -1. Dítě ukončuji předáním errno, což zrovna není nic moc, protože při čtení statusu v rodiči nepoznám, zda návratová hodnota pochází z procesu nebo z nepovedeného execve. K řešení tohoto problému se dostanu v dalších bodech.</p>
<p><strong>Poznámka:</strong> Samozřejmě že na to mohu kašlat, a rodič se může chovat jako by se to zdařilo. On mu totiž hned přiletí SIGCHLD, takže žádná křeč. Ale napište knihovní funkci, která po svém návratu může volajícímu s jistotou oznámit <em>"je to okaj, požadovaný proces byl spuštěn a nyní běží"</em>... či případně vyhodit výjimku která obsahuje kód chyby nastavené při neúspěšném <strong>execve</strong>.</p>
<h2>5. Paměťová náročnost forku</h2>
<p>Ve firmě, ve které pracuji a která je plná linuxáků, jsem narazil při vývoji linuxového backendu na problém, kdy mi fork() vyhazoval chybu, že je nedostatek paměti. Přičemž jediný, co jsem měl v plánu provést je spuštění <strong>wgetu </strong>v rouře s <strong>unzipem </strong>a to celé přivést rourou do aplikace (abych nemusel importovat HTTP klienta a zlib knihovnu). Šel jsem tedy za odborníky z vedlejšího oddělení, aby mi poradili, kde může být zádrhel.</p>
<p>No problém nebyl ani ve wgetu, ani v unzipu, ale v tom, že tou dobou moje aplikace zabírala v paměti slušných 1.5GB na virtuálu, který měl limit 2GB a se systémem tam zbývalo něco přes 300MB volného místa. Tam by se <strong>wget </strong>a <strong>unzip </strong>vešel, ale rozhodně se tam nevejde dalších 1.5GB mé aplikace. Ale co to plácám, vždyť <strong>fork </strong>přece neprovádí duplikaci, pouze nasdílí paměťové stránky přes Copy-On-Write! Taky vám tohle neustálě někdo vštěpuje do hlavy? Ona to pravda je, ale do hry tady vstupuje další linuxová specialita a to <strong>overcommit </strong>(o tom někdy jindy). Sice <strong>fork </strong>neprovede plnou kopii, ale nový proces by mohl časem těch 1.5GB požadovat a to ta mašina nemá k dispozici. Řeší se to tedy tak, že se na rezervaci prostoru pro nový proces pohlíží jako na obyčejný malloc, na který se vztahují pravidla overcommitu. Protože stroj měl volných 300MB a <strong>fork </strong>potřeboval vyhradit 1.5GB, musel by být overcommit nastaven aspoň na 500% a to nebyl.</p>
<p>Tohle všechno jsem se dozvěděl od našich linuxáků s tím, že pokud to chci jinak, tak mám prostě smůlu, Linux to jinak neumí. Ať pošteluju overcommit, nebo si zvětším virtual (pro unzip a wget?). No už vidím, jak naše konzervativní adminy přesvědčuju, že kvůli mé knihovně mají o hodně zvýšit overcommit na produkčních strojích.</p>
<p><strong>Závěr: </strong>Pokud vaše aplikace potřebuje spouštět externí procesy, nesmí zabrat víc paměti, než je zhruba stejné množství volné paměti. Může to být trochu víc, ale pak záleží na tom, jak je nastaven overcommit. Psát univerzální knihovnu tak, že se  předpokládá nějaké nestandardní nastavení systému, je ale prasárna.</p>
<h2>6. Záchrana v podobě vfork</h2>
<p>No v manpages jsem objevil <strong>vfork(2)</strong>. Rychlým pohledem na google a do diskuzí člověk získá dojem, že lidi si nejsou úplně jisti, jak tahle funkce pracuje. Já mám tuto zkušenost:</p>
<p>Jedná se o <strong>virtual fork</strong>. Tváří se to jako fork ale není to fork. Je to <em><strong>fakefork</strong></em>.<strong> </strong>Pokud někdo hledá funkci <strong>CreateProcess</strong>, tak ji právě našel. Funkce <strong>vfork</strong> vytvoří nové dítě ale v paměťovém prostoru rodiče. Rodič je zablokován .... (a teď kontrolní otázka, co když má rodič vlákna, he?) ... a místo něho běží dítě, a to do doby, než se dítě ukončí, nebo zavolá <strong>execve. </strong>Co je tady zajímavé, totiž, že dítě má plný přístup do paměťového prostoru rodiče, a to i pro zápis!</p>
<p><strong>vfork </strong>řeší dvě bolístky</p>
<ol>
<li>Paměťovou náročnost forku ... <strong>vfork </strong>nemusí nic slibovat, protože se nerezevují stránky</li>
<li>Předávání návratové hodnoty z <strong>execve</strong></li>
</ol>
<p>... a navíc, <strong>vfork </strong>je pravděpodobně rychlejší, protože nedochází ke zbytečnému duplikování stránkovacích tabulek, které jsou následně zahozeny.</p>
<p>Podívejme se, jak se bude řešit návratová hodnota z vforku:</p>
<pre>static int execerr;

execerr = 0;
int i = vfork();
if (i &lt; 0) chyba();
if (i == 0) {
    execve(....);
    execerr = errno;
    _exit(0);
}
if (execerr) throw UnableToExecuteProcessException(execerr);</pre>
<p>Zjednodušeně řešeno, dítě vzniklé po vforku()  má přístup k proměnné execerr, která je alokovaná staticky (pokud tam má člověk vlákna, je lepší, když je alokovaná na haldě). Proměnnou není možné dát do zásobníku, protože vfork provede duplikaci zásobníku a změna proměnné by se nepromítla do zásobníku rodiče. Pokud execve selže, nastaví se proměnné execerr a dítě skončí. Pokud neselže, zůstane tam hodnota 0.</p>
<p>Důležitým detailem je také to, že rodič je počas vykonávání dítěte zastaven. Proto není nutné řešit synchronizaci, v okamžiku, kdy je rodič spuštěn, se už může dozvědět výsledek. Pokud je v <strong>execerr </strong>nula, pak bylo spuštění procesu úspěšné. Pokud není  nula, pak bylo neúspěšné.</p>
<p>Kdybych tohle přirovna k sekretářce, tak zatímco použití <strong>fork</strong>u znamená, že sekretářka zkopíruje dokument a ten nový otevře ve Wordu a smaže ho aby napsala nový, u <strong>vfork</strong>u to udělá tak, že ve Wordu otevře starý (dokument se interně zamkne) a následně sekretářka vyvolá příkaz <strong>Save As </strong>s novým názvem a až potom dokument smaže a začne psát nový. Mezi otevřením a přeuložením je krátký časový úsek, kdy s původním dokumentem nelze pracovat.</p>
<p>Aby to nebylo tak jednoduché, <strong>vfork </strong>není standardní. Navíc pořád mě dost děsí výstraha v manuálu, kde prý zápis do proměnné vytvořené v rodiči není definován. Tady je prostor pro budouci změny, aby třeba časem to dítě nevznikalo někde v hyperprostoru, bez možnosti komunikovat s rodičem.</p>
<h2>7. Roury a souborové popisovače - bezpečnostní jáma.</h2>
<p>Co je to proboha popisovač? No přece deskriptor. Miluju český jazyk <img src='http://novacisko.blog.root.cz/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
<p>Krásně se demonstruje na dvojici fork/exec vytváření rour. V rodičovi vytvořím rouru, forknu to, v dítěti jednu stranu zavřu a druhou zduplikuju na některý z popisovačů 0 až 2, podle potřeby. V rodičovi pak zavřu opačný konec a ten co mi zbude používám pro I/O s dítětem. Funkce <strong>execve</strong> má tu vlastnost, že zachová otevřené všechny souborové popisovače, čímž umožňuje realizovat právě onu nádherně vyvedenou pupeční šňůru mezi rodičem a dítětem.</p>
<p><em><span style="text-decoration: line-through">(A to není všechno, fork popisovače duplikuje, takže si každý proces spravuje vlastní sadu popisovačů nezávisle na sobě. To vůbec nehledejte u Windows, kde se popisovače ... HANDLE... sdílí a to takovým způsobem, že když rodič popisovač zavře, automaticky se zavře v dítěti a další operace s ním vedou na chybu "neplatný popisovač! Druhým rozdílem je, že když v Linuxu skončí dítě, zavřou se všechny jeho popisovače a roury spadnou do stavu SIGPIPE. Ve Windows zůstanou handly otevřené a čteni z rour bude viset v deadlocku!)</span></em></p>
<p>Edit: Tvrzení ve výše uvedeném odstavci se nepodařilo ověřit.</p>
<p>Až sem je to růžová zahrada, Linux rulez! Jenže ouha. V aplikaci máte otevřeno několik souborů, pár rour, a nějaká ta síťová spojení, třeba takový ... ssh démon, který vytvoří dítě, spojí se s klientem a spustí <strong>bash</strong>.<strong> </strong>Jenže se nezduplikují jen ty popisovače, které chceme, ale všechny. Pokud nevíme nic o procesu, který naše aplikace spouští, koledujeme si o malér. Ten proces totiž může být nějaký malware, nebo jiná forma škodlivého zvířátka, které po svém spuštění udělá scan otevřených popisovačů. To není tak těžký, všechny mají IDčka někde kolem první stovky. Teoreticky se může dozvědět, jaké zdroje tyto popisovače představují. Mohou například manipulovat s ukazatelem pozice v souborech, které rodič čte a donutit rodiče udělat něco nepředvídatelného, nebo získat přístup k datům, ke kterým normálně nemusí mít práva.</p>
<p>A zatím co v růžovce může maximálně řádit doktor Mázl, tady může řádit někdo mnohem horší. Nejde o bezpečnostní díru, ale přímo o jámu. Co s tím?</p>
<p>Taky mě napadlo, že bych před spuštěním procesu provedl uzavření všech popisovačů kromě 0,1 a 2. Hmm, ale jak se dozvím, kolik jich je a jaké mají IDčka. Na jednom foru jsem se dozvěděl, že nějaká verze <strong>ssh</strong> uzavírá všechny popisovače od 3 do tuším 64 to spekulativně. Co kdyby tam něco bylo otevřeného. Taky řešení... Některé unixy oplývají funkci <a href="http://www.unix.com/man-page/All/3c/closefrom/"><strong>closefrom(3)</strong></a>. Pokud ale chcete dát uživateli knihovny možnost vytvářet i další roury nad rámec prvních tří, pak neshledáte tuto funkci užitečnou.</p>
<p>Je to celkem nedávno, co v Linuxovém jádře zavedli příznak FD_CLOEXEC . Pokud tento příznak nastavíme přes fcntl, pak nám <strong>execve </strong>takový popisovač zavře. Takže je dobré si na to dát pozor a po vzniku jakéhokoliv popisovače (po otevření souboru, socketu, roury,...) ihned nastavit FD_CLOEXEC přes fcntl...</p>
<p>Dobrý nářez co? Zvedněte ruku, kdo tohle někdy řešil? Jo a to není všechno, pokud máte vlákna a zrovna tu smůlu, že mezi otevřením popisovače a nastavením příkazu jiné vlákno stihne udělat <strong>execve</strong>...? Postupně se objevují příkazy, které to umí v jednom kroku... třeba <strong>open(2) </strong>má příznak O_CLOEXEC. Nebo <strong>pipe2(2)</strong>, nebo <strong>dup3(2).</strong> Mimochodem, dup nezduplikuje nastavení příznaku FD_CLOEXEC, takže výsledkem je opět popisovač, který lze ihned nasdílet do cizího procesu. A funkce dup3 nebyla k dispozici ještě na <strong>lennym</strong>, jak je to na <strong>sqeeze</strong> jsem nezkoušel.</p>
<h2>8. Čekání na ukončení procesu</h2>
<pre>bool Process::join(const Timeout &amp;tm);</pre>
<p>Tak vypadá prototyp funkce která počká na ukončení dítěte. Lze nastavit i timeout. Timeout? Nezbláznil jsem se? Přece funkce <strong>waitpid</strong> žádný timeout nemá... ?</p>
<p><em>Mimochodem, Windows rulez: WaitForSingleObject(hProcess,timeout);</em></p>
<p>No nic, vypadá to nějak takhle:</p>
<pre>natural tmms = 1;
int status;
pid_t e = waitpid(ctx.processPid,&amp;status, WNOHANG);
while (e == 0) {
   if (tm.expired()) return false;
   Thread::sleep(tmms); //v milisekundách
   if (tmms &lt; 500) tmms++;
   e = waitpid(ctx.processPid,&amp;status, WNOHANG);
}</pre>
<p>Nebudu ani vysvětlovat co je ta omáčka kolem, princip je asi jasný. Jasně, že by asi šlo použít signál, ale tam je obtíž s tím, že vám SIGCHLD přiletí při každém ukončeném dítěti (nikoliv jen při jednom konkrétním) a v aplikaci složené z vláken bude ještě problém zjistit do kterého vlákna ten signál přiletí a kterou zrovna činnost to přeruší. Takový handlíř tohoto signálu bude asi superglobální objekt a bude muset znát všechny existující instance třídy Process, aby mohl příslušnou třídu informovat, že její proces skončil. Pěkně se to komplikuje.</p>
<h2>9. Čekání na proces spuštěný někým jiným?</h2>
<p>... nechte si zajít chuť. To v Linuxu nejde. Jak vypadají init skripty?</p>
<ol>
<li>kill process</li>
<li>běží proces?</li>
<li>ano, počkej sekundu, nakresli tečku a goto 2.</li>
<li>ne, ohlaš úspěch</li>
</ol>
<p>Ve Windows? WaitForSingleObject(hProcess, timeout)</p>
<h2>Už budu končit</h2>
<p>Spouštění a základní správa procesů v linuxu není zrovna jednoduchá záležitost. Je to prostě krásná ukázka toho, jak jednoduchou věc dělat složitě. Ano, jistě, stojí za tím nějaký historický vývoj, snaha udržet kompatibilitu s posixem a s původními unixy. Ale to se na tom podepisuje na každém kroku. Je otázkou, zda je nutné neustále držet historické API a ohýbat ho na všechny strany podle posledních trendů, nebo vytvářet nová API a ta stará pouze nějak emulovat.</p>
]]></content:encoded>
			<wfw:commentRss>http://novacisko.blog.root.cz/2012/01/21/temna-strana-linuxu-spousteni-procesu/feed/</wfw:commentRss>
		<slash:comments>36</slash:comments>
		</item>
		<item>
		<title>Alokátory a operator new v C++</title>
		<link>http://novacisko.blog.root.cz/2012/01/04/alokatory-a-operator-new-v-c/</link>
		<comments>http://novacisko.blog.root.cz/2012/01/04/alokatory-a-operator-new-v-c/#comments</comments>
		<pubDate>Wed, 04 Jan 2012 18:36:37 +0000</pubDate>
		<dc:creator>Ondřej Novák</dc:creator>
				<category><![CDATA[Nezařazené]]></category>

		<guid isPermaLink="false">http://novacisko.blog.root.cz/?p=5</guid>
		<description><![CDATA[Každý programátor v C++ ví, že pomocí operátoru new můžeme zkonstruovat objekt na globální haldě. Někdy je však potřeba provést alokaci nového objektu jinde, než na globální haldě. Slovíčko „někdy“ zde ale není úplně na místě, v praxi jsem si ověřil, že to přináší výhody, takže spíše by se sem hodilo slovíčko „často“.
To, kde se [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Každý programátor v C++ ví, že pomocí operátoru </strong><strong>new můžeme zkonstruovat objekt na globální haldě. Někdy je však potřeba provést alokaci nového objektu jinde, než na globální haldě. Slovíčko </strong><strong>„</strong><strong>někdy</strong><strong>“</strong><strong> zde ale není úplně na místě, v praxi jsem si ověřil, že to přináší výhody, takže spíše by se sem hodilo slovíčko „často“.</strong></p>
<p>To, kde se nakonec vytvoří prostor pro nový objekt je určeno speciálními objekty, které nazývám alokátory (tedy je to něco podobného jako std::allocator, ale mnohem obecnější, abstraktnější rovině). V článku představím rozhraní takového alokátoru. Nejprve bych ale zopakoval (pro některé) co všechno lze s operátorem <strong>new </strong>dělat.</p>
<p>Tento operátor provádí víceméně dvě operace.</p>
<ol>
<li> Alokuje blok paměti 	tak velký, aby se do něj nově konstruovaný objekt vešel</li>
<li> Provádí konstrukci 	objektu voláním konstruktoru.</li>
</ol>
<p lang="cs-CZ">Už pomocí standardních knihoven lze jednu nebo druhou funkci potlačit. Můžeme požadovat pouze konstrukci objektu v již alokovaném bloku a můžeme požadovat pouze alokaci bloku bez konstrukce objektu.</p>
<ol>
<li><strong>operator new(velikost)</strong> – 	kde velikost je požadovaná 	velikost v bytech</li>
<li><strong>new(adresa) Foo –</strong> konstruuje objekt … instanci třídy Foo na zadané adrese.</li>
</ol>
<p>Můj soukromý průzkum ukazuje, že první vlastnost operátoru <strong>new</strong> spoustu mých profesních kolegů nezná. Často by v této situaci použili <strong>malloc()</strong>. <strong>Upozorňuji, že to není totéž</strong>, přestože ve výchozím stavu toto volání nakonec ten malloc() zavolá.</p>
<p>Druhý bod představuje variantu použití <strong>new</strong> zvanou <strong>placement new</strong>. Zde se použije adresa již kdesi v paměti vyhrazeného bloku, kam bude objekt zkonstruován. Je to vlastně jediný legální způsob, jak nad nějakým kusem paměti zavolat konstruktor a tím z toho kusu paměti vytvořit něco smysluplného. K této variantě neexistuje <strong>operator delete, </strong>a zkonstruovaný objekt musí být zlikvidovat ručním zavoláním destruktoru.</p>
<p>Pro použití s alokátory využijeme <strong>placement new</strong> který obecně připouští použití libovolného množství dodatečných argumentů libovolného typu. Samozřejmě každá varianta musí mít vlastní prototyp. Operátor můžeme deklarovat jako globální nebo členskou funkci.</p>
<h3>Operator new a třídy</h3>
<p lang="cs-CZ">Pracovat s globálním new je trochu obtíž, protože se může dostat do konfliktu. Vlastní verzi přetíženého new nelze umístit do namespace, takže bude vždy globálním a představovat riziko konfliktu s jinou definicí v jiné knihovně. Dá se to obejít, ale nechci se tím teď zabývat.</p>
<p>Mnohem raději používám přetížení operátoru <strong>new </strong>u tříd. Co to znamená? Pokud se někde konstruuje třída, která má deklarovaný členský <strong>operator new</strong>, pak se k alokaci volá právě tento operator namísto globálního. A pozor, platí to i pro všechny podtřídy(!) Žádné konflikty nehrozí a pro knihovní třídy mám možnost nabídnout rozšířené alokace bez velké námahy. Stačí, když všechny třídy podědí nějakou společnou, která standardní operátor <strong>ne</strong><strong>w</strong> (a samozřejmě i patřičné <strong>delete</strong>) přetíží.</p>
<p lang="cs-CZ">Takže malý příklad, nejprve si ale vytvoříme jednoduchý alokátor.</p>
<pre> <span style="color: #0000ff">class</span> <span style="color: #010001">IRuntimeAlloc</span> {
 <span style="color: #0000ff">public</span>:
     <span style="color: #0000ff">virtual</span> <span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #010001">alloc</span><span style="color: #000000">(std::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">objSize</span><span style="color: #000000">)  = 0;</span>
<span style="color: #000000">     </span><span style="color: #0000ff">virtual</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> </span><span style="color: #010001">dealloc</span><span style="color: #000000">(</span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #010001">ptr</span><span style="color: #000000">, std::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">objSize</span><span style="color: #000000">)  = 0;</span>
<span style="color: #000000"> };</span></pre>
<p>Nehledejte tam nic světoborného. Název <strong>IRuntimeAlloc</strong> je možná trochu nesmyslný, ale vznikl historicky s potřebou mít alokátor, který nezná velikost alokovaného objektu během překladu, ale až za běhu (runtime). Druhým důvodem je použití pozdní vazby, také v runtime, protože většina mých alokátorů jsou šablony bez pozdních vazeb. Zvolil jsem tenhle příklad proto, že je nejjednoduší. Funkce <strong>alloc</strong> a <strong>dealloc</strong> kopírují prototypy funkcí <strong>new</strong> a <strong>delete</strong>, takže asi nebude těžké volat je přímo z jejich implementace. Až tedy na jeden detail, a to přítomnost velikost objektu u <strong>dealloc</strong>. Mám vyzkoušeno, že je dobré, když tuto informaci alokátoru dodáme z vnějšku, protože alokátor tak ušetří spoustu místa ukládáním tohoto čísla. Věřte nebo ne, existuje způsob, jak tuto velikost získat bez nutnosti si jí ukládat poblíž alokovaného bloku. Dodá nám jí překladač.</p>
<pre><span style="font-size: x-small"> </span><span style="color: #0000ff">class</span><span style="color: #000000"> </span><span style="color: #010001">DynObject</span><span style="color: #000000">{</span><span style="color: #000000">
</span><span style="color: #0000ff">p</span><span style="color: #0000ff">ublic</span><span style="color: #000000">:
   </span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000">(</span><span style="color: #010001">std::size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #010001">);</span><span style="color: #000000">
   </span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000">(</span><span style="color: #010001">std::size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">, </span><span style="color: #010001">IRuntimeAlloc</span><span style="color: #000000"> &amp;</span><span style="color: #010001">alloc</span><span style="color: #000000">)
   </span><span style="color: #0000ff">void</span><span style="color: #000000"> </span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">delete</span><span style="color: #000000">(</span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #010001">ptr</span><span style="color: #000000">, std::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">);</span><span style="color: #000000">
 </span><span style="color: #000000">};</span></pre>
<p><span style="color: #000000">Teď by tedy mělo postačit všech </span><span style="color: #000000">knihovn</span><span style="color: #000000">í třídy nechat dědit třídu DynObject a mohu vesele používat jak standardni </span><span style="color: #000000"><strong>new Foo</strong></span><span style="color: #000000">; </span><span style="color: #000000">tak </span><span style="color: #000000"><strong>new(alloc) Foo;</strong></span><span style="color: #000000"> kde </span><span style="color: #000000"><strong>alloc</strong></span><span style="color: #000000"> je </span><span style="color: #000000">reference na objekt implementující rozhraní IRuntimeAlloc.</span></p>
<p><span style="color: #000000">Povšimněte si operátoru </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000">. Opět můj soukromý průzkum zjistil, že není mnoho kolegů, kteří by si vzpomněli, že existuje tato varianta. Většina si vzpomněla jen na </span><span style="color: #000000"><strong>delete(void *</strong></span><span style="color: #000000"><strong>ptr</strong></span><span style="color: #000000"><strong>)</strong></span><span style="color: #000000">. Výše uvedenou variantu lze použít pouze u tříd; nebude fungovat u globálního delete. Zato bude správně fungovat i u všech potomků. A to není vše, pokud tuto třídu podědíme v jiné vrstvě hierarchie, než základní třídou (base), bude operator správně volán i v případě, že objekt bude destruován přes ukazatel na základní třídu ... tedy za předpokladu, že </span><span style="color: #000000"><strong>třída má virtuální destruktor!!!! </strong></span><span style="color: #000000">(což je u drtivé většině tříd povinnost)</span></p>
<p><span style="color: #000000">Výhodou varianty </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000"> s uvedenou velikosti je, že si nemusíme velikost objektu nikde pamatovat. Aby to nebylo tak skvělé, máme tu i jednu nevýhodu. Všimněte si, že operátor </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000"> neví nic o použitém alokátoru a přesto je nutné, aby deallokace proběhla přes alokátor, a nikoliv přes standardní </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000">. I když tedy ušetříme místo pro uložení velikosti objektu, musíme navíc přidat odkaz na alokátor, tak, aby </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000"> vědělo, kdo je za  deallokaci zodpovědný. V paměti se pak alokuje o něco větší blok (typicky o velikost ukazatele)<br />
</span></p>
<table border="1" cellspacing="0" cellpadding="4" width="376">
<col width="293"></col>
<col width="65"></col>
<tbody>
<tr valign="TOP">
<td width="293">Prostor pro objekt</td>
<td style="text-align: center" width="65">allocptr</td>
</tr>
</tbody>
</table>
<p lang="cs-CZ"><span style="color: #000000">Pokud některého čtenáře napadlo, že by bylo možné použít něco jako „placement delete“ a allokátor mu dodat jako parametr, tak na to honem rychle zapomeňte. Nic takového nejde.</span></p>
<h3>Výjimky … a bug v normě.</h3>
<p><span style="color: #000000">Nic člověka nepotrápí víc, než výjimky a obzvlášť u přetížených </span><span style="color: #000000"><strong>new </strong></span><span style="color: #000000">a </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000">. Pokud během konstrukce objektu dojde k výjimce, je třeba přidělené místo opět uvolnit. A tady C</span><span style="color: #000000">++ vol</span><span style="color: #000000">í jinou cestu, nedojde k volání standardního </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000">, ale k jakési obdobě „placement delete“. V normě C</span><span style="color: #000000">++ je uvedeno, </span><span style="color: #000000">že prototyp takové funkce je totožný s </span><span style="color: #000000"><strong>new</strong></span><span style="color: #000000"> až tedy na to, že jde o </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000"> a prvním parametrem je ukazatel na alokovaný blok. Zde je několik příkladů:</span></p>
<table border="1" cellspacing="0" cellpadding="4" width="100%">
<col width="128*"></col>
<col width="128*"></col>
<tbody>
<tr valign="TOP">
<td width="50%" height="14"><strong>Funkce pro alokaci</strong></td>
<td width="50%"><strong>Funkce pro dealokaci při 			výjimce</strong></td>
</tr>
<tr valign="TOP">
<td width="50%" height="15">operator new(size_t sz)</td>
<td width="50%">operator delete(void 			*ptr)<br />
nebo 			operator delete(void *ptr, 			std::size_t sz)</td>
</tr>
<tr valign="TOP">
<td width="50%">operator new(size_t sz, void 			*ptr2)</td>
<td width="50%">operator delete(void 			*ptr, void *ptr2)</td>
</tr>
<tr valign="TOP">
<td width="50%">operator 			new(size_t sz, IRuntimeAllocator &amp;a)</td>
<td width="50%">operator delete(void 			*ptr, IRuntimeAllocator &amp;a)</td>
</tr>
</tbody>
</table>
<p lang="cs-CZ"><span style="color: #000000">Pokud tam takové funkce zapomeneme uvést, dojde při výjimce k leaku (k neuvolnění alokované paměti). Což není zrovna chytré. Takže je tam přídáme.</span></p>
<pre><span style="font-size: x-small"> </span><span style="color: #0000ff">class</span><span style="color: #000000"> </span><span style="color: #010001">DynObject</span><span style="color: #000000"> {
 </span><span style="color: #0000ff">public</span><span style="color: #000000">:
    </span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000">(std::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #010001">);</span><span style="color: #000000">
    </span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000">(</span><span style="color: #010001">std::size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">, </span><span style="color: #010001">IRuntimeAlloc</span><span style="color: #000000"> &amp;</span><span style="color: #010001">alloc</span><span style="color: #000000">); </span>
    <span style="color: #0000ff">void</span><span style="color: #000000"> </span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">delete</span><span style="color: #000000">(</span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #010001">ptr</span><span style="color: #000000">, std::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">);</span><span style="color: #000000">
    </span><span style="color: #0000ff">void</span><span style="color: #000000"> </span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">delete</span><span style="color: #000000">(</span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #010001">ptr</span><span style="color: #000000">, </span><span style="color: #010001">IRuntimeAlloc</span><span style="color: #000000"> &amp;</span><span style="color: #010001">alloc</span><span style="color: #000000">); </span><span style="color: #000000">
 </span><span style="color: #000000">};</span></pre>
<p><span style="color: #000000">Vidíte tam nějakou zradu? Já ano, a dost zásadní. Jak jsem napsal výše, náš alokátor vyžaduje velikost alokovaného bloku, aby jej mohl dealokovat. Je fajn, že nám to překladač spočítá, pokud jde o standardní </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000">, ale v případě dealokace během výjimky </span><span style="color: #000000"><strong>nemáme šanci velikost zjistit !!! </strong>To je dost zásadní problém</span><span style="color: #000000">. Z tohoto pohledu to vypadá, že jde o pořádný průšvih v normě C</span><span style="color: #000000">++, </span><span style="color: #000000">velikost uvedená u </span><span style="color: #000000"><strong>delete </strong>je </span><span style="color: #000000">vlastně  k ničemu, protože si ji beztak musím uložit někde poblíž alokovaného bloku. Hledal jsem tedy způsob, jak to obejit a nakonec se mi to podařilo obejít využím pomocného objektu, který bude existovat pouze během alokace paměti a konstrukce nového objektu. Tento objekt vznikne na zásobníku a aby jej programátor nemusel znát, využijeme implicitní volání konstruktoru. Nějak takto:</span></p>
<pre><span style="color: #000000"> </span><span style="color: #0000ff">class</span><span style="color: #000000"> </span><span style="color: #010001">DynObjectAllocHelper</span><span style="color: #000000"> {</span><span style="color: #000000">
 </span><span style="color: #0000ff">public</span><span style="color: #000000">:
    </span><span style="color: #010001">DynObjectAllocHelper</span><span style="color: #000000"> (</span><span style="color: #010001">IRuntimeAlloc</span><span style="color: #000000"> &amp;</span><span style="color: #010001">alloc</span><span style="color: #000000">):</span><span style="color: #010001">alloc</span><span style="color: #000000">(</span><span style="color: #010001">alloc</span><span style="color: #000000">),</span><span style="color: #010001">sz</span><span style="color: #000000">(0) {}
    </span><span style="color: #010001">IRuntimeAlloc</span><span style="color: #000000"> &amp; </span><span style="color: #010001">ref</span><span style="color: #000000">() </span><span style="color: #0000ff">const</span><span style="color: #000000"> {</span><span style="color: #0000ff">return</span><span style="color: #000000"> </span><span style="color: #010001">alloc</span><span style="color: #000000">;}
    </span><span style="color: #0000ff">void</span><span style="color: #000000"> </span><span style="color: #010001">storeSize</span><span style="color: #000000">(</span><span style="color: #010001">std</span><span style="color: #000000">::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">) </span><span style="color: #0000ff">const</span><span style="color: #000000"> {</span><span style="color: #0000ff">this</span><span style="color: #000000">-&gt;</span><span style="color: #010001">sz</span><span style="color: #000000"> = </span><span style="color: #010001">sz</span><span style="color: #000000">;}
    </span><span style="color: #010001">std</span><span style="color: #000000">::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">getSize</span><span style="color: #000000">() </span><span style="color: #0000ff">const</span><span style="color: #000000"> {</span><span style="color: #0000ff">return</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">;}
 </span><span style="color: #0000ff">protected</span><span style="color: #000000">:
    </span><span style="color: #010001">IRuntimeAlloc</span><span style="color: #000000"> &amp; </span><span style="color: #010001">alloc</span><span style="color: #000000">;
    </span><span style="color: #0000ff">mutable</span><span style="color: #000000"> </span><span style="color: #010001">std</span><span style="color: #000000">::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">;</span>
 };</pre>
<p lang="en-US">
<p lang="cs-CZ"><span style="color: #000000">Prototyp třídy DynObject náležitě upravíme:</span></p>
<pre><span style="font-size: x-small"> </span><span style="color: #0000ff">class</span><span style="color: #000000"> </span><span style="color: #010001">DynObject</span><span style="color: #000000"> {
 </span><span style="color: #0000ff">public</span><span style="color: #000000">:
    </span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000">(</span><span style="color: #010001">std::size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #010001">);</span><span style="color: #000000">
    </span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000">(</span><span style="color: #010001">std::size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">,</span><span style="color: #000000"><span style="color: #0000ff">const </span>DynObjectAllocHelper </span><span style="color: #000000">&amp;</span><span style="color: #010001">alloc</span><span style="color: #000000">);
   </span> <span style="color: #0000ff">void</span><span style="color: #000000"> </span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">delete</span><span style="color: #000000">(</span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #010001">ptr</span><span style="color: #000000">, std::</span><span style="color: #010001">size_t</span><span style="color: #000000"> </span><span style="color: #010001">sz</span><span style="color: #000000">);
    </span><span style="color: #0000ff">void</span><span style="color: #000000"> </span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">delete</span><span style="color: #000000">(</span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #010001">ptr</span><span style="color: #010001">);</span><span style="color: #000000">
    </span><span style="color: #0000ff">void</span><span style="color: #000000"> </span><span style="color: #0000ff">operator</span><span style="color: #000000"> </span><span style="color: #0000ff">delete</span><span style="color: #000000">(</span><span style="color: #0000ff">void</span><span style="color: #000000"> *</span><span style="color: #010001">ptr</span><span style="color: #000000">,</span><span style="color: #000000"><span style="color: #0000ff">const </span>DynObjectAllocHelper </span><span style="color: #000000">&amp;</span><span style="color: #010001">alloc</span><span style="color: #000000">); </span></pre>
<pre><span style="color: #000000"> </span><span style="color: #000000">};</span></pre>
<p lang="en-US">
<p lang="cs-CZ"><span style="color: #000000">Použití const reference způsobí, že instance třídy D</span><span style="color: #000000">ynObjectAllocHelper se bude konstruovat v z</span><span style="color: #000000">ásobníku volajícího a teprve pak se použije pro volání </span><span style="color: #000000"><strong>new</strong></span><span style="color: #000000"> případně </span><span style="color: #000000"><strong>delete</strong></span><span style="color: #000000"> při výjimce. Protože konstruktor třídy není deklarován jako </span><span style="color: #000000"><strong>explicit</strong></span><span style="color: #000000"> a má jeden parametr, není jej třeba uvádět, stačí do new dodat referenci na allokátor a překladač sám zajistí implicitní konverzi a konstrukci pomocného objektu. Metoda </span><span style="color: #000000"><strong>storeSize</strong></span><span style="color: #000000"> ukládá do pomocného objektu velikost předanou funkcí </span><span style="color: #000000"><strong>new</strong></span><span style="color: #000000"> tak, aby si jí funkce </span><span style="color: #000000"><strong>delete</strong></span> mohla vyzvednou, pokud dojde k výjimce (má k dispozici referenci na objekt DynObjectAllocHelper předanou parametrem). Protože je objekt s kvalifikátorem const, musíme proměnnou pro velikost deklarovat jako mutable. Čistotu kódu v tom nehledejte, jedná se vlastně o hack. Pomocný objekt je destruován automaticky, jakmile je konstrukce alokovaného objektu dokončena, nebo pokud je dokončen úklid při výjimce.</p>
<h3>Využití</h3>
<p>Jak už bylo na začátku naznačeno, smyslem bylo zavést možnost alokovat objekty pomocí jiných alokátorů. Uvedu pár alokátorů z mé knihovny pro inspiraci</p>
<p><strong>AllocInBuffer</strong> – alokátor, kterému předávám adresu a velikost paměti, ve které si může alokovat. Jedná se o nejjednodušší alokátor, který lze použít jen na alokaci jednoho objektu. Je to obdoba placement new s tím rozdílem, že podporuje i delete, takže na alokovaný objekt lze použít chytrý ukazatel.</p>
<pre><span style="color: #0000ff">char </span>buffer[256];
AllocInBuffer bufalloc(buffer,<span style="color: #0000ff">sizeof</span>(buffer));
MyObject *x = <span style="color: #0000ff">new</span>(bufalloc) MyObject;
//...
<span style="color: #0000ff">delete </span>x;</pre>
<p><strong>TempAlloc</strong> – alokátor optimalizovaný pro krátkodobé alokace, které uspokojuje se složitosti O(1). Nevýhodou je, že nevhodné použití vede na výrazné plýtvání paměti.</p>
<p>Varianta, kdy o umístění rozhoduje volaný</p>
<pre>MyObject *foo() {
   //funkce foo() předává výsledek výpočtu a
   //k alokaci použije alokátor pro krátkodobé alokace
   <span style="color: #0000ff">return </span><span style="color: #0000ff">new</span>(TempAlloc::getInstance()) MyObject;
}</pre>
<pre><span style="color: #0000ff">void </span>bar() {
   MyObject *x = foo();
   //... zpracuji x
   // funkce bar() zpracuje výsledek a paměť uvolní
   // nemusí tušit, že objekt je alokován v dočasné paměti
   <span style="color: #0000ff">delete </span>x;
}</pre>
<p>Varianta, kdy o umístění rozhoduje volající</p>
<pre>MyObject *foo(IRuntimeAlloc &amp;alloc) {
   //funkce foo() předává výsledek výpočtu a
   //k alokaci použije dodaný alokátor
   <span style="color: #0000ff">return </span><span style="color: #0000ff">new</span>(alloc) MyObject;
}</pre>
<pre><span style="color: #0000ff">void </span>bar() {
   //volající ví, že vytvořený objekt bude mít velmi krátkou životnost
   MyObject *x = foo(TempAlloc::getInstance());
   //... zpracuji x
   <span style="color: #0000ff">delete </span>x;
}</pre>
<p><strong>ClusterAlloc</strong> – alokátor objektů stejné velikosti, snižuje fragmentaci paměti a dosahuje konstantní složitosti a dobré lokality dat (ukládají se do clusterů)</p>
<p><strong>FastAlloc –</strong> dealokované bloky udržuje ve spojovém seznamu a při alokaci dokáže velice rychle vydat takový volný blok bez nutnosti volat globální alokátor. Je dobré, pokud každé vlákno má vlastní instanci, zvyšuje to propustnost alokací.</p>
<p>V článku nehledejte implementaci třídy DynObject, k té bych se dostal příště, bude tam pár dobrých nápadů které mohou ve výsledku urychlit alokace a dealokace pomocí allokátorů.</p>
]]></content:encoded>
			<wfw:commentRss>http://novacisko.blog.root.cz/2012/01/04/alokatory-a-operator-new-v-c/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>

