<?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>TURBU Tech &#187; Dark Corners</title>
	<atom:link href="http://tech.turbu-rpg.com/category/delphi/dark-corners/feed" rel="self" type="application/rss+xml" />
	<link>http://tech.turbu-rpg.com</link>
	<description>My thoughts on Delphi programming in general, and particularly on the technical aspects of developing the TURBU engine and editor.</description>
	<lastBuildDate>Fri, 27 Jan 2012 19:53:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Adding boolean support to Firebird+DBX</title>
		<link>http://tech.turbu-rpg.com/324/adding-boolean-support-to-firebirddbx</link>
		<comments>http://tech.turbu-rpg.com/324/adding-boolean-support-to-firebirddbx#comments</comments>
		<pubDate>Sun, 01 May 2011 04:38:14 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>
		<category><![CDATA[RTTI Surgery]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=324</guid>
		<description><![CDATA[Firebird is a great database, but it&#8217;s got one really irritating drawback: no native support for the boolean type.  The standard solution to this issue is to create a BOOLEAN domain as a special restricted version of a smallint, and then make your database driver output the correct type. The first part is easy.  The [...]]]></description>
			<content:encoded><![CDATA[<p>Firebird is a great database, but it&#8217;s got one really irritating drawback: no native support for the boolean type.  The standard solution to this issue is <a href="http://www.firebirdfaq.org/faq12/">to create a BOOLEAN domain</a> as a special restricted version of a smallint, and then make your database driver output the correct type.</p>
<p>The first part is easy.  The second, not so much, if you want to use DBExpress.  This really should be handled internally as a special case inside the DBX driver.  Unfortunately neither Embarcadero nor Chau Chee Yang, maker of the alternative <a href="http://groups.google.com/group/dbxfirebird">dbExpress Firebird</a> driver, has released the source to their drivers, neither driver handles the BOOLEAN domain, and neither driver has any sort of callback/event handler that you can set up to intercept and modify the schema of a query result.  But I&#8217;m not gonna let a little thing like that stop me!<span id="more-324"></span></p>
<p>To know how to fix this, we first need to know what&#8217;s going wrong.  Here&#8217;s the basic overview:</p>
<p>To access database data through DBExpress, you generally use TSimpleDataset.  This is a TCustomClientDataset descendant that contains an internal dataset for DB access called TInternalSQLDataSet.  The outer layer has all the functionality that makes TClientDataset wonderful to work with.  You run a query, and it has the internal dataset access the database and pull back data, which gets stuffed into the client dataset for you to manipulate.</p>
<p>When the internal dataset talks to the database, it runs the query and gets back a record set, which includes data and a schema.  It iterates over the schema and prepares field defs for each column, then creates fields based on the field defs.  If you could catch and modify the field defs before it builds fields from them, you&#8217;d be good, but the whole process happens inside the InternalOpen method, with no conveniently-placed event handlers for you to interpose any code.</p>
<p>However, the InternalInitFieldDefs method, which sets up the field defs, is virtual, so it can be overridden in a descendant class.  Only problem is, we&#8217;re talking about the internal dataset here.  It&#8217;s kind of difficult to replace the internal dataset of your TSimpleDataset, because it&#8217;s a private field.  But the AllocDataSet method, which sets up the internal dataset, is also virtual.  With a little bit of RTTI surgery, we can set a value to a private field.</p>
<p>Things could get really messy at this point.  I could create a new dataset that descends from TSimpleDataset, but then I&#8217;d have to put it in a package, register it on the component palette, etc&#8230; unless I use an <a href="http://groups.google.com/group/borland.public.delphi.language.objectpascal/browse_thread/thread/37c5eb2daa27df6d?tvc=2">interposer class</a>, a component with the same name that&#8217;s hidden from the form designer but gets instantiated instead of the original class at runtime.  So that&#8217;s our starting point.</p>
<p>Here&#8217;s my unit, weighing in at just under 100 lines of code plus the MPL header.  I&#8217;ll explain how it works at the bottom.  This does require Delphi 2010 or greater.</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="coMULTI">{*****************************************************************************
* The contents of this file are used with permission, subject to
* the Mozilla Public License Version 1.1 (the &quot;License&quot;); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
* http://www.mozilla.org/MPL/MPL-1.1.html
*
* Software distributed under the License is distributed on an
* &quot;AS IS&quot; basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
*****************************************************************************
*
* This file was created by Mason Wheeler. &nbsp;He can be reached for support at
* tech.turbu-rpg.com.
*****************************************************************************}</span>
<span class="kw1">unit</span> FirebirdDataset<span class="sy1">;</span>

<span class="kw1">interface</span>
<span class="kw1">uses</span>
&nbsp; &nbsp;SimpleDS<span class="sy1">;</span>

<span class="kw1">type</span>
&nbsp; TSimpleDataset <span class="sy3">=</span> <span class="kw1">class</span><span class="br0">&#40;</span>SimpleDS<span class="sy1">.</span><span class="me1">TSimpleDataset</span><span class="br0">&#41;</span>
&nbsp; <span class="kw1">protected</span>
&nbsp; &nbsp; <span class="kw1">procedure</span> AllocDataSet<span class="sy1">;</span> <span class="kw1">override</span><span class="sy1">;</span>
&nbsp; <span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">implementation</span>
<span class="kw1">uses</span>
&nbsp; Classes<span class="sy1">,</span> SysUtils<span class="sy1">,</span> RTTI<span class="sy1">,</span> DB<span class="sy1">,</span> Provider<span class="sy1">,</span> SqlExpr<span class="sy1">,</span>
&nbsp; DBCommon<span class="sy1">;</span>

<span class="kw1">type</span>
&nbsp; TInternalSQLDataSet <span class="sy3">=</span> <span class="kw1">class</span><span class="br0">&#40;</span>SimpleDS<span class="sy1">.</span><span class="me1">TInternalSQLDataSet</span><span class="br0">&#41;</span>
&nbsp; <span class="kw1">private</span>
&nbsp; &nbsp; FQuery<span class="sy1">:</span> TSqlQuery<span class="sy1">;</span>
&nbsp; <span class="kw1">protected</span>
&nbsp; &nbsp; <span class="kw1">procedure</span> InternalInitFieldDefs<span class="sy1">;</span> <span class="kw1">override</span><span class="sy1">;</span>
&nbsp; <span class="kw1">public</span>
&nbsp; &nbsp; <span class="kw1">constructor</span> Create<span class="br0">&#40;</span>AOwner<span class="sy1">:</span> TComponent<span class="br0">&#41;</span><span class="sy1">;</span> <span class="kw1">override</span><span class="sy1">;</span>
&nbsp; <span class="kw1">end</span><span class="sy1">;</span>

<span class="coMULTI">{ TSimpleDataset }</span>

<span class="kw1">procedure</span> TSimpleDataset<span class="sy1">.</span><span class="me1">AllocDataSet</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; ctx<span class="sy1">:</span> TRttiContext<span class="sy1">;</span>
&nbsp; cls<span class="sy1">:</span> TRttiType<span class="sy1">;</span>
&nbsp; fld<span class="sy1">:</span> TRttiField<span class="sy1">;</span>
&nbsp; FDataset<span class="sy1">:</span> TInternalSQLDataSet<span class="sy1">;</span>
&nbsp; FProvider<span class="sy1">:</span> TDataSetProvider<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; cls <span class="sy1">:</span><span class="sy3">=</span> ctx<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="sy1">.</span><span class="me1">ClassType</span><span class="br0">&#41;</span><span class="sy1">;</span>

&nbsp; fld <span class="sy1">:</span><span class="sy3">=</span> cls<span class="sy1">.</span><span class="me1">GetField</span><span class="br0">&#40;</span><span class="st0">'FDataset'</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; FDataSet <span class="sy1">:</span><span class="sy3">=</span> TInternalSQLDataSet<span class="sy1">.</span><span class="me1">Create</span><span class="br0">&#40;</span><span class="kw2">Self</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; FDataSet<span class="sy1">.</span><span class="me1">Name</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="st0">'InternalDataSet'</span><span class="sy1">;</span>
&nbsp; FDataSet<span class="sy1">.</span><span class="me1">SQLConnection</span> <span class="sy1">:</span><span class="sy3">=</span> Connection<span class="sy1">;</span>
&nbsp; FDataSet<span class="sy1">.</span><span class="me1">SetSubComponent</span><span class="br0">&#40;</span><span class="kw2">True</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; fld<span class="sy1">.</span><span class="me1">SetValue</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="sy1">,</span> FDataset<span class="br0">&#41;</span><span class="sy1">;</span>

&nbsp; fld <span class="sy1">:</span><span class="sy3">=</span> cls<span class="sy1">.</span><span class="me1">GetField</span><span class="br0">&#40;</span><span class="st0">'FProvider'</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; FProvider <span class="sy1">:</span><span class="sy3">=</span> fld<span class="sy1">.</span><span class="me1">GetValue</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsType</span>&lt;TDataSetProvider&gt;<span class="sy1">;</span>
&nbsp; FProvider<span class="sy1">.</span><span class="me1">DataSet</span> <span class="sy1">:</span><span class="sy3">=</span> FDataSet<span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span>

<span class="coMULTI">{ TInternalSQLDataSet }</span>

<span class="kw1">constructor</span> TInternalSQLDataSet<span class="sy1">.</span><span class="me1">Create</span><span class="br0">&#40;</span>AOwner<span class="sy1">:</span> TComponent<span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">const</span>
&nbsp; QUERY <span class="sy3">=</span> <span class="st0">'select rFields.RDB$FIELD_NAME NAME '</span> <span class="sy3">+</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'from &nbsp;RDB$RELATION_FIELDS rFields '</span> <span class="sy3">+</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'join RDB$FIELDS fields ON (rFields.RDB$FIELD_SOURCE = fields.RDB$FIELD_NAME) '</span> <span class="sy3">+</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'where &nbsp;(rFields.RDB$RELATION_NAME=:tablename) and (fields.RDB$FIELD_TYPE = 7) '</span> <span class="sy3">+</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">' &nbsp;and (rFields.RDB$FIELD_SOURCE = '</span><span class="st0">'BOOLEAN'</span><span class="st0">') '</span> <span class="sy3">+</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="st0">'order by rFields.RDB$FIELD_POSITION '</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; <span class="kw1">inherited</span> Create<span class="br0">&#40;</span>AOwner<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; FQuery <span class="sy1">:</span><span class="sy3">=</span> TSqlQuery<span class="sy1">.</span><span class="me1">Create</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; FQuery<span class="sy1">.</span><span class="me1">SQL</span><span class="sy1">.</span><span class="me1">Text</span> <span class="sy1">:</span><span class="sy3">=</span> QUERY<span class="sy1">;</span>
&nbsp; FQuery<span class="sy1">.</span><span class="me1">Params</span><span class="sy1">.</span><span class="me1">AddParameter</span><span class="sy1">.</span><span class="me1">Name</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="st0">'tablename'</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">procedure</span> TInternalSQLDataSet<span class="sy1">.</span><span class="me1">InternalInitFieldDefs</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; def<span class="sy1">:</span> TFieldDef<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; <span class="kw1">inherited</span> InternalInitFieldDefs<span class="sy1">;</span>
&nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="br0">&#40;</span><span class="kw2">self</span><span class="sy1">.</span><span class="me1">CommandType</span> <span class="kw1">in</span> <span class="br0">&#91;</span>ctTable<span class="sy1">,</span> ctQuery<span class="br0">&#93;</span><span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; <span class="kw3">Exit</span><span class="sy1">;</span>

&nbsp; FQuery<span class="sy1">.</span><span class="me1">Active</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; <span class="co1">//Why is TCustomSQLDataSet.SetConnection both virtual and private?!?</span>
&nbsp; FQuery<span class="sy1">.</span><span class="me1">SQLConnection</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">self</span><span class="sy1">.</span><span class="me1">SQLConnection</span><span class="sy1">;</span>
&nbsp; FQuery<span class="sy1">.</span><span class="me1">ParamByName</span><span class="br0">&#40;</span><span class="st0">'tablename'</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsString</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="kw3">UpperCase</span><span class="br0">&#40;</span>GetTableNameFromQuery<span class="br0">&#40;</span><span class="kw2">self</span><span class="sy1">.</span><span class="me1">CommandText</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; FQuery<span class="sy1">.</span><span class="me1">Active</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">true</span><span class="sy1">;</span>
&nbsp; FQuery<span class="sy1">.</span><span class="me1">First</span><span class="sy1">;</span>
&nbsp; FieldDefs<span class="sy1">.</span><span class="me1">BeginUpdate</span><span class="sy1">;</span>
&nbsp; <span class="kw1">try</span>
&nbsp; &nbsp; <span class="kw1">while</span> <span class="kw1">not</span> FQuery<span class="sy1">.</span><span class="kw3">Eof</span> <span class="kw1">do</span>
&nbsp; &nbsp; <span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; def <span class="sy1">:</span><span class="sy3">=</span> TFieldDef<span class="br0">&#40;</span>TDefCollection<span class="br0">&#40;</span>FieldDefs<span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">Find</span><span class="br0">&#40;</span><span class="kw3">Trim</span><span class="br0">&#40;</span>FQuery<span class="sy1">.</span><span class="me1">FieldByName</span><span class="br0">&#40;</span><span class="st0">'NAME'</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsString</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw3">assigned</span><span class="br0">&#40;</span>def<span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;def<span class="sy1">.</span><span class="me1">DataType</span> <span class="sy1">:</span><span class="sy3">=</span> ftBoolean<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; FQuery<span class="sy1">.</span><span class="me1">Next</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">end</span><span class="sy1">;</span>
&nbsp; <span class="kw1">finally</span>
&nbsp; &nbsp; FieldDefs<span class="sy1">.</span><span class="me1">EndUpdate</span><span class="sy1">;</span>
&nbsp; <span class="kw1">end</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">end</span><span class="sy1">.</span></div>
</div>
</pre>
<p>First comes the outer dataset.  It should operate exactly the same way as the original dataset, except with a modified inner dataset.  So I override AllocDataSet and replace it with a copy of the original code, except that it creates the new internal dataset and uses RTTI to access the private fields.  The internal dataset is where the interesting stuff takes place.</p>
<p>You can find out about Firebird domains by querying the internal metadata tables for the database.  That&#8217;s basically the only option available this late in the game, so I gave the new internal dataset an internal dataset of its own, a TSqlQuery which is designed for&#8230; well, you can probably guess.  The constructor creates the TSqlQuery and loads it with a parameterized query that will find any columns in a table that belong to a smallint-based domain named BOOLEAN.</p>
<p>Then, when InternalInitFieldDefs runs, it calls the inherited method, then sees if there&#8217;s anything it needs to fix.  Currently only single-table queries of type ctTable or ctQuery are supported.  If you run a ctQuery with a join on it, it&#8217;ll only get the first table.  Working around this would require building an actual SQL parser into the dataset, and I don&#8217;t really have time for that this evening.</p>
<p>It calls the RTL function DBCommon.GetTableNameFromQuery, which uses a very simple SQL parser to extract the table name. (If your query type is ctTable, it&#8217;ll just return the same table name you passed in.)  It sets the param on the internal TSqlquery and runs it, and then goes over the results. Any fields that get returned get checked against the field defs and their type is switched to ftBoolean.  Then this function returns, and InternalOpen goes on its merry way, setting up boolean fields for you.  Hooray!</p>
<p>But this really should be handled within the driver.  I hope that that can be arranged for the next version. (Of either one.)  Then we won&#8217;t need hacks like this.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/324/adding-boolean-support-to-firebirddbx/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>XE Update 1: you win some, you lose some</title>
		<link>http://tech.turbu-rpg.com/272/yay-weve-got-our-fast-compiler-back</link>
		<comments>http://tech.turbu-rpg.com/272/yay-weve-got-our-fast-compiler-back#comments</comments>
		<pubDate>Tue, 30 Nov 2010 06:15:24 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi XE]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=272</guid>
		<description><![CDATA[In my first look at Delphi XE, I wrote: Oh, and apparently certain aspects of the compiler have slowed down.  Most things will compile about the same speed or even a little faster, but for really large projects (millions of lines) with complex interdependencies between units, you’ll notice some slowdown, and it’ll apparently get worse [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://tech.turbu-rpg.com/181/first-look-at-delphi-xe">first look at Delphi XE</a>, I wrote:</p>
<blockquote><p>Oh, and apparently certain aspects of the compiler have slowed down.   Most things will compile about the same speed or even a little faster,  but for really large projects (millions of lines) with complex  interdependencies between units, you’ll notice some slowdown, and it’ll  apparently get worse the larger your project is.  I timed it at work, on  a project of about 3.5 million lines of code.  It builds in about 2  minutes on D2010, closer to 3 minutes on XE.  <strong>Bad,</strong> but  still a heck of a lot better than the C family could do.  And apparently  it has something to do with making Generics work right, so I can  tolerate that.  Hopefully they’ll find some way to regain some of that  lost speed in updates, though.</p></blockquote>
<p>Well, I finally got around to installing XE Update 1 at work today, and I timed the build.  2:13.<span id="more-272"></span></p>
<p>Unfortunately, one of my coworkers stumbled across a nasty new edge case that didn&#8217;t exist in XE RTM.  If you open a very large project, (the one at work is millions of lines of code, with thousands of units used in the DPR,) pull up the DPR, and do something that produces an error dialog, such as trying to Find an invalid text snippet, before compiling, the IDE locks up on you, burning one core at 100% until you kill it.</p>
<p>I opened a second instance of Delphi and attached the debugger and got some interesting results.  I don&#8217;t have Andreas Hausladen&#8217;s IDE-spelunking brilliance, but apparently the main thread is waiting on something to finish, and if I had to guess, I&#8217;d say that it&#8217;s running a background compile to build *Insight tables.</p>
<p>I submitted a QC report on this, but something went wrong somewhere and it&#8217;s not publicly viewable.  I put a comment on the report to that effect; hopefully someone at Emb. will change it.  So if you can see <a href="http://qc.embarcadero.com/wc/qcmain.aspx?d=90020">QC# 90020,</a> feel free to vote for it.</p>
<p>So Congrats to Barry and the compiler team for getting the compile time back down on par with D2010, and a big raspberry to whoever introduced <em>yet another</em> bit of behavior that blocks the main thread for a perceptible length of time.  That&#8217;s something that should never happen. It&#8217;s gotten better recently, but it still happens far too often in the IDE.  Hopefully the next update will fix this issue.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/272/yay-weve-got-our-fast-compiler-back/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Beware using anonymous methods in loops</title>
		<link>http://tech.turbu-rpg.com/256/beware-using-anonymous-methods-in-loops</link>
		<comments>http://tech.turbu-rpg.com/256/beware-using-anonymous-methods-in-loops#comments</comments>
		<pubDate>Mon, 22 Nov 2010 03:15:30 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Anonymous Methods]]></category>
		<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=256</guid>
		<description><![CDATA[Quick, what&#8217;s the output of this simple routine? procedure AnonLoop; var &#160; i: integer; &#160; proc: TProc; &#160; ProcList: TList&#60;TProc&#62;; begin &#160; ProcList := TList&#60;TProc&#62;.Create; &#160; for i := 1 to 5 do &#160; &#160; ProcList.Add&#40; &#160; &#160; &#160; procedure&#40;&#41; &#160; &#160; &#160; begin &#160; &#160; &#160; &#160; write&#40;i, ' '&#41;; &#160; &#160; &#160; end&#41;; [...]]]></description>
			<content:encoded><![CDATA[<p>Quick, what&#8217;s the output of this simple routine?<br />
<span id="more-256"></span></p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">procedure</span> AnonLoop<span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; i<span class="sy1">:</span> <span class="kw4">integer</span><span class="sy1">;</span>
&nbsp; proc<span class="sy1">:</span> TProc<span class="sy1">;</span>
&nbsp; ProcList<span class="sy1">:</span> TList&lt;TProc&gt;<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; ProcList <span class="sy1">:</span><span class="sy3">=</span> TList&lt;TProc&gt;<span class="sy1">.</span><span class="me1">Create</span><span class="sy1">;</span>
&nbsp; <span class="kw1">for</span> i <span class="sy1">:</span><span class="sy3">=</span> 1 <span class="kw1">to</span> 5 <span class="kw1">do</span>
&nbsp; &nbsp; ProcList<span class="sy1">.</span><span class="me1">Add</span><span class="br0">&#40;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span><span class="br0">&#40;</span><span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">write</span><span class="br0">&#40;</span>i<span class="sy1">,</span> <span class="st0">' '</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><span class="br0">&#41;</span><span class="sy1">;</span>

&nbsp; <span class="kw1">for</span> proc <span class="kw1">in</span> ProcList <span class="kw1">do</span>
&nbsp; &nbsp; proc<span class="sy1">;</span>
&nbsp; procList<span class="sy1">.</span><span class="me1">Free</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>Intuitively, you&#8217;d think it would write &#8220;1 2 3 4 5&#8243;, but you&#8217;d be wrong.  It actually prints out &#8220;6 6 6 6 6&#8243;.  Huh?  6s?  You never fed it a 6!  And why are they all the same?  Well, it has to do with the way anonymous methods work.  Take a look at <a href="http://tech.turbu-rpg.com/13/whats-in-a-name-less-method">my analysis from a while back</a> and see if it becomes any clearer.</p>
<p>Basically, you aren&#8217;t creating a new anonymous method each time you go through the loop.  The functor object is created one time only, on the <strong>begin</strong> line of the enclosing method, not once each time you hit the declaration in the code.</p>
<p>This is a bit disorienting, but technically necessary; if there could be more than one instance of the functor object created each time you run the enclosing method, it would be impossible to support multiple anonymous methods enclosing the same local variables, among other gotchas.</p>
<p>So what to do about it?  Seems to me that we have two problems.  First, you can&#8217;t create multiple anonymous methods by putting a declaration in a loop.  Second, the compiler hides this issue from you by allowing the anonymous method to capture the index variable of a <strong>for</strong> loop, which almost certainly results in silently generating bad code.</p>
<p>To create multiple anonymous methods in a loop, you need to call another function that returns an anonymous method.  This can make things a bit difficult, especially since it negates (or at least lessens) the ability of the anonymous method to capture local variables, which is what makes it so useful.</p>
<p>The compiler already can&#8217;t capture Result and <strong>var</strong>/<strong>out</strong> parameters, because they don&#8217;t belong to the current procedure.  Seems to me that the next version of the Delphi compiler should similarly check for this, and emit an error if an anonymous method tries to capture the index variable of a <strong>for</strong> loop, because that almost certainly doesn&#8217;t belong to the anonymous method.</p>
<p>BTW I know Barry Kelly reads at least some of my posts, because he occasionally responds with comments here when I write about technical issues.  Barry, if you see this, would you mind weighing in on the problem?  Thanks.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/256/beware-using-anonymous-methods-in-loops/feed</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>TStringList updating pitfalls</title>
		<link>http://tech.turbu-rpg.com/231/tstringlist-updating-pitfalls</link>
		<comments>http://tech.turbu-rpg.com/231/tstringlist-updating-pitfalls#comments</comments>
		<pubDate>Tue, 19 Oct 2010 05:55:47 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Optimization]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=231</guid>
		<description><![CDATA[What&#8217;s wrong with this code? procedure TMyCustomChecklistPopupControl.ClosePopup; var &#160; i: integer; begin &#160; inherited ClosePopup; &#160; FInternalItemStringList.Clear; &#160; for i := 0 to Self.CheckedCount - 1 do &#160; &#160; FInternalItemStringList.Add&#40;Self.CheckedItems&#91;i&#93;.Name&#41;; end; At first glance, it looks just fine. It&#8217;s semantically correct&#8211;it will do what you want it to. If you happen to have seen a [...]]]></description>
			<content:encoded><![CDATA[<p>What&#8217;s wrong with this code?</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">procedure</span> TMyCustomChecklistPopupControl<span class="sy1">.</span><span class="me1">ClosePopup</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; i<span class="sy1">:</span> <span class="kw4">integer</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; <span class="kw1">inherited</span> ClosePopup<span class="sy1">;</span>
&nbsp; FInternalItemStringList<span class="sy1">.</span><span class="me1">Clear</span><span class="sy1">;</span>
&nbsp; <span class="kw1">for</span> i <span class="sy1">:</span><span class="sy3">=</span> 0 <span class="kw1">to</span> <span class="kw2">Self</span><span class="sy1">.</span><span class="me1">CheckedCount</span> <span class="sy3">-</span> 1 <span class="kw1">do</span>
&nbsp; &nbsp; FInternalItemStringList<span class="sy1">.</span><span class="me1">Add</span><span class="br0">&#40;</span><span class="kw2">Self</span><span class="sy1">.</span><span class="me1">CheckedItems</span><span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="sy1">.</span><span class="me1">Name</span><span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p><span id="more-231"></span>At first glance, it looks just fine.  It&#8217;s semantically correct&#8211;it will do what you want it to.  If you happen to have seen a certain issue before, something might jump out at you, but if not, you probably think this is OK.  And most of the time, it is.</p>
<p>This is a simplified version of something I ran into at work today, in one of our custom controls.  I ran into it in the debugger, but not because it was raising exceptions or corrupting data.  No, the problem was that when I hit the Check All button, selecting all 200 or so items, and then closed the popup, it took left the UI unresponsive for a good 15 seconds or so.</p>
<p>Turns out the problem isn&#8217;t in what this code was written to do, but in what else it does.  You see, there&#8217;s an OnUpdate event handler attached to the internal <span style="text-decoration: line-through;">TSwissArmyKnife</span> TStringList which goes over the data in the list, calculates a few things, and updates some UI elements.  And yeah, you want that to happen when you make a change.  But you want it to happen once per change, from the user&#8217;s perspective.  This was happening once per change from the TStringList&#8217;s perspective, or in other words, 200+ times in total for a single user action.  And it took forever to finish.</p>
<p>You can be a really good programmer and still not know all the ins and outs of the framework you&#8217;re working with.  I&#8217;m always discovering new little details about how things work.  Turns out I&#8217;ve seen this one before, so when I hit Pause a few seconds in and dropped to the debugger, and saw the following right in the middle of the call stack, I knew what was going on right away.</p>
<pre>TStringList.Changed
TStringList.InsertItem
TStringList.AddObject
TStringList.Add</pre>
<p>What whoever coded this control apparently didn&#8217;t know, probably because they&#8217;d just never run across it before, was that Borland anticipated this very problem&#8211;or more liklely, because so many VCL classes use TStrings descendantes internally, they ran into it themselves at one point&#8211;and put a little switch into TStrings to turn off the OnChanged event handler temporarily.</p>
<p>Once I surrounded this code with a BeginUpdate and EndUpdate pair, the delay on closing up the box went from an angonizing 15 seconds to a tiny fraction of a second that I wouldn&#8217;t have noticed at all if I wasn&#8217;t watching for it.</p>
<p>Hopefully most of the people reading this are familiar with <a href="http://docwiki.embarcadero.com/VCL/en/Classes.TStrings.BeginUpdate">BeginUpdate</a> and <a href="http://docwiki.embarcadero.com/VCL/XE/en/Classes.TStrings.EndUpdate">EndUpdate</a>.  But if anyone who hasn&#8217;t seen it runs across this, now you have a new trick.  Please make sure to use it, to spare your end-users some pain.  Even if you don&#8217;t think it&#8217;s likely to be necessary, please use it anyway.  When this special checklist control was originally written, years ago, it was intended to hold a dozen or so items at most, not hundreds, and it probably performed fine at that scale.  But growing client demand means the app&#8217;s working with more data than it used to, and eventually you hit something like this unless you&#8217;re careful in your design.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/231/tstringlist-updating-pitfalls/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Little things Delphi gets right</title>
		<link>http://tech.turbu-rpg.com/205/little-things-delphi-gets-right</link>
		<comments>http://tech.turbu-rpg.com/205/little-things-delphi-gets-right#comments</comments>
		<pubDate>Mon, 04 Oct 2010 23:10:32 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[StackOverflow]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=205</guid>
		<description><![CDATA[For those who haven&#8217;t seen it yet, due to popular demand, the StackOverflow people created a new site called programmers.stackexchange.com, a site for the more subjective questions that StackOverflow isn&#8217;t really designed for.  Someone recently set up a poll: What&#8217;s your favorite programming language. You can probably guess what my answer was. Delphi It&#8217;s the [...]]]></description>
			<content:encoded><![CDATA[<p>For those who haven&#8217;t seen it yet, due to popular demand, the StackOverflow people created a new site called <a href="http://programmers.stackexchange.com">programmers.stackexchange.com</a>, a site for the more subjective questions that StackOverflow isn&#8217;t really designed for.  Someone recently set up a poll: <a href="http://programmers.stackexchange.com/questions/6847/whats-your-favorite-programming-language">What&#8217;s your favorite programming language.</a> You can probably guess what my answer was.<span id="more-205"></span></p>
<blockquote>
<h2>Delphi</h2>
<p>It&#8217;s the standard, imperative, object-oriented paradigm that most coders are familiar with, but it gets all the little details <em>right</em> that the C family always gets wrong.  Plus the more recent versions  have started to add support for mixing in functional programming  concepts, without running into the ugly abstraction inversions that  typically come with functional languages.</p></blockquote>
<p>I&#8217;ve gotten some interesting replies to that.  One of them asked, &#8220;to justify the C-bashing (so they don&#8217;t clobber you!), and to satisfy my  curiosity, what are the little details Delphi/Pascal gets right?&#8221;</p>
<p>Well, I&#8217;d answer that in a comment, but there isn&#8217;t enough space in 500 characters to do it justice, so I figured I&#8217;d write up my answer here.  Let&#8217;s start with the elephant in the room.</p>
<h1 style="text-align: center;"><strong>SECURITY!!!</strong></h1>
<p>There is no good reason why anyone should <em>ever under any circumstances</em> write a networked program with security requirements&#8211;such as an operating system or a Web browser&#8211;in C, C++ or Objective-C.  We&#8217;ve known that for 22 years now, ever since Robert Tappan Morris released the Internet Worm that used buffer overflow exploits in Unix to crash about 10% of the Internet, causing tens of millions of dollars in damage.  (It was a very small Internet at the time.  Today, the cost would be measured in hundreds of billions, and it probably could be, if not more, if you add up all the damages lost due to buffer overflow and other C-specific vulnerabilities exploited over the last couple decades.)</p>
<p>This should have been a major wake-up call.  <em>You cannot write programs that require reliable security in a language that was designed with no thought to it!</em> At least not with any degree of consistency.  Just look at how many patches are still being issued today for Windows, Linux, OSX, iOS, and various Internet services and Web browsers, all due to buffer overflows.  And it&#8217;s getting worse.  We&#8217;ve got a lot of people talking about computerizing the power grid, which makes a lot of sense in theory, <a href="http://www.smartplanet.com/people/blog/pure-genius/lockheed-martin-on-the-smart-grid-440-million-new-hackable-points/4644/">but it&#8217;s likely to open up a few hundred million new vulnerabilities to terrorists both domestic and foreign.</a> (Who all&#8217;s seen Live Free Or Die Hard?  Anyone want to live in that world, except without Bruce Willis and that &#8220;I&#8217;m a Mac&#8221; kid to conveniently step in and save the day?)</p>
<p>It&#8217;s the same old issue over and over and <em>over again</em>.  It keeps showing up in C programs because it simply can&#8217;t be solved in C without breaking backwards compatibility.  In any sane world, the C language would have been dead by 1989&#8211;the Morris Worm having shown it to be utterly unsuitable for its intended purpose: building operating systems&#8211;and all our computers would be safer for it.  And it&#8217;s not like there are no alternatives.  By the time the worm hit, Apple had been building the most advanced operating system of its day for several years already, in Pascal.  Continuing to write Internet-facing OSes, browsers and apps in C (or C++ or Objective-C) ought to be treated as an act of criminal negligence.</p>
<p>In Delphi, on the other hand, we have a real string type, the best-thought-out string type I&#8217;ve seen in any language.  It&#8217;s reference-counted and grows and resizes automatically as needed, which frees the coder from dealing with string-size and string-memory hassles.  It&#8217;s bounds-checked, and it <strong>does not live on the stack</strong>, so there&#8217;s no way to use a Delphi string buffer overflow for a stack-smashing exploit.  Likewise, for non-string data, Delphi has a real array type which is also bounds-checked.  In fact, we&#8217;ve got two real array types, both bounds-checked, and the one whose size is not fixed (the more dangerous kind) also does not live on the stack.  We&#8217;ve also got a string-format routine that doesn&#8217;t use <strong>varargs</strong>, and string-output code that doesn&#8217;t assume its input is a format string in the first place, which means that Delphi programs are immune to <a href="http://en.wikipedia.org/wiki/Format_string_attack">format string exploits</a>.</p>
<p>In the interest of fairness, of course, I should point out that as with any language that supports pointers, it&#8217;s still possible to write unsafe code in Delphi.  But you have to really go out of your way to do it; it&#8217;s not the default state of the language, the way it is in C!  (And removing pointers from a language causes more problems than it fixes.  This is why Java and C# both have explicit &#8220;unsafe&#8221; features: because they&#8217;re necessary to actually accomplish important tasks.)</p>
<h2 style="text-align: center;"><strong>Syntax and semantics</strong></h2>
<p>OK, enough ranting about security.  Let&#8217;s move on to other things.  A lot of C&#8217;s security problems have been fixed by managed languages like C# and Java.  But what they can&#8217;t fix is the syntax, at least not without abandoning a great deal of their C roots, which is a very important marketing device.  For example, C has no boolean type.  Some of its descendants do, but they haven&#8217;t managed to escape the ramifications of this blunder: because C has no boolean type, anything can be treated as a boolean.</p>
<p>I heard a really horrible pun a while ago:  <a href="http://tvtropes.org/pmwiki/pmwiki.php/Main/Portal">The cake may be a lie</a>, but Pi is always True.  (Because it&#8217;s a nonzero number, and in C, anything can be treated as a boolean.)  When everything is a boolean, including an assignment operation, it&#8217;s not safe to write &#8220;if x = 5&#8243;, no matter how intuitive that looks.  And when everything is a boolean, including a number, if you try to <strong>and</strong> two expressions, the compiler doesn&#8217;t know if you mean a logical or a bitwise <strong>and</strong>, so you need two versions of all the boolean operators.  And if you get them wrong, it might work, or you might end up with some very hard-to-debug issues.</p>
<p>Java, JavaScript and C# still have double versions of all the operators.  And I know the &#8220;if x = 5&#8243; bug still exists in JavaScript.  (I&#8217;ve heard the compiler doesn&#8217;t accept it in C#.  Not sure about Java.)</p>
<p>In Delphi, a boolean is a boolean, and a number (or a string or an object) is not.  This means that we have one <strong>and</strong>, one <strong>or</strong>, one <strong>xor</strong> and one <strong>not</strong>, and the compiler knows what to do with them by looking at the operands.  And if you try to do something nonsensical like <strong>and</strong>ing a boolean and a number, the compiler throws an error instead of silently accepting it and generating nonsensical code.</p>
<p>And while we&#8217;re on the subject of operators, can anyone tell me what * or &amp; do in C?  &#8220;Well, it depends on whether&#8211;&#8221; Oh, I see.  Fundamental syntactic elements whose meanings are context sensitive.  How lovely.  In Delphi, &#8220;a * b&#8221; means multiplication and nothing else.  And for addressing and dereferencing, we&#8217;ve got the @ and ^ symbols, which actually make sense mnemonically.</p>
<p>Then we&#8217;ve got object-oriented programming.  C++&#8217;s object model is a big mess.  There&#8217;s no base object class, which means that there&#8217;s no way to pass an object of arbitrary type between one routine and another.  This also means that there&#8217;s no standardized way to take an object and get RTTI information about it.  And objects are value types, declared by default on the stack (or inline in the larger object that contains them), and passed around by value by default.  This wreaks havoc on inheritance and polymorphism.</p>
<p>For example, what&#8217;s the output of this program?  And if you change the signature of Foo to pass the object by reference, does it alter the output of the program?</p>
<pre>
<div class="codesnip-container" >
<div class="c codesnip" style="font-family:monospace;"><span class="co2">#include &lt;iostream&gt;</span>

class Parent
<span class="br0">&#123;</span>
public<span class="sy0">:</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> a<span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> b<span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> c<span class="sy0">;</span>

&nbsp; &nbsp; &nbsp; &nbsp; Parent<span class="br0">&#40;</span><span class="kw4">int</span> ia<span class="sy0">,</span> <span class="kw4">int</span> ib<span class="sy0">,</span> <span class="kw4">int</span> ic<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a <span class="sy0">=</span> ia<span class="sy0">;</span> b <span class="sy0">=</span> ib<span class="sy0">;</span> c <span class="sy0">=</span> ic<span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">;</span>

&nbsp; &nbsp; &nbsp; &nbsp; virtual <span class="kw4">void</span> doSomething<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy0">::</span><a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span class="kw3">cout</span></a> <span class="sy0">&lt;&lt;</span> <span class="st0">&quot;Parent doSomething&quot;</span> <span class="sy0">&lt;&lt;</span> std<span class="sy0">::</span><span class="me2">endl</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span><span class="sy0">;</span>

class Child <span class="sy0">:</span> public Parent <span class="br0">&#123;</span>
public<span class="sy0">:</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> d<span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">int</span> e<span class="sy0">;</span>

&nbsp; &nbsp; &nbsp; &nbsp; Child<span class="br0">&#40;</span><span class="kw4">int</span> id<span class="sy0">,</span> <span class="kw4">int</span> ie<span class="br0">&#41;</span> <span class="sy0">:</span> Parent<span class="br0">&#40;</span>1<span class="sy0">,</span>2<span class="sy0">,</span>3<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; d <span class="sy0">=</span> id<span class="sy0">;</span> e <span class="sy0">=</span> ie<span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; virtual <span class="kw4">void</span> doSomething<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; std<span class="sy0">::</span><a href="http://www.opengroup.org/onlinepubs/009695399/functions/cout.html"><span class="kw3">cout</span></a> <span class="sy0">&lt;&lt;</span> <span class="st0">&quot;Child doSomething : D = &quot;</span> <span class="sy0">&lt;&lt;</span> d <span class="sy0">&lt;&lt;</span> std<span class="sy0">::</span><span class="me2">endl</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#125;</span>
<span class="br0">&#125;</span><span class="sy0">;</span>

<span class="kw4">void</span> foo<span class="br0">&#40;</span>Parent a<span class="br0">&#41;</span> <span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; a.<span class="me1">doSomething</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>

<span class="kw4">int</span> main<span class="br0">&#40;</span><span class="kw4">void</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
&nbsp; &nbsp; &nbsp; &nbsp; Child c<span class="br0">&#40;</span>4<span class="sy0">,</span> 5<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; foo<span class="br0">&#40;</span>c<span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="nu0">0</span><span class="sy0">;</span>
<span class="br0">&#125;</span></div>
</div>
</pre>
<p>If you have to stop and reason about it for any length of time, that&#8217;s a warning sign.  I asked our local C++ expert at work what this would do, and he thought about it for a few minutes, came to a logical-sounding conclusion about what he thought it <em>should</em> have to do.  Then he wrote up the code above to test it, just to be sure.  (This is a guy who&#8217;s been writing C++ professionally since I was in high school, and he&#8217;s really good at it.  But even with all that experience, he&#8217;s not experienced enough to be confident what it would do without testing it.)</p>
<p>In Delphi, there aren&#8217;t a bunch of arcane passing and copying rules to keep track of when you&#8217;re working with polymorphism.  Objects are always reference types, so when you pass an object to a function, it passes that object, and when you call a virtual method on an object, it calls that object&#8217;s class&#8217;s version.  Always.</p>
<h2 style="text-align: center;"><strong>External code</strong></h2>
<p style="text-align: left;">There are at least technical reasons that can explain a lot of the above issues.  But here&#8217;s something really bizarre that I&#8217;ve never heard a good explanation for.  I had to debug a C DLL that one of my Delphi programs calls into a while ago to fix some problems in it.  I opened it up in Visual Studio and tried to get it to build.  Everything was syntactically correct, and it compiled just fine&#8230; and then failed at the link phase, because it couldn&#8217;t find the .lib file for a second DLL that this DLL requires.</p>
<p style="text-align: left;">.lib file?  What in the name of Turing is a .lib file?!?  Turns out it&#8217;s a file that describes&#8230; something&#8230; about the other DLL so that the linker can hook&#8230; something&#8230; up properly.  I really have no clue what it is or why it&#8217;s necessary.  I&#8217;ve never had to deal with them before.  In Delphi, if you need to link to a DLL, you put a function header in the code, declare that it&#8217;s an external reference, and provide the name of the DLL it&#8217;s found in, and that&#8217;s it.</p>
<p style="text-align: left;">The C code had all the same information: there was a .h file containing the function headers and&#8230; ohhhh, wait.  Now I see what&#8217;s going on!  That&#8217;s actually the exact same .h file that&#8217;s in the DLL I&#8217;m linking against. So it doesn&#8217;t specify that these functions are external references, or where they&#8217;re found.  That information, even though it&#8217;s an important part of your source code, needs to be provided in a .lib file, a binary blob generated by the compiler of the external DLL, that&#8217;s not human-editable and not version-control friendly.  (And if your external DLL wasn&#8217;t written in a C family language, you&#8217;re in for even more fun trying to generate a .lib file.)</p>
<p style="text-align: left;">What it all boils down to is that, for some bizarre reason, Delphi does a more hassle-free job of linking to C DLLs than C does.  That doesn&#8217;t even make any sense, but it&#8217;s true.  Delphi can talk to external C code better than C can!</p>
<p style="text-align: left;">I could go on, (I haven&#8217;t even mentioned templates yet!) but this post is getting long enough already.  But I think the facts speak for themselves.  By paying attention to little details like the things I&#8217;ve mentioned here and thinking through the ramifications, the Delphi language designers have managed to build a language that is easier to work with and easier to write correct code in.  I hope this clarifies what I meant when I posted that.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/205/little-things-delphi-gets-right/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>RTTI errors and omissions in Delphi XE</title>
		<link>http://tech.turbu-rpg.com/194/rtti-errors-and-omissions-in-delphi-xe</link>
		<comments>http://tech.turbu-rpg.com/194/rtti-errors-and-omissions-in-delphi-xe#comments</comments>
		<pubDate>Fri, 24 Sep 2010 00:22:42 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi XE]]></category>
		<category><![CDATA[RTTI]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=194</guid>
		<description><![CDATA[I&#8217;ve been playing around with DeHL a lot lately.  Embarcadero&#8217;s own Alex Ciobanu wrote this library, which provides a lot of useful functionality, including a set of querying methods that, while a bit bulky, are probably the best we&#8217;re gonna get until the compiler team gets around to implementing LINQ as a language feature.  (Which [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been playing around with <a href="http://code.google.com/p/delphilhlplib/">DeHL</a> a lot lately.  Embarcadero&#8217;s own Alex Ciobanu wrote this library, which provides a lot of useful functionality, including a set of querying methods that, while a bit bulky, are probably the best we&#8217;re gonna get until the compiler team gets around to implementing LINQ as a language feature.  (Which I really hope they will do.)<span id="more-194"></span></p>
<p>It uses a lot of generic interfaced collections to pull this off without making a huge mess of the memory management.  This is particularly interesting because during the keynote at Delphi Live, someone (I think it was Mike Rozlog, but I&#8217;m not certain&#8211;where are the videos I saw some people taking?) said that they&#8217;re working on a more modern VCL.  I asked what they meant by that, and one of the answers was more interfaces in more places.</p>
<p>Between that and a lot of other things they&#8217;ve done recently, it seems like they want to bring the VCL to parity with the .NET framework.  But we&#8217;ve got a lot of problems with the interfaces, especially when you bring RTTI into it.  For example, a bit of fun I discovered yesterday.  What would you expect this program to output?</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">program</span> Project3<span class="sy1">;</span>

<span class="co2">{$APPTYPE CONSOLE}</span>

<span class="kw1">uses</span>
&nbsp; SysUtils<span class="sy1">,</span> RTTI<span class="sy1">,</span>
&nbsp; dehl<span class="sy1">.</span><span class="me1">Collections</span><span class="sy1">.</span><span class="me1">base</span><span class="sy1">;</span>

<span class="kw1">type</span>
&nbsp; &nbsp;IntList <span class="sy3">=</span> IList&lt;integer&gt;<span class="sy1">;</span>

&nbsp; &nbsp;TMyObject <span class="sy3">=</span> <span class="kw1">class</span>
&nbsp; &nbsp;<span class="kw1">private</span>
&nbsp; &nbsp; &nbsp; FName<span class="sy1">:</span> <span class="kw4">string</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; FNumbers<span class="sy1">:</span> IntList<span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">procedure</span> Test<span class="sy1">;</span>
<span class="kw1">const</span>
&nbsp; &nbsp;FIELD_STRING <span class="sy3">=</span> <span class="st0">'Field: %s, Type: %s'</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;ctx<span class="sy1">:</span> TRttiContext<span class="sy1">;</span>
&nbsp; &nbsp;field<span class="sy1">:</span> TRttiField<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;ctx <span class="sy1">:</span><span class="sy3">=</span> TRttiContext<span class="sy1">.</span><span class="me1">Create</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">for</span> field <span class="kw1">in</span> ctx<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span>TMyObject<span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">GetFields</span> <span class="kw1">do</span>
&nbsp; &nbsp; &nbsp; <span class="kw3">writeln</span><span class="br0">&#40;</span><span class="kw3">format</span><span class="br0">&#40;</span>FIELD_STRING<span class="sy1">,</span> <span class="br0">&#91;</span>field<span class="sy1">.</span><span class="me1">Name</span><span class="sy1">,</span> field<span class="sy1">.</span><span class="me1">FieldType</span><span class="sy1">.</span><span class="me1">Name</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">begin</span>
&nbsp; <span class="kw1">try</span>
&nbsp; &nbsp; <span class="coMULTI">{ TODO -oUser -cConsole Main : Insert code here }</span>
&nbsp; &nbsp; test<span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw3">readln</span><span class="sy1">;</span>
&nbsp; <span class="kw1">except</span>
&nbsp; &nbsp; <span class="kw1">on</span> E<span class="sy1">:</span> Exception <span class="kw1">do</span>
&nbsp; &nbsp; &nbsp; <span class="kw3">Writeln</span><span class="br0">&#40;</span>E<span class="sy1">.</span><span class="me1">ClassName</span><span class="sy1">,</span> <span class="st0">': '</span><span class="sy1">,</span> E<span class="sy1">.</span><span class="me1">Message</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; <span class="kw1">end</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">.</span></div>
</div>
</pre>
<p>You&#8217;d expect to see:</p>
<pre>Field: FName, Type: string
Field: FNumbers, Type: IntList</pre>
<p>Or maybe, IList&lt;integer&gt; as the type for FNumbers.  But do you know what you do get?  On both D2010 and Delphi XE, you get an access violation, since the compiler doesn&#8217;t generate RTTI for generic interfaces, so field.FieldType returns <strong>nil</strong>.</p>
<p>I had really hoped that that would be fixed in XE.  It&#8217;s disappointing to see that it hasn&#8217;t been.  The compiler needs to be able to generate RTTI for every valid type and member in the language.  (For even more fun, try using RTTI to discover information on the members of an interface, even simple ones like IInterface.  Go ahead, see what you come up with.)  We need full RTTI support for all generics, for indexed properties, and for odd types such as sets larger than 32 bits or non-contiguous enumerations.  Without this, it just feels like a crippled feature that&#8217;s just waiting to trip over something and throw an access violation in code that ought to work.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/194/rtti-errors-and-omissions-in-delphi-xe/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Inheritance baggage</title>
		<link>http://tech.turbu-rpg.com/147/inheritance-baggage</link>
		<comments>http://tech.turbu-rpg.com/147/inheritance-baggage#comments</comments>
		<pubDate>Mon, 21 Jun 2010 15:03:25 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Optimization]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=147</guid>
		<description><![CDATA[A couple posts ago, I mentioned that I&#8217;ve been working with code generation lately.  This is for a part of the TURBU project.  An RPG relies pretty heavily on scripting, and RPG Maker, the system I created TURBU to replace, has a fairly extensive, if limited, scripting system.  The limitations were one of the things [...]]]></description>
			<content:encoded><![CDATA[<p>A couple posts ago, I mentioned that I&#8217;ve been working with code generation lately.  This is for a part of the TURBU project.  An RPG relies pretty heavily on scripting, and RPG Maker, the system I created TURBU to replace, has a fairly extensive, if limited, scripting system.  The limitations were one of the things that made me say &#8220;I could do better than this,&#8221; in fact:  No functions, no local variables, callable procedures exist but parameters don&#8217;t, so any &#8220;passing&#8221; has to be done in global variables, only two data types: integer and boolean, no event handlers, minimal looping support, etc.</p>
<p><span id="more-147"></span></p>
<p>The upside of all this, though, is a very simple scripting system that doesn&#8217;t look much like a programming language, with a simple interface that almost anyone can pick up.  I wanted to keep that simplicity as much as possible, while adding the full flexibility and power of a real scripting language.  So I dreamed up EventBuilder, a set of objects which represent a high-level scripting interface and can also express themselves as <a href="http://www.remobjects.com/ps.aspx">PascalScript</a> code.</p>
<p>I needed some way to create EventBuilder objects that could form a hierarchical tree that can represent blocks of code.  They needed to be easily serializable to some human-readable format so people can copy and paste blocks of EventBuilder script in order to share scripts, ask for help with debugging, etc.  And it needed to be ready quickly, since I want to be able to present as much of this as possible at Delphi Live! in August.</p>
<p>So is there any pre-existing system that supports hierarchical trees of objects and easy serialization to a simple text-based format?  The answer should be obvious to any experienced Delphi user:  descend from TComponent and use its built-in serialization to &#8220;DFM format.&#8221;  I tried that and, once I&#8217;d figured out how to handle a few quirks related to object ownership, it worked great!  All the infrastructure was there for me, tested and tried and proven over the last 15 years, and I could focus on the actual Event Builder logic.  It&#8217;s taken me about a month to get the system to a workable state, and now it&#8217;s more or less all ready.</p>
<p>Then I tried running a very, very large RPG Maker project through my project importer, and it took a long time on converting the global script block.  That&#8217;s sort of to be expected, since there are almost 2000 event scripts in there, but even so it felt like it was taking far too long for the amount of work involved.  I looked at my code and couldn&#8217;t find any obvious issues, so I ran it through <a href="http://delphitools.info/samplingprofiler/">Sampling Profiler</a>.</p>
<p>It&#8217;s a good thing I did, too.  It found a very clear bottleneck in a place I&#8217;d have never thought to look.  Apparently I was spending 77% of my time in TComponent.Notification.  And why would I have never thought to look there?  Because I&#8217;ve never heard of it!  But apparently every time I added a component, it would recursively call this on the entire subtree, turning what ought to have been a O(n) conversion into O(n^2).</p>
<p>With a bit of research, it turns out that TComponent.Notification is for dealing with linked components.  For example, when you link a TDataset to a TDatasource, it needs a notification mechanism so it can clean up references if you free one of them.  Since EventBuilder doesn&#8217;t use linked components, I didn&#8217;t really need this functionality.  Good thing TComponent.Notification is virtual!  I overrode it with a blank method, and suddenly the conversion time dropped from about 12 seconds to about 3 seconds, and everything&#8217;s running smoothly again.</p>
<p>Moral of the story?  Be careful that you understand what you&#8217;re inheriting from, otherwise you might end up with <a href="http://www.snopes.com/humor/nonsense/kangaroo.asp">killer kangaroos</a> or other unwanted features.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/147/inheritance-baggage/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Adding non-data fields to a client dataset</title>
		<link>http://tech.turbu-rpg.com/122/adding-non-data-fields-to-a-client-dataset</link>
		<comments>http://tech.turbu-rpg.com/122/adding-non-data-fields-to-a-client-dataset#comments</comments>
		<pubDate>Wed, 21 Apr 2010 18:56:08 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>
		<category><![CDATA[RTTI Surgery]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=122</guid>
		<description><![CDATA[A lot of the UI design for the TURBU editor is based on data-aware controls bound to client datasets.  I was trying to build a new form this morning that required me to filter one of the datasets.  Problem is, that would break other things that expected it not to be filtered.  Well, that&#8217;s not [...]]]></description>
			<content:encoded><![CDATA[<p>A lot of the UI design for the TURBU editor is based on data-aware controls bound to client datasets.  I was trying to build a new form this morning that required me to filter one of the datasets.  Problem is, that would break other things that expected it not to be filtered.  Well, that&#8217;s not such a big problem, because TClientDataset has an awesome method called <a href="http://edn.embarcadero.com/article/29416">CloneCursor</a> that lets you set up a second client dataset that shares the first one&#8217;s data store, but with independent view settings.  So I used a cloned dataset, and immediately got an exception when I tried to run.  The control I was using couldn&#8217;t find the field.</p>
<p><span id="more-122"></span>After a bit of digging, I found out that when CloneCursor builds the field structure for the cloned dataset, it copies the FieldDefs from the original.  And FieldDefs only define data fields.  The field I was trying to display was a Calculated field, so I ended up without it.  Well, OK, that&#8217;s not such a big problem.  Just add a new calculated field, right?</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">begin</span>
&nbsp; &nbsp;calc <span class="sy1">:</span><span class="sy3">=</span> TWideStringField<span class="sy1">.</span><span class="me1">Create</span><span class="br0">&#40;</span><span class="kw2">nil</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;calc<span class="sy1">.</span><span class="me1">FieldName</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="st0">'DisplayName'</span><span class="sy1">;</span>
&nbsp; &nbsp;calc<span class="sy1">.</span><span class="me1">Size</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">255</span><span class="sy1">;</span>
&nbsp; &nbsp;calc<span class="sy1">.</span><span class="me1">FieldKind</span> <span class="sy1">:</span><span class="sy3">=</span> fkCalculated<span class="sy1">;</span>
&nbsp; &nbsp;calc<span class="sy1">.</span><span class="me1">Dataset</span> <span class="sy1">:</span><span class="sy3">=</span> DataSet<span class="sy1">;</span></div>
</div>
</pre>
<p>That ought to work, but it raises an exception.  &#8220;Cannot perform this operation on an open dataset.&#8221;   OK, I can understand that, almost.  It makes sense for data fields, because the underlying data store has to be set up in a certain way.  But for lookup and calculated fields that aren&#8217;t bound to the data store, it doesn&#8217;t make any sense.  Unfortunately, TField.SetDataset doesn&#8217;t care what kind of field you&#8217;re adding.  It checks to see if the dataset is inactive, and if not, boom!  It blows up in your face.</p>
<p>So I can&#8217;t add a calculated field to the dataset after CloneCursor has run, because CloneCursor calls Open and after that it&#8217;s too late.  So maybe I can add it <em>before</em> CloneCursor has run?  That almost works.  You end up with that calculated field in your cloned dataset&#8230; and nothing else.  Why?  Because about 2/3 of the way through TClientDataset.InternalOpen, it says &#8220;if DefaultFields then CreateFields;&#8221;  And DefaultFields is set by the method that calls InternalOpen, which starts like this:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">procedure</span> TDataSet<span class="sy1">.</span><span class="me1">DoInternalOpen</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; FDefaultFields <span class="sy1">:</span><span class="sy3">=</span> FieldCount <span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; InternalOpen<span class="sy1">;</span></div>
</div>
</pre>
<p>Oops!  Again, not making any distinction between data fields and auxiliary fields.  FDefaultFields is a private field, and DefaultFields is a read-only property, so that makes it very difficult to change.</p>
<p>I could have created an entirely new TClientDataset on my DFM that duplicates the entire field structure of the dataset I&#8217;m trying to clone, including the calculated field, and that would have solved the immediate problem.  But then I&#8217;d have to make another copy of another dataset every time I find myself in a situation like this.  I&#8217;d really prefer to make it recognize that there are some predefined fields that it can integrate into the field structure.</p>
<p>Fortunately, InternalOpen is virtual.  All I need to do is create a subclass of TClientDataset that overrides InternalOpen and resets FDefaultFields to the correct value before calling <strong>inherited</strong>.  Except&#8230; how do you do that?  It&#8217;s a private field of TDataset, accessible only through a read-only property.</p>
<p>Prior to Delphi 2010, I wouldn&#8217;t have been able to do this.  But now, <a href="http://tech.turbu-rpg.com/79/abusing-extended-rtti-for-fun-and-profit">there&#8217;s a way to fix this sort of problem with extended RTTI.</a> The solution looks like this.  (This solution works specifically for TClientDataset, but you can apply it to any TDataset descendant easily enough.)</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">unit</span> extensible_cds<span class="sy1">;</span>

<span class="kw1">interface</span>
<span class="kw1">uses</span>
&nbsp; &nbsp;DBClient<span class="sy1">;</span>

<span class="kw1">type</span>
&nbsp; &nbsp;TExtensibleClientDataset <span class="sy3">=</span> <span class="kw1">class</span><span class="br0">&#40;</span>TClientDataset<span class="br0">&#41;</span>
&nbsp; &nbsp;<span class="kw1">protected</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span> InternalOpen<span class="sy1">;</span> <span class="kw1">override</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">implementation</span>
<span class="kw1">uses</span>
&nbsp; &nbsp;db<span class="sy1">,</span> RTTI<span class="sy1">;</span>

<span class="coMULTI">{ TExtensibleClientDataset }</span>

<span class="kw1">procedure</span> TExtensibleClientDataset<span class="sy1">.</span><span class="me1">InternalOpen</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;context<span class="sy1">:</span> TRttiContext<span class="sy1">;</span>
&nbsp; &nbsp;field<span class="sy1">:</span> TField<span class="sy1">;</span>
&nbsp; &nbsp;value<span class="sy1">:</span> <span class="kw4">boolean</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;value <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">true</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">for</span> field <span class="kw1">in</span> <span class="kw2">self</span><span class="sy1">.</span><span class="me1">Fields</span> <span class="kw1">do</span>
&nbsp; &nbsp; &nbsp; value <span class="sy1">:</span><span class="sy3">=</span> value <span class="kw1">and</span> <span class="br0">&#40;</span>field<span class="sy1">.</span><span class="me1">FieldKind</span> &lt;&gt; fkData<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;context<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="sy1">.</span><span class="me1">ClassInfo</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">GetField</span><span class="br0">&#40;</span><span class="st0">'FDefaultFields'</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">SetValue</span><span class="br0">&#40;</span><span class="kw2">self</span><span class="sy1">,</span> TValue<span class="sy1">.</span><span class="me1">From</span><span class="br0">&#40;</span>value<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">inherited</span> InternalOpen<span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">end</span><span class="sy1">.</span></div>
</div>
</pre>
<p>If this sounds overly complicated, that&#8217;s probably because it is.  TField.SetDataset should only care about the dataset being open if you&#8217;re trying to add a data field.  That would have solved everything.  I submitted <a href="http://qc.embarcadero.com/wc/qcmain.aspx?d=77055">a report to QC about this</a> a few months ago.  Hopefully it&#8217;ll be one of the things they fix for Delphi 2011.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/122/adding-non-data-fields-to-a-client-dataset/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>How to break the D2010 compiler</title>
		<link>http://tech.turbu-rpg.com/90/how-to-break-the-d2010-compiler</link>
		<comments>http://tech.turbu-rpg.com/90/how-to-break-the-d2010-compiler#comments</comments>
		<pubDate>Tue, 09 Mar 2010 19:41:34 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Generics]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=90</guid>
		<description><![CDATA[I really loved when Delphi 2009 came out, how it fixed so many ugly problems in the Delphi IDE.  The stability issues and memory leaks that plagued D2006 and D2007 were greatly reduced.  And it just got better in D2010. The tradeoff, though, seems to have been compiler stability.  Trying to do anything with Generics [...]]]></description>
			<content:encoded><![CDATA[<p>I really loved when Delphi 2009 came out, how it fixed so many ugly problems in the Delphi IDE.  The stability issues and memory leaks that plagued D2006 and D2007 were greatly reduced.  And it just got better in D2010.</p>
<p>The tradeoff, though, seems to have been <em>compiler</em> stability.  Trying to do anything with Generics in D2009 before Update 3 came out was a nightmare, and even after, (and even in D2010,) there were still plenty of dark corners where you can end up with an Internal Compiler Error or linker error on something that, syntactically speaking at least, is perfectly cromulent Object Pascal.</p>
<p><span id="more-90"></span>I think I ran into the darkest corner of all this morning, or at least the darkest one that&#8217;s been found yet.  I found something that not only won&#8217;t compile, but will either destabilize or completely crash the IDE if you try to compile it.</p>
<p>I was trying to build a specialized tree class.  Trees are pretty familiar to anyone who&#8217;s taken a Data Structures class:  Each TreeNode contains a Value and pointers to two or more other TreeNodes.  There&#8217;s an excellent implementation of the standard binary tree concept in <a href="http://code.google.com/p/delphilhlplib/">DeHL</a>, for example.  This works really well if you&#8217;re trying to build a standard binary tree, but when you want a hierarchical tree, (like the way the VCL&#8217;s TTreeView organizes information,) the standard binary tree concept doesn&#8217;t work so well.</p>
<p>I came across a situation while building the TURBU editor where I had a list of objects with an ID property and a Parent property, where both are integers, and I needed to come up with a list of all objects whose Parent value was equal to the ID value of a given object.  In other words, all immediate children of a certain node.</p>
<p>Obtaining all descendants of a binary tree node is trivial if you understand recursion.  But that&#8217;s not what I needed; I needed all immediate children, but not their children.  So a different kind of tree is needed, one composed of TLists of nodes instead of binary subtrees of nodes.  So that&#8217;s what I wrote.  Each node contains a value, a pointer to the list it&#8217;s in, and a pointer  to the list containing its children, and each list is a list of nodes, plus a pointer to its parent node.  It ended up looking like this:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">unit</span> hierarchy_tree<span class="sy1">;</span>

<span class="kw1">interface</span>
<span class="kw1">uses</span>
&nbsp; &nbsp;SysUtils<span class="sy1">,</span> generics<span class="sy1">.</span><span class="me1">collections</span><span class="sy1">;</span>

<span class="kw1">type</span>
&nbsp; &nbsp;THierarchyTreeList&lt;T<span class="sy1">:</span> class&gt; <span class="sy3">=</span> <span class="kw1">class</span><span class="sy1">;</span>

&nbsp; &nbsp;THierarchyTreeNode&lt;T<span class="sy1">:</span> class&gt; <span class="sy3">=</span> <span class="kw1">class</span>
&nbsp; &nbsp;<span class="kw1">private</span> <span class="kw1">type</span>

&nbsp; &nbsp; &nbsp; TEnumerator <span class="sy3">=</span> <span class="kw1">class</span><span class="br0">&#40;</span>TEnumerator&lt;T&gt;<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">private</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FTopNode<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt;<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FIndex<span class="sy1">:</span> <span class="kw4">integer</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FNext<span class="sy1">:</span> TEnumerator<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FTopRead<span class="sy1">:</span> <span class="kw4">boolean</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">protected</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">function</span> DoGetCurrent<span class="sy1">:</span> T<span class="sy1">;</span> <span class="kw1">override</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">function</span> DoMoveNext<span class="sy1">:</span> <span class="kw4">Boolean</span><span class="sy1">;</span> <span class="kw1">override</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">public</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">constructor</span> Create<span class="br0">&#40;</span>value<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt;<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">function</span> GetCurrent<span class="sy1">:</span> T<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">function</span> MoveNext<span class="sy1">:</span> <span class="kw4">boolean</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">property</span> Current<span class="sy1">:</span> T <span class="kw3">read</span> GetCurrent<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><span class="sy1">;</span>

&nbsp; &nbsp; &nbsp; TNodeList <span class="sy3">=</span> THierarchyTreeList&lt;THierarchyTreeNode&lt;T&gt;&gt;<span class="sy1">;</span>

&nbsp; &nbsp;<span class="kw1">private</span>
&nbsp; &nbsp; &nbsp; FData<span class="sy1">:</span> T<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; FRight<span class="sy1">:</span> TNodeList<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; FList<span class="sy1">:</span> TNodeList<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">function</span> GetParent<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt;<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">function</span> GetLevel<span class="sy1">:</span> <span class="kw4">integer</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">public</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">constructor</span> Create<span class="br0">&#40;</span>value<span class="sy1">:</span> T<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">destructor</span> Destroy<span class="sy1">;</span> <span class="kw1">override</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span> Add<span class="br0">&#40;</span>node<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt;<span class="br0">&#41;</span><span class="sy1">;</span> <span class="kw1">overload</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span> Add<span class="br0">&#40;</span>value<span class="sy1">:</span> T<span class="br0">&#41;</span><span class="sy1">;</span> <span class="kw1">overload</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">function</span> GetEnumerator<span class="sy1">:</span> TEnumerator<span class="sy1">;</span>

&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> List<span class="sy1">:</span> TNodeList <span class="kw3">read</span> FList<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> Right<span class="sy1">:</span> TNodeList <span class="kw3">read</span> FRight<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> Parent<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt; <span class="kw3">read</span> GetParent<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> Level<span class="sy1">:</span> <span class="kw4">integer</span> <span class="kw3">read</span> GetLevel<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> Data<span class="sy1">:</span> T <span class="kw3">read</span> FData <span class="kw3">write</span> FData<span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>

&nbsp; &nbsp;THierarchyTreeList&lt;T<span class="sy1">:</span> class&gt; <span class="sy3">=</span> <span class="kw1">class</span><span class="br0">&#40;</span>TObjectList&lt;THierarchyTreeNode&lt;T&gt;&gt;<span class="br0">&#41;</span>
&nbsp; &nbsp;<span class="kw1">private</span>
&nbsp; &nbsp; &nbsp; FParent<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt;<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; FLevel<span class="sy1">:</span> <span class="kw4">integer</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span> SetParent<span class="br0">&#40;</span>Value<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt;<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">public</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">constructor</span> Create<span class="br0">&#40;</span>parent<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt;<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">function</span> Add<span class="br0">&#40;</span>value<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt;<span class="br0">&#41;</span><span class="sy1">:</span> <span class="kw4">integer</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> Parent<span class="sy1">:</span> THierarchyTreeNode&lt;T&gt; <span class="kw3">read</span> FParent <span class="kw3">write</span> SetParent<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> Level<span class="sy1">:</span> <span class="kw4">integer</span> <span class="kw3">read</span> FLevel<span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">implementation</span>

<span class="coMULTI">{ No implementation provided. &nbsp;Compiler will break before reaching this
point anyway. }</span>

<span class="kw1">end</span><span class="sy1">.</span></div>
</div>
</pre>
<p>You can try to compile this if you want, but make sure to save any work before you try.  Turns out this will break the compiler.  The compiler will try to process this, get stuck for about 30 seconds, all the while sucking up memory like there&#8217;s no tomorrow, until it hits 1.5 GB according to Task Manager, and then break.  It may crash, or it may simply become unstable.  If you&#8217;re really lucky, you&#8217;ll get an Out Of Memory error so at least you have some idea what&#8217;s going on.</p>
<p>After sharing this problem with a few other coders, I&#8217;ve got some idea what&#8217;s going on.  The node uses the list in its definition, and the uses the node as its generic parameter.  Apparently the compiler is trying to resolve one of these generics immediately, instead of waiting for instantiaion when a value of T can be provided, and getting stuck in an infinite loop, even though the definition is not infinitely recursive.</p>
<p>I&#8217;ve submitted <a href="http://qc.embarcadero.com/wc/qcmain.aspx?d=82838">a bug report about this to QC </a>. Thanks to <a href="http://stackoverflow.com/users/80565/moritz-beutel">Moritz Beutel</a> and <a href="http://delphiaddict.blogspot.com/">Stephen Kamradt</a> for the insights as to what was going on!</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/90/how-to-break-the-d2010-compiler/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>

