<?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/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Meta C++</title>
	<atom:link href="http://metacplusplus.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://metacplusplus.wordpress.com</link>
	<description>Just another WordPress.com weblog</description>
	<lastBuildDate>Thu, 13 May 2010 11:16:21 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='metacplusplus.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Meta C++</title>
		<link>http://metacplusplus.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://metacplusplus.wordpress.com/osd.xml" title="Meta C++" />
	<atom:link rel='hub' href='http://metacplusplus.wordpress.com/?pushpress=hub'/>
		<item>
		<title>const_string: Ein nichtkopierender read-only-string.</title>
		<link>http://metacplusplus.wordpress.com/2010/01/24/const_string-ein-nichtkopierender-read-only-string/</link>
		<comments>http://metacplusplus.wordpress.com/2010/01/24/const_string-ein-nichtkopierender-read-only-string/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 15:47:50 +0000</pubDate>
		<dc:creator>metacplusplus</dc:creator>
				<category><![CDATA[Optimierung]]></category>

		<guid isPermaLink="false">http://metacplusplus.wordpress.com/?p=98</guid>
		<description><![CDATA[1. Warum braucht man einen const_string? Ein const_string ist besonders geeignet für Parser, die nur lesend auf strings operieren. Hierzu ein simples Beispiel: std::string between_brackets(const std::string&#38; s) { std::size_t start = s.find('('); std::size_t end = s.find(')'); return s.substr(start, end - start); } Das Problem dieses Codes ist, dass es den Substring ausschneidet und in einen [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=98&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h2>1. Warum braucht man einen const_string?</h2>
<p>Ein const_string ist besonders geeignet für Parser, die nur lesend auf strings operieren. Hierzu ein simples Beispiel:</p>
<pre class="brush: cpp;">
std::string between_brackets(const std::string&amp; s)
{
    std::size_t start = s.find('(');
    std::size_t end = s.find(')');
    return s.substr(start, end - start);
}
</pre>
<p>Das Problem dieses Codes ist, dass es den Substring ausschneidet und in einen neuen String kopiert. Der const_string verhindert diesen Overhead.</p>
<h2>2. Die Arbeitsweise des const_strings</h2>
<p>Der const_string kann Kopien vollständig vermeiden, indem er lediglich einen Zeiger auf den Anfang und auf das Ende des Strings speichert. Er ist nicht Eigentümer der Daten, und kann so mit anderen strings den Inhalt teilen. Wird nun beispielsweise ein Substring erzeugt, wird ein neues const_string Objekt erstellt, bei dem nur begin und end-Zeiger verschoben sind. Der Inhalt selbst wird nicht kopiert.</p>
<h2>3. Implementation</h2>
<p>Nachdem das Prinzip erklärt wurde, hier nun die Implementierung.</p>
<pre class="brush: cpp;">
class const_string
{
public:
    const_string(const char* str):
        begin_(str),
        end_(str + std::strlen(str))
    {}
    const_string(const char* begin, const char* end):
        begin_(begin),
        end_(end)
    {}
    const char* begin() const
    {
        return begin_;
    }
    const char* end() const
    {
        return end_;
    }
    std::size_t size() const
    {
        return end() - begin();
    }
    const_string substr(std::size_t pos, std::size_t length = -1)
    {
        return const_string(begin() + pos, begin() + pos + std::min(size() - pos, n));
    }
private:
    const char* begin;
    const char* end;
};

// Verwendung:
const_string str = &quot;(teststring)&quot;;
const_string other_string = str.substr(1, str.size() - 2);
</pre>
<p>Diese Implementierung dient nur der Verdeutlichung des Prinzips, es fehlen noch einige wichtige Methoden, die ein string haben sollte. Das Interface ist außerdem nicht STL-kompatibel. Eine vollständige(re) Implementierung gibt&#8217;s <a href="http://nopaste.info/6674a896ad.html">hier.</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/metacplusplus.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/metacplusplus.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/metacplusplus.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/metacplusplus.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/metacplusplus.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/metacplusplus.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/metacplusplus.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/metacplusplus.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/metacplusplus.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/metacplusplus.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/metacplusplus.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/metacplusplus.wordpress.com/98/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/metacplusplus.wordpress.com/98/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/metacplusplus.wordpress.com/98/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=98&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://metacplusplus.wordpress.com/2010/01/24/const_string-ein-nichtkopierender-read-only-string/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/553031aa3fe562f5d10b6150c4c16de5?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">metacplusplus</media:title>
		</media:content>
	</item>
		<item>
		<title>Small Object Allocator</title>
		<link>http://metacplusplus.wordpress.com/2009/10/15/small-object-allocator-1/</link>
		<comments>http://metacplusplus.wordpress.com/2009/10/15/small-object-allocator-1/#comments</comments>
		<pubDate>Thu, 15 Oct 2009 09:58:16 +0000</pubDate>
		<dc:creator>metacplusplus</dc:creator>
				<category><![CDATA[Optimierung]]></category>

		<guid isPermaLink="false">http://metacplusplus.wordpress.com/?p=71</guid>
		<description><![CDATA[1. Wozu ein Small Object Allocator? Die meisten C++ Objekte sind relativ klein, meistens nur wenige Bytes. Manchmal muss man auch Integer anfordern, beispielsweise für den Referenzzähler bei Smart-pointern: template&#60;typename T&#62; class shared_ptr { public: explicit shared_ptr(T* ptr = 0): ptr(ptr), counter(new int(1)) {} shared_ptr(const shared_ptr&#38; other): ptr(other.ptr), counter(other.counter) {} ~shared_ptr() { if(--*counter == 0) [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=71&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h2>1. Wozu ein Small Object Allocator?</h2>
<p>Die meisten C++ Objekte sind relativ klein, meistens nur wenige Bytes. Manchmal muss man auch Integer anfordern, beispielsweise für den Referenzzähler bei Smart-pointern:</p>
<pre class="brush: cpp;">
template&lt;typename T&gt;
class shared_ptr
{
public:
    explicit shared_ptr(T* ptr = 0):
        ptr(ptr),
        counter(new int(1))
    {}
    shared_ptr(const shared_ptr&amp; other):
        ptr(other.ptr),
        counter(other.counter)
    {}
    ~shared_ptr()
    {
        if(--*counter == 0)
        {
            delete counter;
            delete ptr;
        }
    }
    // Und weitere typische smart-pointer methoden...
    T&amp; operator*()
    {
        return *ptr;
    }
private:
    int* counter;
    T* ptr;
};
</pre>
<p>Das ist eine unvollständige Implementierung eines shared_ptrs, ein Zeiger der ein Objekt automatisch löscht, sobald keine Referenzen mehr darauf existieren. Sie soll nur eines zeigen: Hier wird Speicher für ein sehr kleines Objekt angefordert, einen einfachen Integer.</p>
<p><em> Gut, wo ist jetzt das Problem?</em><br />
Die Speicherverwaltung für C++ ist dafür ausgelegt, mit allem zu funktionieren. Auch mit großen Objekten. Dementsprechend ist sie nicht für kleine Objekte optimiert: Je nach Implementation werden 4 bis 32 Byte zusätzlich zum eigentlichen Speicherblock allokiert, für Daten wie z.B. ob der Speicherbereich belegt ist, wie groß er ist etc. Wenn ein int z.B. 4 Byte groß ist, und unsere Speicherverwaltung benötigt 32 Byte an zusätzlichen Daten, haben wir einen Overhead von 800%. Fordert man aber z.B. 4096 Byte an, liegt der maximale Overhead nur noch bei ~0.8%.<br />
Also fordert man optimalerweise große Speicherblöcke über den Standardallokator an und verwaltet diesen selbst. Hier kommt der Small Object Allocator zum Einsatz, der diese Arbeit für uns übernimmt.</p>
<h2>2. Die Arbeitsweise</h2>
<p>Um den Speicher verwalten zu könnten, benötigen wir für jedes Objekt 2 Daten: Die Position im Speicher und die Objektgröße. Wenn wir allerdings jede Objektgröße erlauben, müssen wir zu jeder Speicherstelle noch eine Variable halten, die die Größe speichert. Im Fall von kleinen Objekten reicht ein Byte aus, aber der Overhead im Beispiel des shared_ptrs wäre immer noch bei 25%. Also lassen wir diese Information weg und erstellen uns einen Allokator, der immer nur Objekte einer bestimmten Größe anfordert.<br />
Eine Information müssen wir allerdings immer noch speichern: wo der verfügbare Speicher überhaupt liegt. Ein Zeiger ist üblicherweise 4 Byte groß, das macht im bekannten shared_ptr Beispiel einen Overhead von 100% aus. Das ist eindeutig zuviel. Man könnte nun folgendes tun: Man speichert lediglich einen Zeiger auf den ersten verfügbaren Speicher. Der verfügbare Speicher selbst zeigt auf den Speicherbereich dahinter, bis irgendwann ein Speicher eine 0 beinhaltet. Wenn wir bei einer 0 ankommen, fordern wir einen neuen größeren Speicherblock an den wir entsprechend mit seinen Verweisen initialisieren. Da der angeforderte Speicher selbst die Informationen zur Verwaltung trägt, haben wir lediglich den Overhead eines einzigen Zeigers für alle Objekte einer Größe. Zur Verdeutlichung, folgende Asciigrafik:</p>
<pre class="brush: plain;">
+-------------+   +-------------+   +-------------+
|Speicherblock|--&gt;|Speicherblock|--&gt;|Speicherblock|--&gt;
+-------------+   +-------------+   +-------------+
      ^
      |
+---------------------+
|Zeiger auf den ersten|
|verfügbaren Speicher |
+---------------------+
</pre>
<p>Wird jetzt Speicher angefordert, wird der erste Speicherbereich ausgelesen, der Zeiger auf den verfügbaren Speicher eins weitergesetzt und die Speicherstelle zurückgegeben, auf die der Zeiger zuerst verwies. Dadurch verlieren wir jegliche Kenntnis vom vorherigen Speicher. Was passiert, wenn wir den Speicher zur Freigabe zurückbekommen? Nehmen wir an, wir hätten 2 mal Speicher angefordert und der erste Speicher wurde vom Nutzer an uns zurückgegeben. Folgende Situation wäre dann gegeben:</p>
<pre class="brush: plain;">
+-------------+   +-------------+   +-------------+
|Speicherblock|   |Speicherblock|   |Speicherblock|--&gt;
+-------------+   +-------------+   +-------------+
                                         ^
                                         |
+---------------------+                  |
|Zeiger auf den ersten|------------------+
|verfügbaren Speicher |
+---------------------+
</pre>
<p>Nach der Freigabe des ersten Speicherblocks lassen wir den freigegebenen Speicher auf den nächsten verfügbaren Speicher verweisen und setzen unseren Zeiger auf den eben frei gewordenen Speicherblock. Dadurch können wir wieder freigewordenen Speicher wiederverwenden und haben kein Memoryleak das unendlich wächst. Nach dem Freigeben sieht unsere Struktur so aus:</p>
<pre class="brush: plain;">
       +----------------------------------+
       |                                  v
+-------------+   +-------------+   +-------------+
|Speicherblock|   |Speicherblock|   |Speicherblock|--&gt;
+-------------+   +-------------+   +-------------+
       ^
       |
+---------------------+
|Zeiger auf den ersten|
|verfügbaren Speicher |
+---------------------+
</pre>
<p>Diesem System ist es auch egal, ob die Blöcke am Stück im Speicher liegen, oder getrennt sind. Wir können also jederzeit neue größere Speicherblöcke anfordern und in unsere Liste einfügen. Hier wird der Speicher nur angefordert, wenn unser Zeiger auf 0 zeigt, also kein Block mehr verfügbar ist.</p>
<h2>3. Implementation</h2>
<p>Zunächst sollten wir ein grobes Interface für den Allokator definieren. Das Prinzip das wir hier haben, heißt auch Pool. Dementsprechend benennen wir unsere Struktur:</p>
<pre class="brush: cpp;">
struct pool
{
    void init(std::size_t num_blocks, std::size_t object_size);
    void* alloc();
    void free(void* p);
    void release();
};
</pre>
<p>init initialisiert das Objekt. Der Parameter num_objects gibt an, wieviele Objekte in einem Speicherblock Platz haben sollen. object_size ist die Größe der Objekte in Bytes. Das Gegenstück zu init, release, gibt alle angeforderten Speicherblöcke wieder frei. Dazu müssen wir zusätzlich Daten speichern; das wird erst am Ende dieses Abschnitts besprochen. (Ich verwende hier bewusst init und release statt Kon- und Destruktoren. Später wollen wir mehr Kontrolle über die Pools haben und selbst bestimmen, wann erzeugt und wann freigegeben wird. Es kommt im Prinzip auf das selbe heraus, wenn wir new und delete für einen Pool nutzen würden.)<br />
alloc gibt den Speicherplatz für ein Objekt zurück, free gibt den übergebenen Speicher wieder frei, bzw. fügt ihn in unsere Liste ein.<br />
Nun zur Implementation: Wir benötigen zuerst eine kleine Hilfsstruktur und 2 Methoden die uns das Leben etwas vereinfachen.</p>
<pre class="brush: cpp;">
    struct block
    {
        block* next;
    };

    static void set_next(void* p, void* next)
    {
        static_cast&lt;block*&gt;(p)-&gt;next = static_cast&lt;block*&gt;(next);
    }

    static void* get_next(void* p)
    {
        return static_cast&lt;block*&gt;(p)-&gt;next;
    }
</pre>
<p>Diese sind direkt innerhalb der Poolstruktur definiert, wir benötigen sie auch sonst nirgends. Wenn ein neuer Speicherblock angelegt wird, wird er zuerst mit den Blöcken überschrieben und die Zeiger der Blöcke entsprechend gesetzt. Da wir normalerweise nur void-Zeiger auf den Speicher haben, gibt es die Funktionen set_next und get_next die uns das casten abnehmen. get_next liefert die Adresse des Speicherbereichs, auf die der Block zeigt, set_next setzt die Adresse neu.<br />
Außerdem benötigen wir in der Struktur noch den Zeiger auf den Anfang unserer Liste. Wir nennen ihn head_of_free_list. Die Größe der Objekte und des Blocks werden auch in der Struktur gespeichert. Die init-Methode unseres Pools sieht so aus:</p>
<pre class="brush: cpp;">
void pool::init(std::size_t num_objects, std::size_t object_size)
{
    this-&gt;num_objects = num_objects;
    this-&gt;object_size = std::max(object_size, sizeof(block));
    head_of_free_list = 0;
}
</pre>
<p>Nichts besonders kompliziertes. Man mag sich fragen, wozu std::max hier gebraucht wird, aber das ist schnell erklärt. Die einzelnen Speicherstellen müssen mindestens so groß sein wie ein Objekt unserer Blockstruktur, damit wir unsere verkettete Liste aufbauen können. Man hat aber selten Objekte, die noch kleiner sind als die Blockstruktur, die schließlich nur einen Zeiger hält.<br />
Jetzt wird es etwas schwieriger: die Implementation von alloc.</p>
<pre class="brush: cpp;">
void* pool::alloc()
{
    void* p = head_of_free_list;
    if(p)
    {
        head_of_free_list = get_next(p);
    }
    else
    {
        char* new_block = new char[num_objects * object_size];
        for(std::size_t i = object_size; i &lt; (num_objects - 1) * object_size; i += object_size)
        {
            set_next(&amp;new_block[i], &amp;new_block[i + object_size]);
        }

        set_next(&amp;new_block[(num_objects - 1) * object_size], 0);
        p = new_block;
        head_of_free_list = &amp;new_block[object_size];
    }

    return p;
}
</pre>
<p>Ist noch Platz in der Liste, ist das ganze trivial: p ist nicht 0, also wird head_of_free_list eins weiter gesetzt und p zurückgegeben, das vorher den Wert von head_of_free_list angenommen hatte.<br />
Etwas unverständlicher sieht dagegen der else-Teil aus. Zunächst werden für den neuen Block num_objects * object_size Bytes Speicher angefordert. Die seltsam anmutende Schleife beschreibt das zweite bis zum vorletzten Objekt mit jeweils einem Zeiger auf das nächste. Das erste Objekt wird nicht beschrieben, da es ja sowieso danach an den Anwender zurückgegeben wird. Das letzte benötigt auch eine Spezialbehandlung, da es ins Nirvana zeigen soll, um das Ende der Liste anzugeben. Danach wird noch head_of_free_list auf das 2. Element des Blockes gesetzt und das 1. Element zurückgegeben. Das wars schon.</p>
<p>free gestaltet sich relativ einfach:</p>
<pre class="brush: cpp;">
void pool::free(void* p)
{
    if(p == 0)
        return;

    block* dead_object = static_cast&lt;block*&gt;(p);

    dead_object-&gt;next = static_cast&lt;block*&gt;(head_of_free_list);
    head_of_free_list = dead_object;
}
</pre>
<p>Der freigegebene Speicher verweist nun auf das vorher erste Objekt in der Liste und der Zeiger auf den Listenanfang wurde neu gesetzt.<br />
Übergebene 0-Zeiger ignorieren wir übrigens einfach aus Konsistenzgründen. In C++ ist schließlich auch ein delete 0; ohne weiteres möglich.<br />
Zu release:<br />
Das endgültige Freigeben des Speichers an das Betriebssystem beim Zerstören des Pools kann auch weggelassen werden. Dadurch haben wir nicht unbedingt ein Memoryleak. Memoryleaks entstehen, wenn man keinen Zugriff mehr auf Speicher hat, der noch nicht freigegeben wurde. Dadurch, dass wir aber die Objekte in unserer Liste verwalten, wächst der Speicherbedarf nicht ins unendliche, sondern erreicht einen Maximalwert. Es sei denn, jemand kommt auf die Idee, den pool immer wieder in einer Schleife anzulegen, dann entsteht natürlich ein Leak. Später in der Kapselung werden wir allerdings dafür sorgen, dass ein pool das komplette Programm überdauert. Sollte man dennoch den Speicher freigeben wollen, fügt man leider ein wenig Overhead hinzu: man muss beim Anfordern eines größeren Blockes einen Zeiger in einen std::vector einfügen, beim release durchlaufen und jedes Element mit delete [] löschen. Auf diese Weise kann man auch lokale Pools verwenden, falls man das unbedingt benötigt.</p>
<h2>4. Kapselung des Pools</h2>
<p>Zunächst sollten wir uns überlegen, wie die Anwendung aussehen soll. Es würde z.B. sehr einfach werden, wenn man Objekte einfach nur von einer Klasse small_object ableiten müsste, und schon wird bei jedem new automatisch dieser Allokator genutzt. Damit small_object die Größe des Typen kennt, werden wir daraus ein Template machen und als Parameter den Typen nehmen, der ableitet. In small_object reichen dann ein paar einfache Überladungen von operator new und delete und wir sind fast fertig. Zuerst benötigen wir einen Wrapper um den Pool, der automatisch init und release aufruft, und als dummy-Parameter für alloc und free die Größe des Speichers übernimmt. Wozu das gut ist, sehen wir später. Hier zuerst die einfache Implementation des fixed_size_allocators:</p>
<pre class="brush: cpp;">
class fixed_size_allocator
{
public:
    fixed_size_allocator(std::size_t num_objects, std::size_t object_size)
    {
        allocator.init(num_objects, object_size);
    }

    ~fixed_size_allocator()
    {
        allocator.release();
    }

    void* alloc(std::size_t)
    {
        return allocator.alloc();
    }

    void free(void* p, std::size_t)
    {
        allocator.free(p);
    }
private:
    fixed_size_allocator(const fixed_size_allocator&amp;);
    fixed_size_allocator&amp; operator=(const fixed_size_allocator&amp;);
    pool allocator;
};
</pre>
<p>Die Implementation von small_object kann dann so aussehen:</p>
<pre class="brush: cpp;">
template&lt;typename T&gt;
class small_object
{
    static void* operator new(std::size_t size)
    {
        return allocator.alloc(size);
    }
    static void* operator new(std::size_t size, void* p)
    {
        return p;
    }
    static void operator delete(void* p, std::size_t size)
    {
        allocator.free(p, size);
    }
private:
    static fixed_size_allocator allocator;
};
template&lt;typename T&gt;
fixed_size_allocator small_object&lt;T&gt;::allocator(64, sizeof(T));
</pre>
<p>Der 2. operator new sorgt übrigens nur dafür, dass placement-new immer noch möglich ist.<br />
Die Anwendung sieht folgendermaßen aus:</p>
<pre class="brush: cpp;">
struct foo : small_object&lt;foo&gt;
{
    int i;
};
</pre>
<p>Mehr braucht man nicht. Nur ableiten. Ein Problem hat diese Implementierung allerdings: Für jeden Typen wird ein neuer pool angelegt. Auch wenn 2 Typen die selbe Größe haben. Um das zu umgehen, schreiben wir nun eine Klasse small_obj_allocator, die Objekte beliebiger Größe anfordern kann. Sie sammelt dazu lediglich alle pools in einem vector und sucht nach dem mit der passenden Größe.</p>
<pre class="brush: cpp;">
class small_obj_allocator
{
    static bool compare_pool_size(const pool&amp; p, std::size_t size)
    {
        return p.object_size &lt; size;
    }
public:
    small_obj_allocator(std::size_t num_objects, std::size_t max_object_size):
        num_objects(num_objects),
        max_object_size(max_object_size),
        allocators(),
        last_alloc(0),
        last_free(0)
    {
    }
    ~small_obj_allocator()
    {
        std::for_each(allocators.begin(), allocators.end(),
                      std::mem_fun_ref(&amp;pool::release));
    }

    void* alloc(std::size_t size)
    {
        if(size &gt; max_object_size)
        {
            return ::operator new(size);
        }
        size = std::max(size, sizeof(pool::block));

        if(last_alloc &amp;&amp; last_alloc-&gt;object_size == size)
        {
            return last_alloc-&gt;alloc();
        }

        std::vector&lt;pool&gt;::iterator iter =
            std::lower_bound(allocators.begin(),
                             allocators.end(),
                             size,
                             compare_pool_size);

        if(iter == allocators.end() || iter-&gt;object_size != size)
        {
            iter = allocators.insert(iter, pool());
            iter-&gt;init(num_objects, size);
            last_free = &amp;allocators.front();
        }
        last_alloc = &amp;*iter;
        return last_alloc-&gt;alloc();
    }

    void free(void* p, std::size_t size)
    {
        if(size &gt; max_object_size)
        {
            ::operator delete(p);
            return;
        }

        if(last_free &amp;&amp; last_free-&gt;object_size == size)
        {
            last_free-&gt;free(p);
            return;
        }
        std::vector&lt;pool&gt;::iterator iter =
            std::lower_bound(allocators.begin(),
                             allocators.end(),
                             size,
                             compare_pool_size);
        last_free = &amp;*iter;
        last_free-&gt;free(p);
    }
private:
    std::size_t num_objects;
    std::size_t max_object_size;
    std::vector&lt;pool&gt; allocators;
    pool* last_alloc;
    pool* last_free;
};
</pre>
<p>Falls der Code jemandem bekannt vorkommt: Das ist die selbe Strategie, mit der Andrei Alexandrescus SmallObjAllocator die Anfragen an seine Pools in der <a href="http://loki-lib.sourceforge.net/">Loki-lib</a> weiterleitet. Ich werde hier nicht näher darauf eingehen, nur erklären wie er ungefähr arbeitet: Da man häufig mehrere Objekte hintereinander mit gleicher Größe anfordert oder freigibt, speichert small_obj_allocator den zuletzt verwendeten pool. Ist dieser für die Freigabe oder Allokation nicht zuständig, wird mit einer Binären Suche der passende pool festgestellt. Das ist möglich, da der vector ständig sortiert wird.<br />
Nun müssen wir das noch in unser small_object einbauen &#8211; wir sollten dem Benutzer aber die Wahl lassen. Die Suche nach dem passenden pool geht auf Kosten der Laufzeit, auf der anderen Seite benötigt der fixed_size_allocator wegen möglichen Doppelungen von pools mehr Speicher.<br />
Wir testen einfach den an small_object übergebenen typen. Wenn er ein von uns vordefinierter dummy-typ ist, wird der small_obj_allocator verwendet, ansonsten der fixed_size_allocator.<br />
Hier zeigt sich auch der Vorteil der Übergabe des size-Parameters an fixed_size_allocator: small_obj_allocator benötigt die Größe, fixed_size_allocator eigentlich nicht. Dadurch, dass aber beide nun die selbe Schnittstelle besitzen, müssen wir lediglich über ein typedef den Allokatortypen festlegen und der Rest geht von allein. Zusätzlich legen wir noch eine Konstante object_size fest. Bei dem vordefinierten dummy-typen wird sie auf 64 gesetzt, ansonsten auf sizeof(T). Das bedeutet, sobald vom small_obj_allocator ein Speicherbereich abgefragt wird, der größer als 64 Byte ist, wird die Anfrage an den globalen operator new weitergeleitet. Ebenso beim Freigeben an operator delete.<br />
Unser neues small_object-Template:</p>
<pre class="brush: cpp;">
struct empty_type {};

template&lt;typename T = empty_type&gt;
class small_object
{
    typedef is_same&lt;T, empty_type&gt; is_empty_type;
public:
    typedef typename if_
    &lt;
        is_empty_type,
        small_obj_allocator,
        fixed_size_allocator
    &gt;::type allocator_type;

    static const std::size_t object_size = is_empty_type::value ? 64 : sizeof(T);

    static void* operator new(std::size_t size)
    {
        return allocator.alloc(size);
    }

    static void* operator new(std::size_t size, void* p)
    {
        return p;
    }

    static void operator delete(void* p, std::size_t size)
    {
        allocator.free(p, size);
    }
private:
    static allocator_type allocator;
};

template&lt;typename T&gt;
typename small_object&lt;T&gt;::allocator_type
small_object&lt;T&gt;::allocator(64, small_object&lt;T&gt;::object_size);
</pre>
<p>is_same ist ein einfaches Template mit einer statischen integralen Konstante, die true ist, wenn beide übergebenen Typen gleich sind, ansonsten false. if_ wertet den ersten Parameter als Bedingung aus, wenn dessen value true ist, wird ein typedef auf den 2. übergebenen Typen gemacht, ansonsten auf den 3. Ein Compile-time-if also.<br />
Hier kurz ihre Definitionen:</p>
<pre class="brush: cpp;">
template&lt;bool If, typename Then, typename Else&gt;
struct if_c
{
    typedef Then type;
};

template&lt;typename Then, typename Else&gt;
struct if_c&lt;false, Then, Else&gt;
{
    typedef Else type;
};

template&lt;typename If, typename Then, typename Else&gt;
struct if_ : if_c&lt;If::value, Then, Else&gt; {};

template&lt;typename T, typename U&gt;
struct is_same
{
    static const bool value = false;
};

template&lt;typename T&gt;
struct is_same&lt;T, T&gt;
{
    static const bool value = true;
};
</pre>
<p>Ende. Ein einsatzbereiter Small Object Allocator.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/metacplusplus.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/metacplusplus.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/metacplusplus.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/metacplusplus.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/metacplusplus.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/metacplusplus.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/metacplusplus.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/metacplusplus.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/metacplusplus.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/metacplusplus.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/metacplusplus.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/metacplusplus.wordpress.com/71/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/metacplusplus.wordpress.com/71/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/metacplusplus.wordpress.com/71/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=71&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://metacplusplus.wordpress.com/2009/10/15/small-object-allocator-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/553031aa3fe562f5d10b6150c4c16de5?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">metacplusplus</media:title>
		</media:content>
	</item>
		<item>
		<title>C++ Idiom: RAII</title>
		<link>http://metacplusplus.wordpress.com/2009/09/17/c-idiom-raii/</link>
		<comments>http://metacplusplus.wordpress.com/2009/09/17/c-idiom-raii/#comments</comments>
		<pubDate>Thu, 17 Sep 2009 09:23:36 +0000</pubDate>
		<dc:creator>metacplusplus</dc:creator>
				<category><![CDATA[Idiom]]></category>

		<guid isPermaLink="false">http://metacplusplus.wordpress.com/?p=25</guid>
		<description><![CDATA[Beim Anfordern von Ressourcen muss man als Programmierer immer aufpassen, diese auch wieder richtig freizugeben. Beispiele sind Dateien schließen, Speicher freigeben oder Locks aufheben. Gleich zu Beginn ein Beispiel-Code: lock l; void do_something() { l.acquire(); if(some_resource == 0) { throw std::runtime_error(&#34;resource not found&#34;); } else { some_resource-&#62;weird_stuff(); } l.release(); } Das Problem ist klar: Wird [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=25&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Beim Anfordern von Ressourcen muss man als Programmierer immer aufpassen, diese auch wieder richtig freizugeben. Beispiele sind Dateien schließen, Speicher freigeben oder Locks aufheben. Gleich zu Beginn ein Beispiel-Code:</p>
<pre class="brush: cpp;">
lock l;

void do_something()
{
    l.acquire();
    if(some_resource == 0)
    {
        throw std::runtime_error(&quot;resource not found&quot;);
    }
    else
    {
        some_resource-&gt;weird_stuff();
    }
    l.release();
}
</pre>
<p>Das Problem ist klar: Wird eine Exception geworfen oder aus sonst einem Grund die Funktion früher erlassen, haben wir einen Deadlock. l wurde niemals freigegeben. Vor das throw auch noch ein l.release() zu schreiben, kann nicht die Lösung sein. weird_stuff könnte auch eine Exception werfen, und so die Funktion beenden, was ist also zu tun? Man könnte umständlich mit try-catch alles fangen, den Lock freigeben und rethrowen. Aber C++ hat ein besseres Mittel: Destruktoren</p>
<pre class="brush: cpp;">
class scoped_lock
{
public:
    scoped_lock(lock&amp; l):
        l(l)
    {
        l.acquire();
    }
    ~scoped_lock()
    {
        l.release();
    }
private:
    lock&amp; l;
};
</pre>
<p>Anwendung in unserem Fall:</p>
<pre class="brush: cpp;">
lock l;

void do_something()
{
    scoped_lock sl(l);
    if(some_resource == 0)
    {
        throw std::runtime_error(&quot;resource not found&quot;);
    }
    else
    {
        some_resource-&gt;weird_stuff();
    }
}
</pre>
<p>Egal auf welchem Weg die Funktion verlassen wird, der Destruktor von sl wird immer aufgerufen und der Lock freigegeben.</p>
<p>Wie sieht RAII in anderen Sprachen aus? Die meisten Sprachen kennen diese Technik nicht. So sieht z.B. der Code um eine Datei zu öffnen und aus ihr zu lesen in Java so aus:</p>
<pre class="brush: java;">
FileInputStream fis = null;
try
{
    fis = new FileInputStream(&quot;datei.txt&quot;);
    // mach was mit fis
    // ...
}
finally
{
    if(fis != null)
        fis.close();
}
</pre>
<p>Python hat hierfür seit Version 2.6 ein with-statement:</p>
<pre class="brush: python;">
with open(&quot;datei.txt&quot;) as f:
    # mach was mit f
    pass
</pre>
<p>Hier noch ein Beispiel in C++ zur Vertiefung: Eine Implementierung einer file-Klasse die auf C-Dateioperationen basiert und immer alles korrekt aufräumt (der Einfachheit halber nur zum Schreiben):</p>
<pre class="brush: cpp;">
class file
{
public:
    file(const std::string&amp; filename):
        fp(std::fopen(filename.c_str(), &quot;w&quot;))
    {
        if(fp == 0)
        {
            throw std::runtime_error(&quot;Could not open file&quot;);
        }
    }
    ~file()
    {
        std::fclose(fp);
    }

    void write(const std::string&amp; text)
    {
        if(std::fputs(text.c_str(), fp) == EOF)
        {
            throw std::runtime_error(&quot;Could not write to file&quot;);
        }
    }
private:
    std::FILE* fp;

    file(const file&amp;);
    file&amp; operator=(const file&amp;);
};
</pre>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/metacplusplus.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/metacplusplus.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/metacplusplus.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/metacplusplus.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/metacplusplus.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/metacplusplus.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/metacplusplus.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/metacplusplus.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/metacplusplus.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/metacplusplus.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/metacplusplus.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/metacplusplus.wordpress.com/25/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/metacplusplus.wordpress.com/25/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/metacplusplus.wordpress.com/25/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=25&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://metacplusplus.wordpress.com/2009/09/17/c-idiom-raii/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/553031aa3fe562f5d10b6150c4c16de5?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">metacplusplus</media:title>
		</media:content>
	</item>
		<item>
		<title>C++ Idiom: Copy &amp; Swap</title>
		<link>http://metacplusplus.wordpress.com/2009/09/12/c-idiom-copy-swap/</link>
		<comments>http://metacplusplus.wordpress.com/2009/09/12/c-idiom-copy-swap/#comments</comments>
		<pubDate>Sat, 12 Sep 2009 10:29:21 +0000</pubDate>
		<dc:creator>metacplusplus</dc:creator>
				<category><![CDATA[Idiom]]></category>

		<guid isPermaLink="false">http://metacplusplus.wordpress.com/?p=18</guid>
		<description><![CDATA[Nehmen wir einmal an, wir möchten eine Arrayklasse für Objekte dieses Typen bauen: struct object { object&#38; operator=(const object&#38; other) { if(std::rand() == 12389) { throw std::exception(); } } }; Eine erste Implementation könnte so aussehen: class object_array { public: object_array(): array(0), size(0) {} explicit object_array(std::size_t size): array(new object[size]), size(size) {} object_array(const object_array&#38; other): array(0), [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=18&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Nehmen wir einmal an, wir möchten eine Arrayklasse für Objekte dieses Typen bauen:</p>
<pre class="brush: cpp;">
struct object
{
    object&amp; operator=(const object&amp; other)
    {
        if(std::rand() == 12389)
        {
            throw std::exception();
        }
    }
};
</pre>
<p>Eine erste Implementation könnte so aussehen:</p>
<pre class="brush: cpp;">
class object_array
{
public:
    object_array():
        array(0),
        size(0)
    {}

    explicit object_array(std::size_t size):
        array(new object[size]),
        size(size)
    {}

    object_array(const object_array&amp; other):
        array(0),
        size(other.size)
    {
        if(other.array &amp;&amp; size)
        {
            try
            {
                array = new object[size];
                std::copy(other.array, other.array + size, array);
            }
            catch(...)
            {
                delete [] array; // Im Fehlerfall speicher freigeben, um kein Speicherleck zu hinterlassen.
                throw;
            }
        }
    }

    ~object_array()
    {
        delete [] array;
    }

    object_array&amp; operator=(const object_array&amp; other)
    {
        delete [] array;
        array = 0;
        size = other.size;
        if(other.array &amp;&amp; size)
        {
            array = new object[size];
            std::copy(other.array, other.array + size, array);
        }
        return *this;
    }

    object&amp; operator[](std::size_t index)
    {
        assert(index &lt; size);
        return array[index];
    }

    object operator[](std::size_t index) const
    {
        assert(index &lt; size);
        return array[index];
    }

private:
    object* array;
    std::size_t size;
};
</pre>
<p>Diese Implementation weist einige Probleme auf. operator= beinhaltet praktisch den selben Code wie der Kopierkonstruktor. Nun könnte man eine Funktion copy schreiben, die von operator= und vom Kopierkonstruktor aufgerufen wird.</p>
<p>Doch was passiert bei einer Selbstzuweisung?</p>
<pre class="brush: cpp;">
object_array array(5);
array = array;
</pre>
<p>Verursacht undefiniertes Verhalten. Das eigene Array wird im Zuweisungsoperator gelöscht und vom anderen Objekt in das eigene Array hineinkopiert. Wenn das andere Array das selbe ist wie das eigene wird beim Kopieren auf Speicher zugegriffen, der gerade erst freigegeben wurde. Das lässt sich aber auch regeln. Über eine Simple abfrage ob this == &amp;other.</p>
<p>Ein Problem besteht aber immer noch: Eine Kopieroperation von object kann eine Exception werfen. Was passiert nun, wenn bei operator= das Kopieren eines Elements mittendrin fehlschlägt? operator= bricht ab und lässt das Array in einem ungültigen Zustand. Alle Elemente nach dem objekt, das die Exception warf, bleiben unkopiert.</p>
<p>Die Lösung: Das Copy &amp; Swap Idiom. Alle eben genannten Probleme lassen sich darüber lösen, ohne irgendwelchen copy-Funktionen oder Abfragen ob this == &amp;other. Wir ergänzen eine swap-Funktion, die 2 object_arrays miteinander tauscht:</p>
<pre class="brush: cpp;">
    void swap(object_array&amp; other)
    {
        std::swap(array, other.array);
        std::swap(size, other.size);
    }
</pre>
<p>Unseren operator= implementieren wir nun so:</p>
<pre class="brush: cpp;">
    object_array&amp; operator=(object_array other)
    {
        swap(other);
        return *this;
    }
</pre>
<p>Er arbeitet folgendermaßen: Das auf der rechten Seite stehende Objekt der Zuweisung wird über den Kopierkonstruktor kopiert. Dieses Objekt wird über swap getauscht. Wenn beim Kopieren etwas fehlschlägt, bleibt das Objekt, dem zugewiesen wurde, in seinem ursprünglichen, gültigen Zustand. Beim swap kann nichts fehlschlagen, da lediglich Zeiger und Integer getauscht werden müssen. Das other-objekt übernimmt den Speicher des alten Objekts und zerstört diesen beim Verlassen der Funktion. Hier noch einmal die vollständige Klasse:</p>
<pre class="brush: cpp;">
class object_array
{
public:
    object_array():
        array(0),
        size(0)
    {}

    explicit object_array(std::size_t size):
        array(new object[size]),
        size(size)
    {}

    object_array(const object_array&amp; other):
        array(0),
        size(other.size)
    {
        if(other.array &amp;&amp; size)
        {
            try
            {
                array = new object[size];
                std::copy(other.array, other.array + size, array);
            }
            catch(...)
            {
                delete [] array; // Im Fehlerfall speicher freigeben, um kein Speicherleck zu hinterlassen.
                throw;
            }
        }
    }

    ~object_array()
    {
        delete [] array;
    }

    object_array&amp; operator=(object_array other)
    {
        swap(other);
        return *this;
    }

    void swap(object_array&amp; other)
    {
        std::swap(array, other.array);
        std::swap(size, other.size);
    }

    object&amp; operator[](std::size_t index)
    {
        assert(index &lt; size);
        return array[index];
    }

    object operator[](std::size_t index) const
    {
        assert(index &lt; size);
        return array[index];
    }

private:
    object* array;
    std::size_t size;
};
</pre>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/metacplusplus.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/metacplusplus.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/metacplusplus.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/metacplusplus.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/metacplusplus.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/metacplusplus.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/metacplusplus.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/metacplusplus.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/metacplusplus.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/metacplusplus.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/metacplusplus.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/metacplusplus.wordpress.com/18/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/metacplusplus.wordpress.com/18/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/metacplusplus.wordpress.com/18/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=18&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://metacplusplus.wordpress.com/2009/09/12/c-idiom-copy-swap/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/553031aa3fe562f5d10b6150c4c16de5?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">metacplusplus</media:title>
		</media:content>
	</item>
		<item>
		<title>LISP-Konzepte in C++ Templates</title>
		<link>http://metacplusplus.wordpress.com/2009/09/11/3/</link>
		<comments>http://metacplusplus.wordpress.com/2009/09/11/3/#comments</comments>
		<pubDate>Fri, 11 Sep 2009 12:02:00 +0000</pubDate>
		<dc:creator>metacplusplus</dc:creator>
				<category><![CDATA[Metaprogrammierung]]></category>

		<guid isPermaLink="false">http://metacplusplus.wordpress.com/?p=3</guid>
		<description><![CDATA[In diesem Eintrag will ich zeigen, wie man einige Konzepte aus LISP direkt in C++ Templates übertragen kann. Zu Beginn folgender Scheme-Code: (define (cons a b) (lambda (f) (f a b))) (define (car l) (l (lambda (a b) a))) (define (cdr l) (l (lambda (a b) b))) Das ist eine Möglichkeit, ein Listensystem in Scheme [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=3&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In diesem Eintrag will ich zeigen, wie man einige Konzepte aus LISP direkt in C++ Templates übertragen kann.<br />
Zu Beginn folgender Scheme-Code:</p>
<p>
<pre class="brush: cpp;">
(define (cons a b)
  (lambda (f) (f a b)))
(define (car l)
  (l (lambda (a b) a)))
(define (cdr l)
  (l (lambda (a b) b)))
</pre>
</p>
<p>Das ist eine Möglichkeit, ein Listensystem in Scheme zu implementieren. Das sieht folgendermaßen aus: Eine Liste ist ein Paar bestehend aus einem Element und dem Rest, wobei der Rest wiederum eine Liste ist, bis irgendwann ein Element am Ende steht, das nichts repräsentiert.</p>
<p>
<pre class="brush: cpp;">
(cons 1 (cons 2 (cons 3 (cons 4 nil))))
</pre>
</p>
<p>erstellt eine Liste mit den Zahlen von 1 bis 4. Mit car besteht die Möglichkeit, auf das Element zuzugreifen, mit cdr auf den Rest. (Die eben konstruierte Liste ist hier l):<br />
(cdr l) sind die Zahlen 2 bis 4.<br />
(car l) ist die 1.<br />
Unsere Implementation in Scheme funktioniert so:<br />
cons gibt eine Funktion zurück, die eine Funktion als Parameter übernimmt, ihr das Element und Rest der Liste als Parameter übergibt, und ihren Rückgabewert zurückgibt. car übergibt der mit cons erzeugten Funktion eine Funktion, die ihren ersten Parameter zurückgibt, also das Element. cdr tut das gleiche, allerdings gibt es den Rest zurück. Das will ich nun in C++ umsetzen.<br />
Um alles mögliche in einer Liste zu speichern, benötigen wir eine allgemeine Schnittstelle, wie Listenelemente aussehen sollen. In der Metaprogrammierung in C++ verwendet man dafür normalerweise Typen. Wir brauchen also eine Möglichkeit, um aus Zahlen Typen zu machen. Das ist relativ simpel:</p>
<p>
<pre class="brush: cpp;">
template&lt;typename T, T Value&gt;
struct integral_constant
{
    typedef integral_constant&lt;T, Value&gt; type;
    typedef T value_type;
    static const T value = Value;
};
template&lt;int Value&gt;
struct int_ : integral_constant&lt;int, Value&gt;{};
</pre>
</p>
<p>integral_constant speichert den übergebenen Wert im statischen Member value. Dadurch können wir aus dem Typen auch jederzeit die Zahl wieder herausholen. int_ ist hier nur dazu da, damit wir über eine kürzere Schreibweise aus einem Integer einen Typen machen können.<br />
Nun zum komplizierten Teil: Wie setzt man Lambdas in C++ um? Dazu benötigen wir zunächst das Konzept der Metafunktion. Eine Metafunktion ist grundsätzlich so aufgebaut:</p>
<p>
<pre class="brush: cpp;">
struct mul
{
    template&lt;typename Param1, typename Param2&gt;
    struct apply
    {
        typedef int_&lt;Param1::value * Param2::value&gt; type;
    };
};
</pre>
</p>
<p>Die Metafunktion muss ein Strukturentemplate mit dem Namen apply besitzen. apply ist die eigentliche Funktion. Ihr Rückgabewert stellt sich über einen Typen mit dem Namen type dar. Die hier gezeigte Funktion nimmt an, dass die Parameter das integral_constant Konzept unterstützen und hat als Rückgabewert einen Integer, in dem das Produkt der beiden Parameter steht. Ein Aufruf inklusive Ausgabe könnte z.B. so aussehen:</p>
<p>
<pre class="brush: cpp;">
std::cout &lt;&lt; mul::apply&lt;int_&lt;3&gt;, int_&lt;2&gt; &gt;::type::value;
</pre>
</p>
<p>Die Ausgabe ist 6.<br />
Eine Lambda können wir in C++ als einfache lokale Metafunktion umsetzen. Damit wird unser cons &#8211; übrigens auch eine Metafunktion &#8211; zu:</p>
<p>
<pre class="brush: cpp;">
struct cons
{
    template&lt;typename A, typename B&gt;
    struct apply
    {
        struct type
        {
            template&lt;typename F&gt;
            struct apply
            {
                typedef typename F::template apply&lt;A, B&gt;::type type;
            };
        };
    };
};
</pre>
</p>
<p>Das Ganze sieht erstmal etwas kompliziert aus, ist es aber eigentlich gar nicht. apply kennen wir ja schon, es beinhaltet praktisch die eigentliche Funktion. Es bekommt hier das Element (A) und den Rest der Liste (B) als Parameter. Der Rückgabewert type ist hier erneut eine Metafunktion, die eine Metafunktion (F) erwartet. Dessen Rückgabewert ist auch der Rückgabewert der Metafunktion type.<br />
Nun benötigen wir erst einmal die Funktion car, um auf das erste Element der Liste zugreifen zu können.</p>
<p>
<pre class="brush: cpp;">
struct car
{
    template&lt;typename L&gt;
    struct apply
    {
        struct lambda
        {
            template&lt;typename A, typename B&gt;
            struct apply
            {
                typedef A type;
            };
        };
        typedef typename L::template apply&lt;lambda&gt;::type type;
    };
};
</pre>
</p>
<p>Auch hier ist wieder in apply die eigentliche Funktion. Sie erwartet die Liste L, eigentlich eine Metafunktion und übergibt ihr eine Lambdafunktion, die ihr erstes Argument zurückgibt. Also genau das Prinzip wie in der Schemeimplementation. cdr, die Funktion die den Rest der Liste zurückgibt, sieht ähnlich aus und unterscheidet sich nur in der Lambdafunktion:</p>
<p>
<pre class="brush: cpp;">
struct cdr
{
    template&lt;typename L&gt;
    struct apply
    {
        struct lambda
        {
            template&lt;typename A, typename B&gt;
            struct apply
            {
                typedef B type;
            };
        };
        typedef typename L::template apply&lt;lambda&gt;::type type;
    };
};
</pre>
</p>
<p>Als Markierung für das Ende einer Liste nehmen wir einfach einen Typen, der nil darstellt.</p>
<p>
<pre class="brush: cpp;">struct nil {};</pre>
</p>
<p>Das Ganze nun in einem Programm angewandt:</p>
<p>
<pre class="brush: cpp;">
#include &lt;iostream&gt;
typedef cons::apply&lt;int_&lt;1&gt;,
        cons::apply&lt;int_&lt;2&gt;,
        cons::apply&lt;int_&lt;3&gt;,
        cons::apply&lt;int_&lt;4&gt;,
        nil&gt;::type&gt;::type&gt;::type&gt;::type list;
template&lt;typename L&gt;
struct print_list
{
    static void run()
    {
        std::cout &lt;&lt; car::apply&lt;L&gt;::type::value &lt;&lt; std::endl;
        print_list&lt;typename cdr::apply&lt;L&gt;::type&gt;::run();
    }
};
template&lt;&gt;
struct print_list&lt;nil&gt;
{
    static void run()
    {
    }
};
int main()
{
    print_list&lt;list&gt;::run();
}
</pre>
</p>
<p>print_list&lt;&gt;::run gibt das erste Element der Liste aus, und ruft sich rekursiv mit dem Rest der Liste auf. Die Rekursion stoppt, wenn der Parameter nur noch nil ist.</p>
<p>Eine Liste zur Compilezeit geht in C++ natürlich auch einfacher, aber hier wollte ich nur die direkte Umsetzung eines LISP Konzepts in C++ zeigen, auch wie Lambdas möglich sind und wie man eine Allgemeine Schnittstelle für Zahlen, Typen oder Funktionen findet.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/metacplusplus.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/metacplusplus.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/metacplusplus.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/metacplusplus.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/metacplusplus.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/metacplusplus.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/metacplusplus.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/metacplusplus.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/metacplusplus.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/metacplusplus.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/metacplusplus.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/metacplusplus.wordpress.com/3/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/metacplusplus.wordpress.com/3/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/metacplusplus.wordpress.com/3/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=metacplusplus.wordpress.com&amp;blog=9422707&amp;post=3&amp;subd=metacplusplus&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://metacplusplus.wordpress.com/2009/09/11/3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/553031aa3fe562f5d10b6150c4c16de5?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">metacplusplus</media:title>
		</media:content>
	</item>
	</channel>
</rss>
