<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Engine Pattern]]></title><description><![CDATA[<p>Hallo,</p>
<p>ich betrachte gerade folgendes Beispiel für statischen Polymorphismus. Und mir ist nicht ganz klar, was der Vorteil der &quot;statischen basisklasse&quot; <em>Swallow</em> ist. Ich kann ja, wie in der methode <em>print_nobase()</em> eine Art &quot;duck-typing&quot; betreiben, und verschiedene klassen gleich behandeln.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

template&lt;class Engine&gt; 
class Swallow
{
    Engine engine_imp;
public:
    float get_airspeed() const
    {
	return engine_imp.get_airspeed();
    }
};

class EuropeanSwallow
{
 public:
    float get_airspeed() const
    {
	return 11.0f;
    }
};

class AfricanSwallow
{
 public:
    float get_airspeed() const
    {
	return 12.0f;
    }
};

template&lt;class T&gt;
void print_airspeed(const Swallow&lt;T&gt; &amp; s)
{
    cout &lt;&lt; &quot;Flying at a speed of &quot; &lt;&lt; s.get_airspeed() &lt;&lt; endl;
}

template&lt;class T&gt;
void print_nobase(const T &amp; s)
{
    cout &lt;&lt; &quot;Flying at a speed of &quot; &lt;&lt; s.get_airspeed() &lt;&lt; &quot;, NO BASE!&quot; &lt;&lt; endl;
}

int main()
{
    Swallow&lt;AfricanSwallow&gt; a;
    Swallow&lt;EuropeanSwallow&gt; b;
    print_airspeed(a);
    print_airspeed(b);
    print_nobase(a);
    print_nobase(b);
}
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/topic/265578/engine-pattern</link><generator>RSS for Node</generator><lastBuildDate>Thu, 03 Sep 2026 20:31:42 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/265578.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 25 Apr 2010 17:44:31 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Engine Pattern on Sun, 25 Apr 2010 17:44:31 GMT]]></title><description><![CDATA[<p>Hallo,</p>
<p>ich betrachte gerade folgendes Beispiel für statischen Polymorphismus. Und mir ist nicht ganz klar, was der Vorteil der &quot;statischen basisklasse&quot; <em>Swallow</em> ist. Ich kann ja, wie in der methode <em>print_nobase()</em> eine Art &quot;duck-typing&quot; betreiben, und verschiedene klassen gleich behandeln.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;

template&lt;class Engine&gt; 
class Swallow
{
    Engine engine_imp;
public:
    float get_airspeed() const
    {
	return engine_imp.get_airspeed();
    }
};

class EuropeanSwallow
{
 public:
    float get_airspeed() const
    {
	return 11.0f;
    }
};

class AfricanSwallow
{
 public:
    float get_airspeed() const
    {
	return 12.0f;
    }
};

template&lt;class T&gt;
void print_airspeed(const Swallow&lt;T&gt; &amp; s)
{
    cout &lt;&lt; &quot;Flying at a speed of &quot; &lt;&lt; s.get_airspeed() &lt;&lt; endl;
}

template&lt;class T&gt;
void print_nobase(const T &amp; s)
{
    cout &lt;&lt; &quot;Flying at a speed of &quot; &lt;&lt; s.get_airspeed() &lt;&lt; &quot;, NO BASE!&quot; &lt;&lt; endl;
}

int main()
{
    Swallow&lt;AfricanSwallow&gt; a;
    Swallow&lt;EuropeanSwallow&gt; b;
    print_airspeed(a);
    print_airspeed(b);
    print_nobase(a);
    print_nobase(b);
}
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/1888185</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1888185</guid><dc:creator><![CDATA[SummerOf84]]></dc:creator><pubDate>Sun, 25 Apr 2010 17:44:31 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Mon, 26 Apr 2010 08:54:08 GMT]]></title><description><![CDATA[<p>In dem Beispiel hast du keinen Vorteil.<br />
Im realen Einsatz hast du vermutlich relativ viel Code in Swallow&lt;T&gt; stehen, und nur relativ wenig in T. Bzw. einfach andere Dinge.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1888354</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1888354</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Mon, 26 Apr 2010 08:54:08 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Mon, 26 Apr 2010 09:57:59 GMT]]></title><description><![CDATA[<p>hustbaer schrieb:</p>
<blockquote>
<p>In dem Beispiel hast du keinen Vorteil.<br />
Im realen Einsatz hast du vermutlich relativ viel Code in Swallow&lt;T&gt; stehen, und nur relativ wenig in T.</p>
</blockquote>
<p>Aber dann kann ich die Spezialisierungen immer noch von Swallow erben (mit nichtvirtuellen Methoden), und habe keinen Geschwindigkeitsnachteil?</p>
<blockquote>
<p>Bzw. einfach andere Dinge.</p>
</blockquote>
<p>elaborate? real world examples?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1888377</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1888377</guid><dc:creator><![CDATA[SummerOf84]]></dc:creator><pubDate>Mon, 26 Apr 2010 09:57:59 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Mon, 26 Apr 2010 12:33:31 GMT]]></title><description><![CDATA[<p>SummerOf84 schrieb:</p>
<blockquote>
<p>hustbaer schrieb:</p>
<blockquote>
<p>In dem Beispiel hast du keinen Vorteil.<br />
Im realen Einsatz hast du vermutlich relativ viel Code in Swallow&lt;T&gt; stehen, und nur relativ wenig in T.</p>
</blockquote>
<p>Aber dann kann ich die Spezialisierungen immer noch von Swallow erben (mit nichtvirtuellen Methoden), und habe keinen Geschwindigkeitsnachteil?</p>
</blockquote>
<p>Wo siehst du hier Spezialisierung? Ich sehe hier nur ein ungünstiges Beispiel, das nur einen Spezialfall zeigt, nämlich wo die &quot;Engine&quot; Funktion gleicht heisst und genau 100% dasselbe tut wie die Frontend-Funktion.</p>
<blockquote>
<blockquote>
<p>Bzw. einfach andere Dinge.</p>
</blockquote>
<p>elaborate? real world examples?</p>
</blockquote>
<p>Look for yourself?<br />
Boost.Asio?</p>
<p>----</p>
<p>Vielleicht ist in dem Paper/... wo du das her hast etwas anderes gemeint. Wäre halt hilfreich gewesen wenn du es verlinkt hättest...</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1888464</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1888464</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Mon, 26 Apr 2010 12:33:31 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Mon, 26 Apr 2010 12:35:17 GMT]]></title><description><![CDATA[<p>hustbaer schrieb:</p>
<blockquote>
<blockquote>
<blockquote>
<p>Bzw. einfach andere Dinge.</p>
</blockquote>
<p>elaborate? real world examples?</p>
</blockquote>
<p>Look for yourself?<br />
Boost.Asio?</p>
</blockquote>
<p>Oder vielleicht etwas einfacher zu verstehen:<a href="http://www.boost.org/doc/libs/1_42_0/libs/random/index.html" rel="nofollow"> <code>Boost.Random</code> </a>.</p>
<p>Grüssli</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1888466</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1888466</guid><dc:creator><![CDATA[Dravere]]></dc:creator><pubDate>Mon, 26 Apr 2010 12:35:17 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Mon, 26 Apr 2010 20:31:00 GMT]]></title><description><![CDATA[<p>hustbaer schrieb:</p>
<blockquote>
<p>Vielleicht ist in dem Paper/... wo du das her hast etwas anderes gemeint. Wäre halt hilfreich gewesen wenn du es verlinkt hättest...</p>
</blockquote>
<p><a href="http://hal.iwr.uni-heidelberg.de/teaching/numsimneuro_ss2010/c++-skript.pdf" rel="nofollow">http://hal.iwr.uni-heidelberg.de/teaching/numsimneuro_ss2010/c++-skript.pdf</a></p>
]]></description><link>https://www.c-plusplus.net/forum/post/1888692</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1888692</guid><dc:creator><![CDATA[SummerOf84]]></dc:creator><pubDate>Mon, 26 Apr 2010 20:31:00 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Mon, 26 Apr 2010 23:28:03 GMT]]></title><description><![CDATA[<p>Also das Beispiel in dem Skript finde ich genauso unglücklich gewählt wie dein Beispiel <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f609.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--winking_face"
      title=";)"
      alt="😉"
    /></p>
<p>Vergiss erstmal dass in das Skript von klassischer Vererbungs-Polymorphis mit virtuellen Funktionen ausgeht und versucht dann die Kurve zur &quot;Engine&quot; zu kratzen.</p>
<p>Das was du &quot;statische Basisklasse&quot; nennst, ist nämlich keine Basisklasse, das ist einfach eine Klasse die halt irgendeine Aufgabe hat.</p>
<p>Sehen wir uns ein Boost.Random-inspiriertes Beispiel an.</p>
<p>Sagen wir du willst Zufallszahlen erzeugen. Sagen wir weiterhin du benötigst immer nur gleichverteilte Zahlen von 0 bis N-1 (N=variabel zur Laufzeit), möchtest aber verschiedene Generatoren verwenden können (LC, Mersenne-Twister, echte Hardware-Generatoren - was auch immer).</p>
<p>Könnte man natürlich alles ohne Polymorphie und/oder Engines lösen. Geht aber schöner mit, denn den Teil &quot;gleichverteilte Zahlen von 0 bis N-1&quot; können wir 1x für alle Generatoren implementieren. Dazu benötigen wir vom Generator lediglich eine Funktion mit der man gleichverteilte Zahlen von 0 bis K-1 ziehen kann, wobei K vom Generator vorgegeben wird (üblicherweise ist K konstant, dürfte aber für dieses Beispiel genauso variabel sein, nur dass wir der Einfachkeit halber N &lt;= K fordern).</p>
<p>Klassisch <em>könnte</em> man das nun natürlich über eine Basisklasse und Vererbung lösen (wobei ich es nicht empfehlen würde, weil es Zuständigkeiten vermischt und die Vererbung vollkommen unnötig ist):</p>
<pre><code class="language-cpp">class GeneratorBase
{
private:
	// Einschränkung: K muss in einem 32 Bit Integer Platz haben
	virtual uint32_t GetRawUpperBound() const = 0; // unser &quot;K&quot;
	virtual uint32_t GetRawNumber() = 0;
public:
	uint32_t GetNumber(uint32_t upperBound) // unser &quot;N&quot;
	{
		assert(upperBound &gt;= 1); // Resultat von 0 bis (upperBound - 1) gewünscht, also darf upperBound nie 0 werden.
		uint32_t const rawUpperBound = GetRawUpperBound();
		assert(rawUpperBound &gt;= upperBound); // würde für dieses Beispiel zu weit führen wenn wir N &gt; K erlauben
		uint32_t const usableRawLimit = (rawUpperBound / upperBound) * upperBound;
		for (;;)
		{
			uint32_t const rawNumber = GetRawNumber();
			if (rawNumber &lt; usableRawLimit)
				return rawNumber % upperBound;
		}
	}
};

class BuiltinGenerator : public GeneratorBase
{
private:
	virtual uint32_t GetRawUpperBound() const { return RAND_MAX; }
	virtual uint32_t GetRawNumber() { return rand(); }
};

class SomeHardwareGenerator : public GeneratorBase
{
public:
	SomeHardwareGenerator(char const* devicePath)
	{
		// Device öffnen
	}
private:
	virtual uint32_t GetRawUpperBound() const
	{
		// ggf. Upper Bound vom Device holen wenn nicht zur Compile-Zeit bekannt
	}
	virtual uint32_t GetRawNumber()
	{
		// Nummer vom Device holen
	}
};
</code></pre>
<p>Kann man aber auch so schreiben:</p>
<pre><code class="language-cpp">class RngEngine
{
public:
	virtual uint32_t GetRawUpperBound() const { return RAND_MAX; }
	virtual uint32_t GetRawNumber() { return rand(); }
};

class VariateGenerator
{
public:
	explicit VariateGenerator(RngEngine* engine) :
		m_engine(engine)
	{
	}

	uint32_t GetNumber(uint32_t upperBound)
	{
		assert(upperBound &gt;= 1); 
		uint32_t const rawUpperBound = m_engine-&gt;GetRawUpperBound(); // immer noch ein virtual call, aber bessere Trennung der Zuständigkeiten
		assert(rawUpperBound &gt;= upperBound); 
		uint32_t const usableRawLimit = (rawUpperBound / upperBound) * upperBound;
		for (;;)
		{
			uint32_t const rawNumber = m_engine-&gt;GetRawNumber(); // ebenso
			if (rawNumber &lt; usableRawLimit)
				return rawNumber % upperBound;
		}
	}
private:
	RngEngine* m_engine;
};

class BuiltinGenerator : public RngEngine
{
public:
	virtual uint32_t GetRawUpperBound() const { return RAND_MAX; }
	virtual uint32_t GetRawNumber() { return rand(); }
};

class SomeHardwareGenerator : public RngEngine
{
public:
	explicit SomeHardwareGenerator(char const* devicePath) { ... }
	virtual uint32_t GetRawUpperBound() const { ... }
	virtual uint32_t GetRawNumber() { ... }
};
</code></pre>
<p>Und schon sind wir die (sinnlose) Vererbung los.</p>
<p>Genau das kann man jetzt auch mittels Duck-Typing machen, was die virtuellen Methodenaufrufe zu normalen macht, was wiederum Inlining ermöglicht, was wiederum viele weitere Optimierungen ermöglicht.<br />
Sieht dann so aus:</p>
<pre><code class="language-cpp">template &lt;class Engine&gt;
class VariateGenerator
{
public:
	explicit VariateGenerator(Engine engine = Engine()) :
		m_engine(engine)
	{
	}

	uint32_t GetNumber(uint32_t upperBound)
	{
		assert(upperBound &gt;= 1);
		uint32_t const rawUpperBound = m_engine.GetRawUpperBound();
		assert(rawUpperBound &gt;= upperBound);
		uint32_t const usableRawLimit = (rawUpperBound / upperBound) * upperBound;
		for (;;)
		{
			uint32_t const rawNumber = m_engine.GetRawNumber();
			if (rawNumber &lt; usableRawLimit)
				return rawNumber % upperBound;
		}
	}
private:
	Engine m_engine;
};

class BuiltinGenerator
{
public:
	uint32_t GetRawUpperBound() const { return RAND_MAX; }
	uint32_t GetRawNumber() { return rand(); }
};

class SomeHardwareGenerator
{
public:
	explicit SomeHardwareGenerator(char const* devicePath) { ... }
	uint32_t GetRawUpperBound() const { ... }
	uint32_t GetRawNumber() { ... }
};
</code></pre>
<p>Und man muss hier nicht Schluss machen. Wenn wir schon mit Templates arbeiten, dann muss uint32_t hier kein Fixer Typ mehr sein, sondern wir können ein Typedef in der Engine-Klasse verwenden, um so wo nötig einen grösseren Typ verwenden zu können (z.B. uint64_t, evtl. noch grössere wenn die Plattform welche hat), und wo nicht nötig einen kleineren Typ (z.B. weil Rechnungen mit kleineren Typen auf einigen CPUs schneller sind).</p>
<p>Bei diesem einfachen Beispiel fallen mir jetzt auch keine weiteren Dinge ein wo man sich zu Netzen machen könnte dass man mit Templates arbeitet, aber je nach Anwendung kann es da einiges geben.</p>
<p>Nun noch zum Sinn oder Unsinn eine Template-Funktion auf Ding&lt;T&gt; (in meinem Beispiel VariateGenerator&lt;Engine&gt;) einzuschränken, anstatt einfach jedes T zuzulassen...<br />
Sinn: eine solche Einschränkung kann zu besser verständlichem und robusterem Code führen.<br />
Unsinn: es ist eben eine Einschränkung <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /> Könnte ja sein dass jemand schon ein fertiges AnderesDing hat, was genau die gewünschten Eigenschaften erfüllt. Dieses AnderesDing könnte er dann nichtmehr verwenden.</p>
<p>Was schwerer wiegt muss man von Fall zu Fall unterscheiden.</p>
<p>p.S.:<br />
Und natürlich kann es auch Sinn machen das ganze mit klassischer Polymorphie zu mischen. Das sähe in unserem Beispiel dann vielleicht so aus:</p>
<pre><code class="language-cpp">class IVariateGenerator // muss kein reines Interface sein, könnte auch Datenmember
                        // und/oder implementierte Funktionen haben
{
public:
	virtual uint32_t GetNumber(uint32_t upperBound) = 0;
};

template &lt;class Engine&gt;
class VariateGenerator : public IVariateGenerator
{
public:
	explicit VariateGenerator(Engine engine = Engine()) :
		m_engine(engine)
	{
	}

	virtual uint32_t GetNumber(uint32_t upperBound)
	{
		// ...
	}
private:
	Engine m_engine;
};
</code></pre>
<p>Dadurch könnte man verschiedene VariateGenerator an Stellen verwenden wo Runtime-Polymorphismus nötig ist und/oder Sinn macht (z.B. weniger Tempalte-Bloat).<br />
Allerdings lässt sich das auch nachträglich drüberstülpen, in dem man den VariateGenerator&lt;T&gt; nochmal in eine Klasse &quot;einwickelt&quot; die dann von der Interface-Klasse erbt. Ich nehme an dass das auch der Grund ist, warum man solche Konstrukte relativ selten in Libraries findet.</p>
<p>p^2.S.: Im Prinzip ist auch std::string so ein Ding. Die &quot;Engine&quot; wäre hier die char_traits Klasse. Oder auch alle Standard-Container, die als &quot;Engine&quot; einen Allocator verwenden.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1888732</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1888732</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Mon, 26 Apr 2010 23:28:03 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Tue, 27 Apr 2010 21:21:33 GMT]]></title><description><![CDATA[<p>Whoa <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f576.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--sunglasses"
      title=":sunglasses:"
      alt="🕶"
    /> super Beitrag. Jetzt bin ich glaube ich wunschlos glücklich.</p>
<p>hier ist nochmal das Orginalpaper von Veldhuizen: <a href="https://www.cs.indiana.edu/cgi-bin/techreports/TRNNN.cgi?trnum=TR542" rel="nofollow">https://www.cs.indiana.edu/cgi-bin/techreports/TRNNN.cgi?trnum=TR542</a></p>
]]></description><link>https://www.c-plusplus.net/forum/post/1889177</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1889177</guid><dc:creator><![CDATA[SummerOf84]]></dc:creator><pubDate>Tue, 27 Apr 2010 21:21:33 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Tue, 27 Apr 2010 21:47:27 GMT]]></title><description><![CDATA[<p>Muaha <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f603.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--grinning_face_with_big_eyes"
      title=":D"
      alt="😃"
    /> (siehe unten)<br />
Danke für das vollständige Paper!</p>
<p>Jetzt weiss ich auch was mit &quot;Barton and Nackman Trick&quot;, der meint einfach nur CRTP. Klar dass man das in seinem Beispiel mit CRTP vereinfachen kann, und dann macht auch der Begriff &quot;statische Basisklasse&quot; auf einmal etwas Sinn.</p>
<p>Zumindest laut Wikipedia ist der &quot;Barton and Nackman Trick&quot; nämlich mehr als nur einfach CRTP:<br />
<a href="http://en.wikipedia.org/wiki/Barton-Nackman_trick" rel="nofollow">http://en.wikipedia.org/wiki/Barton-Nackman_trick</a></p>
<p>Und wie man DAS auf das in dem Paper beschriebene Problem anwenden sollte, weiss ich wirklich nicht <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f642.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--slightly_smiling_face"
      title=":)"
      alt="🙂"
    /><br />
Wegen muaha: musste nur lachen, weil ich mir mein Hirn zergrübelt habe, und sich jetzt herausstellt, dass wiedermal nur Begriffsverwirrung das Problem war.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1889185</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1889185</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Tue, 27 Apr 2010 21:47:27 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Wed, 28 Apr 2010 07:50:45 GMT]]></title><description><![CDATA[<p>Hi,</p>
<p>nur um klarzuwerden dass ich das auch richtig verstanden haben: Der Vorteil von CRTP ist die Möglichkeit, aus der Basisklasse auf Methoden der Kinder zugreifen zu können (ohne virtuelle Funktionen zu benutzen)?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1889279</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1889279</guid><dc:creator><![CDATA[SummerOf84]]></dc:creator><pubDate>Wed, 28 Apr 2010 07:50:45 GMT</pubDate></item><item><title><![CDATA[Reply to Engine Pattern on Wed, 28 Apr 2010 10:57:54 GMT]]></title><description><![CDATA[<p>Ja, und nicht nur auf Methoden. Wenn du magst auch auf Konstanten, Typedefs etc.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1889376</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1889376</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Wed, 28 Apr 2010 10:57:54 GMT</pubDate></item></channel></rss>