<?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</title>
	<atom:link href="http://tech.turbu-rpg.com/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>Tue, 09 Mar 2010 19:41:34 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<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 in [...]]]></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>
		<item>
		<title>Random musing: TStringList</title>
		<link>http://tech.turbu-rpg.com/87/random-musing-tstringlist</link>
		<comments>http://tech.turbu-rpg.com/87/random-musing-tstringlist#comments</comments>
		<pubDate>Mon, 15 Feb 2010 00:42:59 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=87</guid>
		<description><![CDATA[A while back, Jim McKeeth told me (jokingly, of course) that with a TStringList and a TDataset, you can solve any programming problem.  He also likes to compare TStringList to a Swiss Army Knife, because it can do so many things.  And it sort of made me wonder.  In object-oriented philosophy, a &#8220;good&#8221; object is [...]]]></description>
			<content:encoded><![CDATA[<p>A while back, Jim McKeeth told me (jokingly, of course) that with a TStringList and a TDataset, you can solve any programming problem.  He also likes to compare TStringList to a Swiss Army Knife, because it can do so many things.  And it sort of made me wonder. <span id="more-87"></span> In object-oriented philosophy, a &#8220;good&#8221; object is supposed to have a single well-defined responsibility.  But I generally see  TStringList being used for three very different things:</p>
<ol>
<li>As a <em>list of strings</em>, either a simple string list or occasionally as a quick-and-dirty parser by setting the DelimitedText or CommaText properties in order to create a list of strings.</li>
<li>As a <em>string-string key-value pair dictionary</em>, using the .Value property.</li>
<li>As a <em>string-object key-value pair dictionary,</em> using the .Objects property.</li>
</ol>
<p>You don&#8217;t often see the same TStringList use functionality from more than one of the above three modes.  So that makes me wonder, now that we have proper key-value pair dictionaries in the RTL, with better APIs for storing key-value pairs and especially for retrieving the value associated with a certain key than what TStrings provides, what would TStringList be like if Generics.Collections.pas had been available in Delphi 1?</p>
<p>I think there would probably be two predefined classes:</p>
<ul>
<li>TStringList = class(TList&lt;string&gt;);  It would implement CommaText and DelimitedText, and the LoadFromX and SaveToX methods.</li>
<li>TStringDictionary = class(TDictionary&lt;string, string&gt;); It would implement LoadFromX and SaveToX,</li>
</ul>
<p>No predefined string/object dictionary, since they&#8217;re easy enough to create yourself and get the right class for the objects.  It seems to me that that would be a more logical design.  But then again, how would that affect the way a bunch of VCL collection controls use custom TStrings descendants as string/object dictionaries?  (TListBox comes to mind.)</p>
<p>It&#8217;s an interesting question.  How would having generics available from Delphi 1 have affected the design of TStrings and the VCL in general?  Any thoughts?</p>
<p>P.S. Happy 15th birthday, Delphi!</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/87/random-musing-tstringlist/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Learning Delphi: A true story</title>
		<link>http://tech.turbu-rpg.com/85/learning-delphi-a-true-story</link>
		<comments>http://tech.turbu-rpg.com/85/learning-delphi-a-true-story#comments</comments>
		<pubDate>Thu, 21 Jan 2010 03:50:25 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=85</guid>
		<description><![CDATA[It&#8217;s pretty common where I work to go out to lunch together with other coworkers.  We&#8217;re located downtown, right across the street from a major shopping mall and with plenty of restaurants within walking distance.  A few months ago I was out with a few of the other coders, and one of the [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s pretty common where I work to go out to lunch together with other coworkers.  We&#8217;re located downtown, right across the street from a major shopping mall and with plenty of restaurants within walking distance.  A few months ago I was out with a few of the other coders, and one of the guys, David, told the story of how he got into programming.  It made quite an impression on me.  You&#8217;ll see why.  Unfortunately he&#8217;s no longer with the company, so I can&#8217;t get the exact story from him to retell here, but I&#8217;ll give the idea of it here.</p>
<p>Disclaimer:  Even when I give the story from his perspective, they aren&#8217;t exact quotes by any means.  My memory&#8217;s real good, but not <em>that</em> good!  But this is the basic idea of what he said, with no ficionalizing.</p>
<p>He wasn&#8217;t originally a computer programmer.  Hadn&#8217;t even really studied it in college.  David&#8217;s degree was in accounting, and he was working as an accountant with an insurance company back in the 90s.  He explained, &#8220;They had a tuition reimbursement program set up with the local college.  You could take classes there, and as long as you got a B or better at the end of the semester, they&#8217;d pay you back for the courses.  The tuition was several hundred dollars per course, so you had a pretty strong incentive to do well in them!</p>
<p>&#8220;Anyway, I was working with this insurance company, and my job was to take care of custom policies.  They had this thing where they&#8217;d write out custom policies for all the small businesses in the area, which were all more or less from the same template but with a bunch of cudtomized details tailored to each one.  And every month I&#8217;d get this big stack of a few hundred sheets of printer paper and have to crunch all the numbers.  It was annoying, really tedious work.&#8221;  We commiserated with him about how much that must have sucked, as we sat there waiting for our orders to arrive.</p>
<p>&#8220;After a while I got them to give me direct access to the database to read these values from, instead of giving me a printout from it, and that helped a bit, but there was still a lot of stuff I had to do by hand.  I got to thinking that this was the sort of thing that could be simplified by writing a computer program, if only I knew how to.  Well, they had a course at the college on database programming, and I signed up for it.&#8221;</p>
<p>He looked at us a bit wryly and said, &#8220;To this day I don&#8217;t know how I actually got into the class.  I wasn&#8217;t paying enough attention when I registered for it.  Turns out it was a graduate-level Computer Science course!  I didn&#8217;t have any CS background, and after the first day in the class I was completely lost.  The entire grade for the semester was based on a project.  You needed to create a database program to solve a real-world problem.  And it had to have a graphical user interface, it had to have reporting&#8230;&#8221; he listed off several requirements, and this was about where I saw where the story was headed.  It was almost like he was reading off a lit of Delphi&#8217;s historical strong points.</p>
<p>&#8220;I was completely lost.  I didn&#8217;t even know where to start!  But I&#8217;d already paid the tuition and I couldn&#8217;t get it back without a B or better in the class.  So I went to talk to a friend of mine who was a professor in the CS department.  I explained the problem to him, and he listened, and when I was done he thought about it for a moment.  &#8216;Well,&#8217; he said, &#8216;you could try Visual Basic, but there&#8217;s this new thing that just came out that might work better for you.  It&#8217;s called Delphi.&#8217;  He was able to get me set up with a copy, and I installed it on my computer at work.</p>
<p>&#8220;For the next few months, that&#8217;s pretty much what my life was about.  I&#8217;d work on my job during the day, then stay after in the evening to figure out how to use Delphi and how to make it talk to a database.  Needless to say, I didn&#8217;t get very much sleep!  But when the end of the semester rolled around, I had a working program.  And I ended up getting an A on the class.&#8221;</p>
<p>Ladies and gentlemen, that right there is a testament to the power of the Delphi language!  From a cold start, no programming background at all, to writing an app good enough to ace a graduate-level CS course in a matter of months.  Granted, David&#8217;s a very intelligent guy, and that certainly played a part in being able to pick it up so quickly, but even so, a lot of it&#8217;s the Delphi language itself.  (You&#8217;d never see results like that in the C family!)</p>
<p>Delphi is an evolution of Pascal, which was originally created by Niklaus Wirth as a teaching language.  Being easy to learn was an explicit design goal.  That originally came with <a href="http://www.lysator.liu.se/c/bwk-on-pascal.html">a few tradeoffs and drawbacks,</a> but the Borland team managed to extend the language into a powerful general-purpose programming system while still holding to the Pascal philosophy of making the language&#8217;s syntax and semantics as intuitive and easy to understand as they could.  That&#8217;s what made it possible for David to pick up so quickly, and that&#8217;s why even experienced programmers who have been coding for years love Delphi:  the language <em>just makes sense</em>.  It may not have &lt;insert trendy feature from some other language here&gt;, but language design makes the code, even code the dreaded &#8220;other people&#8217;s&#8221; variety, easier to read and comprehend, and that counts for a lot in modern team-based software development.</p>
<p>In Object Pascal, things make sense, and they tend to work right even if you don&#8217;t quite understand them.  IMO this is the main reason why so many people who know Delphi actually find it enjoyable to work with, as opposed to just something they write code in because they need to get something written, and why they sometimes even go so far as to call it &#8220;the best programming language.&#8221;</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/85/learning-delphi-a-true-story/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Abusing extended RTTI for fun and profit</title>
		<link>http://tech.turbu-rpg.com/79/abusing-extended-rtti-for-fun-and-profit</link>
		<comments>http://tech.turbu-rpg.com/79/abusing-extended-rtti-for-fun-and-profit#comments</comments>
		<pubDate>Fri, 15 Jan 2010 21:00:14 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=79</guid>
		<description><![CDATA[I&#8217;ll admit, I don&#8217;t like the default settings for Delphi 2010&#8217;s extended RTTI.  Making almost everything included by default ends up compiling a ton of junk into the EXE, most of which will never get used.  But every once in a while, you can find some sort of use for it.
For example, let&#8217;s take the [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ll admit, I don&#8217;t like the default settings for Delphi 2010&#8217;s extended RTTI.  Making almost everything included by default ends up compiling a ton of junk into the EXE, most of which will never get used.  But every once in a while, you can find some sort of use for it.</p>
<p><span id="more-79"></span>For example, let&#8217;s take the common case of trying to create a custom control that&#8217;s mostly like an existing VCL control, but slightly different.  You need to change some minor feature in some way.  Unfortunately, that feature relies on a private field in the base class that the author didn&#8217;t have the foresight to make Protected, and so to make it work, you end up having to copy and paste half the original class into your descendant.  I&#8217;ve seen a few examples of &#8220;custom components&#8221; incorporate pretty much the entire base class, because they had to in order to make one change.  With RTTI generated for private fields in Delphi 2010, we can remedy this oversight, to a certain degree at least.</p>
<p>One thing that&#8217;s always bugged me is the behavior of TDBComboBox.  It&#8217;s not all that often that you have a list of strings that you want to be able to choose from to put into a TStringField.  More common, in my experience at least, is to want to use it like a radio group:  fill it with strings and let the &#8220;important&#8221; property be ItemIndex, not Text.  (Though, now that I look at it, TDBRadioGroup does the same thing: it acts like what you care about is the name of the radio button, not its ItemIndex.)  So far, the only way to use data-aware controls to represent an enumerated type or similar selection by index of a group of strings is to set up a second dataset and use a lookup combo box.  I dunno about you, but that always felt like a hack to me.  I asked on StackOverflow, more than a year ago, <a href="http://stackoverflow.com/questions/299548/is-there-a-tdbcombobox-equivalent-that-goes-by-itemindex">if there was a control that would do what I&#8217;m looking for</a>, and never got a good answer, so I decided to try and build my own.  Let&#8217;s see how we can do this, and how using extended RTTI makes the fix easier.</p>
<p>We start by creating a descendant of TDBComboBox.  I&#8217;ll call it TDBIndexComboBox.  TDBComboBox already does almost everything we need.  All that&#8217;s really significant is the ItemIndex requirement.  We want the data-aware property of the control to be &#8220;aware of&#8221; the ItemIndex property instead of the Text property.  So first we need to know how it handles data-awareness.  Let&#8217;s look at the constructor for our first clue:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="co1">//inside the constructor</span>
&nbsp; FDataLink <span class="sy1">:</span><span class="sy3">=</span> TFieldDataLink<span class="sy1">.</span><span class="me1">Create</span><span class="sy1">;</span>
&nbsp; FDataLink<span class="sy1">.</span><span class="me1">Control</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">Self</span><span class="sy1">;</span>
&nbsp; FDataLink<span class="sy1">.</span><span class="me1">OnDataChange</span> <span class="sy1">:</span><span class="sy3">=</span> DataChange<span class="sy1">;</span>
&nbsp; FDataLink<span class="sy1">.</span><span class="me1">OnUpdateData</span> <span class="sy1">:</span><span class="sy3">=</span> UpdateData<span class="sy1">;</span>
&nbsp; FDataLink<span class="sy1">.</span><span class="me1">OnEditingChange</span> <span class="sy1">:</span><span class="sy3">=</span> EditingChange<span class="sy1">;</span></div>
</div>
</pre>
<p>It uses a TFieldDataLink object, declared in DBCtrls.pas, and assigns some TDBComboBox methods as event handlers.  If we want to change the way the data-aware control works, we&#8217;re going to have to override those event handlers in our descendant class.  Problem is, the FDataLink field is private, and there&#8217;s no way to get at it.  TDBRadioGroup and TDBLookupControl offer a protected &#8220;property DataLink: TFieldDataLink read FDataLink;&#8221;, but the rest of them don&#8217;t, for whatever reason.  So our first task is getting at FDataLink.</p>
<p>T0 use extended RTTI, you need a TRttiContext.  <a href="http://blog.barrkel.com/2010/01/delphi-2010-rtti-contexts-how-they-work.html">It&#8217;s an interface to a global RTTI object pool, and you only need one of them.</a> So it makes sense to attach it at the class level instead of to each individual object.  So as long as we&#8217;re doing this the Delphi 2010 way, let&#8217;s put it in a <a href="http://blogs.embarcadero.com/abauer/2009/05/29/38888">class constructor</a>.</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">type</span>
&nbsp; &nbsp;TDBIndexComboBox <span class="sy3">=</span> <span class="kw1">class</span><span class="br0">&#40;</span>TDBComboBox<span class="br0">&#41;</span>
&nbsp; &nbsp;<span class="kw1">private</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">class</span> <span class="kw1">var</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FContext<span class="sy1">:</span> TRttiContext<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;FDatalinkField<span class="sy1">:</span> TRttiField<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">class</span> <span class="kw1">constructor</span> Create<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">class</span> <span class="kw1">destructor</span> Destroy<span class="sy1">;</span>
<span class="sy1">...</span>

<span class="kw1">class</span> <span class="kw1">constructor</span> TDBIndexComboBox<span class="sy1">.</span><span class="me1">Create</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;classType<span class="sy1">:</span> TRttiType<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; FContext <span class="sy1">:</span><span class="sy3">=</span> TRttiContext<span class="sy1">.</span><span class="me1">Create</span><span class="sy1">;</span>
&nbsp; classType <span class="sy1">:</span><span class="sy3">=</span> FContext<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span>ClassParent<span class="sy1">.</span><span class="me1">ClassInfo</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; <span class="kw3">assert</span><span class="br0">&#40;</span><span class="kw3">assigned</span><span class="br0">&#40;</span>classType<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; FDatalinkField <span class="sy1">:</span><span class="sy3">=</span> classType<span class="sy1">.</span><span class="me1">GetField</span><span class="br0">&#40;</span><span class="st0">'FDataLink'</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; <span class="kw3">assert</span><span class="br0">&#40;</span><span class="kw3">assigned</span><span class="br0">&#40;</span>FDatalinkField<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">class</span> <span class="kw1">destructor</span> TDBIndexComboBox<span class="sy1">.</span><span class="me1">Destroy</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;FContext<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>This is pretty straightforward.  Create the RTTI Context, retrieve the RTTI info for the parent class, where FDatalink is declared as a private field and retrieve an RTTI reference to FDatalink.  I threw in a  few assertions for safety checks.  If we happen to be running on a codebase where extended RTTI for private fields has been turned off somehow, this will cause hard-to-track-down access violations when we try to use the component.</p>
<p>Next, we need to actually use the datalink, now that we have an RTTI reference to it.  So let&#8217;s define the constructor.</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="co1">//Class definition, continued from above</span>
&nbsp; &nbsp;<span class="kw1">private</span>
  &nbsp; &nbsp; FDataLink<span class="sy1">:</span> TFieldDataLink<span class="sy1">;</span>

&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span> SetComboText<span class="br0">&#40;</span><span class="kw1">const</span> Value<span class="sy1">:</span> <span class="kw4">string</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">function</span> GetComboText<span class="sy1">:</span> <span class="kw4">string</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span> UpdateData<span class="br0">&#40;</span>Sender<span class="sy1">:</span> <span class="kw4">TObject</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span> DataChange<span class="br0">&#40;</span>Sender<span class="sy1">:</span> <span class="kw4">TObject</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">protected</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> DataLink<span class="sy1">:</span> TFieldDataLink <span class="kw3">read</span> FDataLink<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; &nbsp;<span class="kw1">published</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">property</span> Style default csDropDownList<span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>

<span class="sy1">...</span>

<span class="kw1">constructor</span> TDBIndexComboBox<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">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; FDataLink <span class="sy1">:</span><span class="sy3">=</span> FDatalinkField<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">AsObject</span> <span class="kw1">as</span> TFieldDataLink<span class="sy1">;</span>
&nbsp; FDataLink<span class="sy1">.</span><span class="me1">OnDataChange</span> <span class="sy1">:</span><span class="sy3">=</span> DataChange<span class="sy1">;</span>
&nbsp; FDataLink<span class="sy1">.</span><span class="me1">OnUpdateData</span> <span class="sy1">:</span><span class="sy3">=</span> UpdateData<span class="sy1">;</span>
&nbsp; <span class="kw2">self</span><span class="sy1">.</span><span class="me1">Style</span> <span class="sy1">:</span><span class="sy3">=</span> csDropDownList<span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>I could have used the datalink field object directly throughout the component, but since I need it in more than one method, I decided to redeclare it as a field for simplicity&#8217;s sake, and make a protected property for it so it can be extended easily in the future.  So the constructor gets the value of the FDataLink defined in the parent class with its somewhat odd &#8220;GetValue(self)&#8221; syntax.  This takes the RTTI field object, which understands members of a <em>class</em>, and gives it an <em>instance</em> (self) to retrieve the value from.  After that, we can add new event handlers for OnDataChange and OnUpdateData.  I also redefined the default Style property because if you have a combo box with a predefined list of options that the user is supposed to choose from, sticking an edit control onto it is just asking for trouble from the user-interface perspective.</p>
<p>Now that we&#8217;ve overcome the difficulty of getting at the datalink, how do we set up the data-aware properties of the control?  Sort of like this:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">procedure</span> TDBIndexComboBox<span class="sy1">.</span><span class="me1">DataChange</span><span class="br0">&#40;</span>Sender<span class="sy1">:</span> <span class="kw4">TObject</span><span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="br0">&#40;</span>Style <span class="sy3">=</span> csSimple<span class="br0">&#41;</span> <span class="kw1">and</span> DroppedDown <span class="kw1">then</span> <span class="kw3">Exit</span><span class="sy1">;</span>
&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>FDataLink<span class="sy1">.</span><span class="me1">Field</span> &amp;lt<span class="sy1">;</span>&amp;gt<span class="sy1">;</span> <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">and</span> <span class="br0">&#40;</span>FDataLink<span class="sy1">.</span><span class="me1">Field</span> <span class="kw1">is</span> TNumericField<span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; <span class="kw2">self</span><span class="sy1">.</span><span class="me1">ItemIndex</span> <span class="sy1">:</span><span class="sy3">=</span> FDataLink<span class="sy1">.</span><span class="me1">Field</span><span class="sy1">.</span><span class="me1">AsInteger</span>
&nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; <span class="kw1">if</span> csDesigning <span class="kw1">in</span> ComponentState <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; SetComboText<span class="br0">&#40;</span>Name<span class="br0">&#41;</span>
&nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; SetComboText<span class="br0">&#40;</span><span class="st0">''</span><span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">procedure</span> TDBIndexComboBox<span class="sy1">.</span><span class="me1">UpdateData</span><span class="br0">&#40;</span>Sender<span class="sy1">:</span> <span class="kw4">TObject</span><span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; <span class="kw1">if</span> FDatalink<span class="sy1">.</span><span class="me1">Editing</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp;FDataLink<span class="sy1">.</span><span class="me1">Field</span><span class="sy1">.</span><span class="me1">AsInteger</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">self</span><span class="sy1">.</span><span class="me1">ItemIndex</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>DataChange gets called when the data field changes, to update the control.  This is mainly a copy of the original TDBComboBox.DataChange, except for the second and third lines of the method.  First, we want to restrict it to only numeric fields, since ItemIndex doesn&#8217;t make much sense as a string or a boolean.  Using a non-numeric field will cause the control to appear inactive.  Then, instead of the original &#8220;SetComboText(FDataLink.Field.Text)&#8221;, we want to change the item index instead.  The UpdateData is similarly straightforward.  No type-checking here, though if you want it it shouldn&#8217;t be too difficult to put some in.</p>
<p>Unfortunately, the SetComboText method is also private, and private methods, unlike private fields, don&#8217;t get RTTI generated for them.  So I ended up having to copy SetComboText (and GetComboText, which it references) verbatim from the parent class.  The way these two are set up, they look like they should be the read and write specifiers for a protected (or even public) ComboText property, but they aren&#8217;t.  Even so, that&#8217;s a lot better than what I&#8217;d have had to do if there was no way to get at the FDataLink field.  The workarounds required for that would probably involve reimplementing the entire class, or at least a sizable fraction thereof.</p>
<p>I&#8217;m using this control in the TURBU project, and I&#8217;ll update the repository soon (probably tomorrow sometime) with the code for this.  I&#8217;ll also put it up as a standalone download here on TURBU Tech tomorrow.  What I&#8217;ve published here, though, should be enough to get the component working if you have D2010 and some knowledge of the fundamentals of custom component work.</p>
<p>Like it?  Hate it?  Think of some way I could improve on it?  Let me know in the comments!</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/79/abusing-extended-rtti-for-fun-and-profit/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Programming Ethics 101</title>
		<link>http://tech.turbu-rpg.com/75/programming-ethics-101</link>
		<comments>http://tech.turbu-rpg.com/75/programming-ethics-101#comments</comments>
		<pubDate>Tue, 05 Jan 2010 01:43:47 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Programming Ethics]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=75</guid>
		<description><![CDATA[A few days ago on StackOverflow, someone posted the question, How can I keep Task Manager from killing my program? The first comment asks a very good question:  &#8220;What legitimate reason do you have for doing this?&#8221;
It reminded me of the guy who wanted to know how to make a file that can&#8217;t be edited [...]]]></description>
			<content:encoded><![CDATA[<p>A few days ago on StackOverflow, someone posted the question, <a href="http://stackoverflow.com/questions/1983564/how-can-i-keep-task-manager-from-killing-my-program">How can I keep Task Manager from killing my program?</a> The first comment asks a very good question:  &#8220;What legitimate reason do you have for doing this?&#8221;<br />
It reminded me of the guy who wanted to know how to <a href="http://stackoverflow.com/questions/1333807/how-can-i-make-a-file-trully-immutable-non-deletable-and-read-only">make a file that can&#8217;t be edited or deleted by any means.</a> Both of these guys seemed to have honorable intentions, but just hadn&#8217;t thought the ramifications through all that well.<span id="more-75"></span></p>
<p>As programmers, we often have near-absolute power on a computer.  We have access to everything that&#8217;s not specifically denied to us by system security, and with that much power comes great responsibility.  It&#8217;s important to remember what I consider the first principle of programming ethics: <em> </em><strong>When you write a program for someone else to use, the computer they are running it on is their property, not yours, and your program needs to behave itself accordingly.</strong></p>
<p>There&#8217;s a negative corollary to the Golden Rule that applies here.  <em>Do not unto others as you would not have them do unto you.</em> Much like a surgeon whose power over a patient in his care is similarly near-absolute, we have the implicit responsibility to &#8220;<a href="http://en.wikipedia.org/wiki/Primum_non_nocere">first, do no harm</a>.&#8221;  Your program needs to behave itself as an invited guest in someone else&#8217;s home.  You do not walk in and act like you own the place, and this has implications far beyond simply avoiding virus-like behavior.</p>
<p>For example, unless you&#8217;re writing for a very old computer, or for a few particularly backwards modern devices, such as the iPhone or most game consoles, your program is going to have to share the system with a bunch of other programs, and they need to execute too.  This means that you need to be careful to accomplish your task while using as few system resources as possible so you don&#8217;t end up hogging resources that another program may need.  If possible, keep your CPU and memory usage low.  This also means, for example, that it&#8217;s a very bad idea to use <a href="http://blog.barrkel.com/2009/12/commonly-confused-tidbits-re-net.html">a garbage collector that&#8217;s designed to build up as much garbage as possible before collecting.</a> That&#8217;s like never putting anything in the dishwasher until you have no clean dishes left in the house.  (And I&#8217;m sure some of you out there have kids that do exactly that.  Doesn&#8217;t it drive you up the wall?  Do you really want a program doing that on your computer?  If not, don&#8217;t do it to other people&#8217;s computers.)</p>
<p>Then there&#8217;s &#8220;protection.&#8221;  Most programmers tend to be pretty good about this sort of issue, until the idea of someone doing unauthorized things with their program comes up, and then all rational thought, not to mention consideration for ethics, goes right out the window, seemingly replaced by testosterone-driven outrage from coders who are usually rather mild and easygoing.  &#8220;What?!?  They&#8217;ll <em>never</em> get away with stealing <strong>my</strong> program!&#8221;  And then they proceed to do <a href="http://en.wikipedia.org/wiki/Sony_BMG_CD_copy_protection_scandal">all sorts of</a> <a href="http://www.boingboing.net/2006/01/30/anticopying_malware_.html">blatantly evil things</a> <a href="http://www.myce.com/news/Settec-Alpha-DVD-protection-used-on-German-Mr_-Mrs_Smith-11468/">to other people&#8217;s computers</a>.</p>
<p>Like most emotional knee-jerk reactions, this approach ignores the actual facts of the matter.  For example, empirical evidence suggests that, <a href="http://www.nytimes.com/2004/06/06/magazine/what-the-bagel-man-saw.html">in the absence of any enforcement, almost 90% of people tend to be basically honest.</a> That&#8217;s pretty darn good, even before you figure in the additional cost of actually providing the enforcement.</p>
<p>Also, copy protection <em>simply does not work</em>, due to a combination of two factors.  First, in order for the program to actually run, or for protected data to be read by a program, there has to be a &#8220;door&#8221; in the copy protection someplace.  Not a secret &#8220;backdoor,&#8221; just the ordinary variety that lets the authorized user through.  Thing is, if that exists in a computer-readable format, some user can find it and figure out how it works.  There are some coders out there who can read assembly as easily as you and I can read the language of our choice, and if one of them tries to find a hole in your protection, it won&#8217;t last long, and this is a very important point.</p>
<p>DRM proponents often say that a copy-protection scheme doesn&#8217;t have to be perfect; just good enough to discourage casual hackers and &#8220;keep honest people honest.&#8221;  The part about keeping honest people honest is nonsense, of course.  Either someone is honest or they aren&#8217;t.  But the thing is, so is the part about it not having to be perfect.  That might have been true twenty years ago, but today a copy-protection scheme has to be absolutely perfect, because if it&#8217;s cracked once, by anyone anywhere, it&#8217;s all over.  The crack will be posted online and any of those 13% or so of dishonest people out there who wants to use your program for free will have immediate access to it at the cost of just a little bit of searching.</p>
<p>And then there are the ethical issues involved.  In any other context, an external programmer taking control of the functionality of a computer away from the computer&#8217;s owner and using it against the owner&#8217;s interests is known as computer hacking, (or <em>cracking</em> if you prefer to use the term &#8220;hacker&#8221; in its original, positive context,) and is quite illegal.  Why all the special pleading in the case of DRM?  Because there&#8217;s lost revenue involved?  When did ensuring revenue become more a important consideration than not committing a crime?  Follow that line of reasoning far enough and you end up with Enron and Bernie Madoff, or the Sony rootkit.</p>
<p>We&#8217;re beginning a new year, and it&#8217;s traditional to set resolutions.  I hope everyone involved in programming will resolve to try to hold ourselves to the high standard of not writing code to do anything on another person&#8217;s computer that we wouldn&#8217;t want done on ours, and also to the even higher standard of actually thinking through the implications of what we try to do to figure out whether or not it would end up doing something bad.  Then maybe we&#8217;ll end up with a few less questions like that on StackOverflow, and I won&#8217;t have to keep posting the same answer to them. <img src='http://tech.turbu-rpg.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Happy New Year, everyone!</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/75/programming-ethics-101/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>RTTI and enumerators</title>
		<link>http://tech.turbu-rpg.com/70/rtti-and-enumerators</link>
		<comments>http://tech.turbu-rpg.com/70/rtti-and-enumerators#comments</comments>
		<pubDate>Fri, 18 Dec 2009 20:19:11 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=70</guid>
		<description><![CDATA[I&#8217;ve been doing a lot of work with the new RTTI system lately, figuring out how to take an arbitrary object and upload its data to a TClientDataset, or serialize it to a file.  I&#8217;ve got it working, and most of it was pretty straightforward once I managed to wrap my brain around the RTTI [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been doing a lot of work with the new RTTI system lately, figuring out how to take an arbitrary object and upload its data to a TClientDataset, or serialize it to a file.  I&#8217;ve got it working, and most of it was pretty straightforward once I managed to wrap my brain around the RTTI system.</p>
<p>Getting inside a generic list, though, was a bit of a hack.  Since <a href="http://stackoverflow.com/questions/1687755/">Delphi 2010 doesn&#8217;t support covariance and contravariance on generic classes</a>, you can&#8217;t take a routine that accepts &#8220;a TList&lt;T&gt;&#8221; and enumerate over all its contents, even if you constrain T to only descendants of a specific base class.  What I ended up doing is kinda ugly:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">procedure</span> TDatafile<span class="sy1">.</span><span class="me1">uploadList</span><span class="br0">&#40;</span>db<span class="sy1">:</span> TDataset<span class="sy1">;</span> <span class="kw1">const</span> value<span class="sy1">:</span> TValue<span class="sy1">;</span> fieldname<span class="sy1">:</span> <span class="kw4">string</span><span class="br0">&#41;</span><span class="sy1">;</span>

&nbsp; &nbsp;<span class="kw1">function</span> isUploadable<span class="br0">&#40;</span>method<span class="sy1">:</span> TRttiMethod<span class="br0">&#41;</span><span class="sy1">:</span> <span class="kw4">boolean</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">var</span>
&nbsp; &nbsp; &nbsp; param<span class="sy1">:</span> TArray&amp;lt<span class="sy1">;</span>TRttiParameter&amp;gt<span class="sy1">;;</span>
&nbsp; &nbsp; &nbsp; enumerator<span class="sy1">:</span> TRttiType<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; prop<span class="sy1">:</span> TRttiProperty<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; retval<span class="sy1">:</span> TRttiType<span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; result <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; param <span class="sy1">:</span><span class="sy3">=</span> method<span class="sy1">.</span><span class="me1">GetParameters</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="br0">&#40;</span><span class="kw3">length</span><span class="br0">&#40;</span>method<span class="sy1">.</span><span class="me1">GetParameters</span><span class="br0">&#41;</span> <span class="sy3">=</span> 0<span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Exit</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; enumerator <span class="sy1">:</span><span class="sy3">=</span> method<span class="sy1">.</span><span class="me1">ReturnType</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="kw3">assigned</span><span class="br0">&#40;</span>enumerator<span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Exit</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; prop <span class="sy1">:</span><span class="sy3">=</span> enumerator<span class="sy1">.</span><span class="me1">GetProperty</span><span class="br0">&#40;</span><span class="st0">'current'</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="kw3">assigned</span><span class="br0">&#40;</span>prop<span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw3">Exit</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; retval <span class="sy1">:</span><span class="sy3">=</span> prop<span class="sy1">.</span><span class="me1">PropertyType</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; result <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>retval <span class="kw1">is</span> TRttiInstanceType<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">var</span>
&nbsp; &nbsp;iType<span class="sy1">:</span> TRttiInstanceType<span class="sy1">;</span>
&nbsp; &nbsp;enumMethod<span class="sy1">:</span> TRttiMethod<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;<span class="kw3">assert</span><span class="br0">&#40;</span>value<span class="sy1">.</span><span class="me1">Kind</span> <span class="sy3">=</span> tkClass<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;iType <span class="sy1">:</span><span class="sy3">=</span> FContext<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span>value<span class="sy1">.</span><span class="kw3">TypeInfo</span><span class="br0">&#41;</span> <span class="kw1">as</span> TRttiInstanceType<span class="sy1">;</span>
&nbsp; &nbsp;enumMethod <span class="sy1">:</span><span class="sy3">=</span> iType<span class="sy1">.</span><span class="me1">GetMethod</span><span class="br0">&#40;</span><span class="st0">'GetEnumerator'</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw3">assert</span><span class="br0">&#40;</span><span class="kw3">assigned</span><span class="br0">&#40;</span>enumMethod<span class="br0">&#41;</span> <span class="kw1">and</span> <span class="br0">&#40;</span>isUploadable<span class="br0">&#40;</span>enumMethod<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;uploadEnumerable<span class="br0">&#40;</span>db<span class="sy1">,</span> value<span class="sy1">,</span> enumMethod<span class="sy1">,</span> fieldname<span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">procedure</span> TDatafile<span class="sy1">.</span><span class="me1">uploadEnumerable</span><span class="br0">&#40;</span>db<span class="sy1">:</span> TDataset<span class="sy1">;</span> <span class="kw1">const</span> value<span class="sy1">:</span> TValue<span class="sy1">;</span>
&nbsp; uploadMethod<span class="sy1">:</span> TRttiMethod<span class="sy1">;</span> fieldName<span class="sy1">:</span> <span class="kw4">string</span><span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;enumerator<span class="sy1">:</span> <span class="kw4">TObject</span><span class="sy1">;</span>
&nbsp; &nbsp;enumType<span class="sy1">:</span> TRttiType<span class="sy1">;</span>
&nbsp; &nbsp;current<span class="sy1">:</span> TRttiProperty<span class="sy1">;</span>
&nbsp; &nbsp;moveNext<span class="sy1">:</span> TRttiMethod<span class="sy1">;</span>
&nbsp; &nbsp;datafile<span class="sy1">:</span> TDatafile<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;enumerator <span class="sy1">:</span><span class="sy3">=</span> uploadMethod<span class="sy1">.</span><span class="me1">Invoke</span><span class="br0">&#40;</span>value<span class="sy1">,</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsObject</span><span class="sy1">;</span>
&nbsp; &nbsp;enumType <span class="sy1">:</span><span class="sy3">=</span> FContext<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span>enumerator<span class="sy1">.</span><span class="me1">ClassInfo</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;current <span class="sy1">:</span><span class="sy3">=</span> enumType<span class="sy1">.</span><span class="me1">GetProperty</span><span class="br0">&#40;</span><span class="st0">'Current'</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;moveNext <span class="sy1">:</span><span class="sy3">=</span> enumType<span class="sy1">.</span><span class="me1">GetMethod</span><span class="br0">&#40;</span><span class="st0">'MoveNext'</span><span class="br0">&#41;</span><span class="sy1">;</span>

&nbsp; &nbsp;<span class="kw1">while</span> moveNext<span class="sy1">.</span><span class="me1">Invoke</span><span class="br0">&#40;</span>enumerator<span class="sy1">,</span> <span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsBoolean</span> <span class="kw1">do</span>
&nbsp; &nbsp;<span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; datafile <span class="sy1">:</span><span class="sy3">=</span> current<span class="sy1">.</span><span class="me1">GetValue</span><span class="br0">&#40;</span>enumerator<span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsType</span>&amp;lt<span class="sy1">;</span>TDatafile&amp;gt<span class="sy1">;;</span>
&nbsp; &nbsp; &nbsp; db<span class="sy1">.</span><span class="kw3">Append</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; datafile<span class="sy1">.</span><span class="me1">upload</span><span class="br0">&#40;</span>db<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">if</span> db<span class="sy1">.</span><span class="me1">state</span> <span class="kw1">in</span> dsEditModes <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; db<span class="sy1">.</span><span class="me1">Post</span><span class="sy1">;</span>
&nbsp; &nbsp;enumerator<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>There&#8217;s some unrelated code mixed in which is specific to the way I&#8217;m uploading the objects to the datasets, but this is mainly about enumerating a generic list of an unknown type.  Basically, we&#8217;re doing manually what the language-level enumerator support does automatically, extracting and validating the enumerator and running the enumeration by hand.  Scary, no?  There&#8217;s gotta be a better way.</p>
<p>What I&#8217;d really like to see is something like this.  TRttiStructuredType (the base class for TRttiRecordType, TRttiInstanceType and TRttiInterfaceType) should get two methods:</p>
<p>function TRttiStructuredType.IsEnumerable: boolean;</p>
<p>function TRttiStructuredType.GetEnumerator: TRttiEnumerator; //returns nil if IsEnumerable is false</p>
<p>The TRttiEnumerator would basically do what my code above does: crack open the underlying type&#8217;s enumerator and run it, returning an enumerated sequence of TRttiObject objects.  But in order for that to work well, (without the sort of ugly hacks you see above,) it needs compiler-level support.  Maybe we can hope to see it in D2011?</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/70/rtti-and-enumerators/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>StringList Comparison</title>
		<link>http://tech.turbu-rpg.com/66/stringlist-comparison</link>
		<comments>http://tech.turbu-rpg.com/66/stringlist-comparison#comments</comments>
		<pubDate>Mon, 16 Nov 2009 04:53:51 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=66</guid>
		<description><![CDATA[A few weeks ago I wrote about using the List Comparison algorithm to optimize a slow nested loop.  This evening I saw someone on the Delphi forums asking about a way to compare two TStringLists, so I wrote up a quick version of the List Comparison algorithm specifically for TStringLists.  I used the basic algorithm [...]]]></description>
			<content:encoded><![CDATA[<p>A few weeks ago I wrote about using the List Comparison algorithm to optimize a slow nested loop.  This evening I saw someone on the Delphi forums asking about a way to compare two TStringLists, so I wrote up a quick version of the List Comparison algorithm specifically for TStringLists.  I used the basic algorithm as a higher-order function that you can pass method pointers to to configure its behavior, and added a few built-in comparison functions that perform common tasks.  You can find it in the Downloads page.  I&#8217;m working on a List Comparison library that uses generics and anonymous methods to compare TList&lt;T&gt; descendants, but it&#8217;s not done yet.  I&#8217;ll post it here when it&#8217;s ready.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/66/stringlist-comparison/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>As-sertion casting</title>
		<link>http://tech.turbu-rpg.com/56/as-sertion-casting</link>
		<comments>http://tech.turbu-rpg.com/56/as-sertion-casting#comments</comments>
		<pubDate>Fri, 13 Nov 2009 21:46:39 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=56</guid>
		<description><![CDATA[So you&#8217;ve got some code that you know has to be in a certain state. It can&#8217;t be proven at compile-time, but you know that it has to be like this, and if it wasn&#8217;t, it could cause some serious problems. But you&#8217;re a good, careful coder, and you don&#8217;t want to just assume that [...]]]></description>
			<content:encoded><![CDATA[<p>So you&#8217;ve got some code that you know has to be in a certain state. It can&#8217;t be proven at compile-time, but you know that it has to be like this, and if it wasn&#8217;t, it could cause some serious problems. But you&#8217;re a good, careful coder, and you don&#8217;t want to just assume that the state of the program will be a certain way just because you think it should. You know better than that, so you add a bit of special code to verify your assumptions. And if it turns out you were wrong, it&#8217;ll raise an exception that you almost certainly have nothing in place to catch at any level before the global exception handler for your app.</p>
<p>Sound familiar?  It should.  You probably do it all the time.  It&#8217;s an as-cast.<br />
<span id="more-56"></span><br />
&#8230;what? You thought I was going to say &#8220;assertion&#8221;? Good point! An as-cast is a whole lot like an assertion. There&#8217;s one big difference, though. When an assertion fails, your error log ends up giving you some very useful (and easily customizable) information about the nature of the error. With an as-cast failure&#8230; nothing.  Granted, any decent error-logging framework will give you the unit name and line number anyway, but assertions still offer custom error messages that can make maintenance easier. (And, if your app doesn&#8217;t have an error-logging framework installed, the assertion failure can point you directly to whatever&#8217;s going wrong faster than just about anything else.)</p>
<p>Now I&#8217;m not saying that as-casts are bad. They can be useful, just like assertions can. But I often see code that uses as-casts when they&#8217;re not needed, and it feels like <a href="http://en.wikipedia.org/wiki/Cargo_cult_programming">cargo cult programming</a> to me.  For example, have you ever seen (or written) a construct like this?</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">if</span> sender <span class="kw1">is</span> TMyObject <span class="kw1">then</span>
&nbsp; <span class="br0">&#40;</span>sender <span class="kw1">as</span> TMyObject<span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">DoSomething</span><span class="sy1">;</span></div>
</div>
</pre>
<p>What&#8217;s wrong with this picture?  Let&#8217;s look at what an as-cast does.  From system.pas:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">function</span> _AsClass<span class="br0">&#40;</span>Child<span class="sy1">:</span> <span class="kw4">TObject</span><span class="sy1">;</span> Parent<span class="sy1">:</span> <span class="kw4">TClass</span><span class="br0">&#41;</span><span class="sy1">:</span> <span class="kw4">TObject</span><span class="sy1">;</span>
<span class="co2">{$IFDEF PUREPASCAL}</span>
<span class="kw1">begin</span>
&nbsp; Result <span class="sy1">:</span><span class="sy3">=</span> Child<span class="sy1">;</span>
&nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="br0">&#40;</span>Child <span class="kw1">is</span> Parent<span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; Error<span class="br0">&#40;</span>reInvalidCast<span class="br0">&#41;</span><span class="sy1">;</span>   <span class="co1">// loses return address</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>I&#8217;ll skip the assembly version, though it&#8217;s worth noting that the PUREPASCAL reference code isn&#8217;t completely accurate.  (Think about how it would handle a <strong>nil</strong> object reference.)  But it&#8217;s close enough.  If we substitute the code into my example, we get this:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">if</span> Sender <span class="kw1">is</span> TMyObject <span class="kw1">then</span>
&nbsp; <span class="kw1">begin</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="kw1">not</span> <span class="br0">&#40;</span>Sender <span class="kw1">is</span> TMyObject<span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; Error<span class="br0">&#40;</span>reInvalidCast<span class="br0">&#41;</span><span class="sy1">;</span> &nbsp; <span class="co1">// loses return address</span>
&nbsp; <span class="kw1">end</span><span class="sy1">;</span>
TMyObject<span class="br0">&#40;</span>Sender<span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">DoSomething</span><span class="sy1">;</span></div>
</div>
</pre>
<p>Why are we calling<strong> is</strong> twice?  Unless some other thread is replacing one object with another in between the two lines, there&#8217;s nothing to worry about, and if you do have threads behaving that way, you&#8217;ve got bigger issues to worry about.  But even so, does it really matter?  Would that be a bad thing to do?  Well, with all due credit to Bill Clinton, that depends on what the definition of <strong>is</strong> is.  Fortunately, it&#8217;s defined in system.pas, right above the definition of <strong>as</strong>.  It simply checks for nil, then calls InheritsFrom on the object.  So what does InheritsFrom do?  Here&#8217;s the PUREPASCAL version:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">class</span> <span class="kw1">function</span> <span class="kw4">TObject</span><span class="sy1">.</span><span class="me1">InheritsFrom</span><span class="br0">&#40;</span>AClass<span class="sy1">:</span> <span class="kw4">TClass</span><span class="br0">&#41;</span><span class="sy1">:</span> <span class="kw4">Boolean</span><span class="sy1">;</span>
<span class="co2">{$IFDEF PUREPASCAL}</span>
<span class="kw1">var</span>
&nbsp; ClassPtr<span class="sy1">:</span> <span class="kw4">TClass</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; ClassPtr <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">Self</span><span class="sy1">;</span>
&nbsp; <span class="kw1">while</span> <span class="br0">&#40;</span>ClassPtr &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">and</span> <span class="br0">&#40;</span>ClassPtr &lt;&gt; AClass<span class="br0">&#41;</span> <span class="kw1">do</span>
&nbsp; &nbsp; ClassPtr <span class="sy1">:</span><span class="sy3">=</span> <span class="kw4">PPointer</span><span class="br0">&#40;</span><span class="kw4">Integer</span><span class="br0">&#40;</span>ClassPtr<span class="br0">&#41;</span> <span class="sy3">+</span> vmtParent<span class="br0">&#41;</span><span class="sy2">^</span><span class="sy1">;</span>
&nbsp; Result <span class="sy1">:</span><span class="sy3">=</span> ClassPtr <span class="sy3">=</span> AClass<span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>It&#8217;s basically doing a linear search of a linked list, which runs in O(n) time and isn&#8217;t particularly cache-friendly.  Not much to worry about, though, unless you do it in a tight loop.  But when you do, the results can be informative.  One thing I didn&#8217;t mention in my post a few weeks ago about <a href="http://tech.turbu-rpg.com/42/real-world-optimization" target="_blank">optimizing a routine at work</a> was the impact of as-casts.  I didn&#8217;t mention it because it became irrelevant once I removed the horrendously inefficient comparison double-loop, but when it was there, the profiler found that 15% of the time was spent in System._AsClass.  Why?  Because our ORM object list was doing an as-cast in its Get routine.</p>
<p>You&#8217;ve probably seen custom object lists before, wrappers around TObjectList that only hold certain kinds of objects.  This was one of those, and on its Get routine it did something like this:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">function</span> TOrmObjectList<span class="sy1">.</span><span class="me1">Get</span><span class="br0">&#40;</span>index<span class="sy1">:</span> <span class="kw4">integer</span><span class="br0">&#41;</span><span class="sy1">:</span> TOrmObject<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;result <span class="sy1">:</span><span class="sy3">=</span> <span class="kw1">inherited</span> Get<span class="br0">&#40;</span>index<span class="br0">&#41;</span> <span class="kw1">as</span> TOrmObject<span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>Our inheritance tree was pretty deep, and so every time it tried to retrieve an object from the list, it had to walk a linked list 6 or 7 steps before arriving at the base class the as-cast was looking for.  Do that a few million times and it starts to really add up.  And the thing is, it wasn&#8217;t even needed there because the Put method would only accept TOrmObject instances anyway.  The compiler&#8217;s type safety made the cast redundant, and I ended up replacing it with a hard cast.  (And yes, there&#8217;s always the possibility of memory corruption.  But in my experience, if a bad pointer ends up stomping your RAM and writing over your data, the likelihood that whatever essentially random bytes overwrite your object reference will also be a pointer to a valid object of a different class is extremely low, and an as-cast in this case is far more likely to produce an access violation than an EInvalidCast exception.  This will become even more true when we go to 64 bits and the size of a pointer doubles.)</p>
<p>Again, as-casts aren&#8217;t bad.  I&#8217;ve caught a fair number of errors with them, and I&#8217;d be the last to say they&#8217;re &#8220;considered harmful&#8221; or anything like that.  But using them when there&#8217;s no need for one doesn&#8217;t actually improve safety and can result in significant performance hits if it&#8217;s used in a tight loop.  A hard cast has zero performance impact at runtime.  No code is generated for it; it&#8217;s just there to satisfy the compiler&#8217;s type safety rules.  So when you write your typecasts, ask yourself if you really need an as-sertion here.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/56/as-sertion-casting/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>How to leak a class you never defined</title>
		<link>http://tech.turbu-rpg.com/52/how-to-leak-a-class-you-never-defined</link>
		<comments>http://tech.turbu-rpg.com/52/how-to-leak-a-class-you-never-defined#comments</comments>
		<pubDate>Thu, 29 Oct 2009 02:45:29 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Anonymous Methods]]></category>
		<category><![CDATA[Delphi]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=52</guid>
		<description><![CDATA[Quick, what&#8217;s wrong with this code?


procedure ContrivedExample;
var
&#160; &#160;factorial: TFunc;
begin
&#160; &#160;factorial :=
&#160; &#160; &#160; function&#40;input: Integer&#41;: integer
&#160; &#160; &#160; begin
&#160; &#160; &#160; &#160; &#160;if &#40;input = 0&#41; or &#40;input = 1&#41; then
&#160; &#160; &#160; &#160; &#160; &#160; result := input
&#160; &#160; &#160; &#160; &#160;else result := input * &#40;factorial&#40;input - 1&#41;&#41;;
&#160; &#160; &#160; end;

&#160; &#160;writeln&#40;factorial&#40;5&#41;&#41;;
end;



Or how [...]]]></description>
			<content:encoded><![CDATA[<p>Quick, 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> ContrivedExample<span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;factorial<span class="sy1">:</span> TFunc<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;factorial <span class="sy1">:</span><span class="sy3">=</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">function</span><span class="br0">&#40;</span>input<span class="sy1">:</span> <span class="kw4">Integer</span><span class="br0">&#41;</span><span class="sy1">:</span> <span class="kw4">integer</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">if</span> <span class="br0">&#40;</span>input <span class="sy3">=</span> 0<span class="br0">&#41;</span> <span class="kw1">or</span> <span class="br0">&#40;</span>input <span class="sy3">=</span> 1<span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result <span class="sy1">:</span><span class="sy3">=</span> input
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">else</span> result <span class="sy1">:</span><span class="sy3">=</span> input <span class="sy3">*</span> <span class="br0">&#40;</span>factorial<span class="br0">&#40;</span>input <span class="sy3">-</span> 1<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><span class="sy1">;</span>

&nbsp; &nbsp;<span class="kw3">writeln</span><span class="br0">&#40;</span>factorial<span class="br0">&#40;</span>5<span class="br0">&#41;</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-52"></span><br />
Or how about this one, which doesn&#8217;t use recursion, and is based on something I noticed when using the list comparison algorithm to solve a problem at work?</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">procedure</span> SomewhatLessContrivedExample<span class="br0">&#40;</span>list1<span class="sy1">,</span> list2<span class="sy1">:</span> TList<span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;newItem<span class="sy1">,</span> editItem<span class="sy1">,</span> deleteItem<span class="sy1">:</span> TProc<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;newItem <span class="sy1">:</span><span class="sy3">=</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span><span class="br0">&#40;</span>value<span class="sy1">:</span> TMyObject<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fMainList<span class="sy1">.</span><span class="me1">Add</span><span class="br0">&#40;</span>value<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><span class="sy1">;</span>

&nbsp; &nbsp;editItem <span class="sy1">:</span><span class="sy3">=</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span><span class="br0">&#40;</span>value<span class="sy1">:</span> TMyObject<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">if</span> value<span class="sy1">.</span><span class="me1">CheckSomeFlag</span> <span class="sy3">=</span> <span class="kw2">true</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; newItem<span class="br0">&#40;</span>value<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">else</span> fMainList<span class="br0">&#91;</span>value<span class="sy1">.</span><span class="me1">id</span><span class="br0">&#93;</span><span class="sy1">.</span><span class="me1">Update</span><span class="br0">&#40;</span>value<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><span class="sy1">;</span>

&nbsp; &nbsp;deleteItem <span class="sy1">:</span><span class="sy3">=</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">procedure</span><span class="br0">&#40;</span>value<span class="sy1">:</span> TMyObject<span class="br0">&#41;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fMainList<span class="sy1">.</span><span class="kw3">Delete</span><span class="br0">&#40;</span>value<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><span class="sy1">;</span>

&nbsp; &nbsp;listCompare<span class="br0">&#40;</span>list1<span class="sy1">,</span> list2<span class="sy1">,</span> editItem<span class="sy1">,</span> newItem<span class="sy1">,</span> deleteItem<span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>If you were paying close attention in my first two posts about how anonymous methods are implemented, you might realize that these anonymous method references are interface references to a TInterfacedObject descendant, and that by having the closure capture one, the object now holds a reference to itself.  Which means that when you actually run the code, you could end up with FastMM4 reporting something like this:</p>
<p>An unexpected memory leak has occurred. The unexpected small block leaks are:<br />
21 &#8211; 28 bytes: ContrivedExample$ActRec x 1</p>
<p>Oops!  That&#8217;s not good, especially if you call ContrivedExample in a main loop somewhere and end up leaking hundreds of thousands of them.  This is too bad, since there are all sorts of interesting things you could do by using anonymous methods recursively.  And since anonymous methods don&#8217;t have a name, they can&#8217;t invoke themselves recursively without capturing the reference variable.</p>
<p>There&#8217;s probably no way for the compiler to prevent this in the general case, but it could be solved easily enough for examples that fit the pattern described in my examples:  When an anonymous method reference variable is only assigned once in an enclosing procedure, and that one point is where the method is defined within the procedure (i.e. it&#8217;s not passed in as a parameter or already assigned at global or object scope), then the compiler can be certain that the Self pointer for the underlying object is the same as the one it&#8217;s currently executing on, and transform it behind the scenes from an interface reference to a normal method call.</p>
<p>Can anyone else think of possible ways to fix this?</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/52/how-to-leak-a-class-you-never-defined/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Real-world optimization</title>
		<link>http://tech.turbu-rpg.com/42/real-world-optimization</link>
		<comments>http://tech.turbu-rpg.com/42/real-world-optimization#comments</comments>
		<pubDate>Tue, 20 Oct 2009 15:22:27 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Optimization]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=42</guid>
		<description><![CDATA[Last week at work, I was asked to look at one of our verification modules that was taking about three times longer to run than it had in an earlier version.  This module takes a set of result files, compares them against another file showing expected results, and reports any discrepancies that are outside the [...]]]></description>
			<content:encoded><![CDATA[<p>Last week at work, I was asked to look at one of our verification modules that was taking about three times longer to run than it had in an earlier version.  This module takes a set of result files, compares them against another file showing expected results, and reports any discrepancies that are outside the defined margin of error.  It&#8217;s some pretty heavy work involving hundreds of thousands of data points, and the old version already took more than ten minutes.  Increasing the running time by a factor of three just wasn&#8217;t acceptable.  So I started to look at what was going on.</p>
<p><span id="more-42"></span></p>
<p>Verification takes place in four steps:</p>
<ol>
<li>Load the data from the files</li>
<li>Process the data</li>
<li>Process the data some more</li>
<li>Retrieve the results</li>
</ol>
<p>Steps 3 and 4 only take a few seconds each.  Step 2 takes a couple minutes, but the bulk of the time is spent in step 1.  So I decided to focus on there to see if I could find what was making it take so long.  First thing to do is establish a baseline. I built the old version and turned on SQL Server Profiler for the database and <a href="http://delphitools.info/samplingprofiler/">Sampling Profiler</a>, an excellent tool written in Delphi that helps you profile Delphi apps without slowing them down the way AQTime does.  I ran the entire verification process and found that yes, not only was step 1 taking most of the time, over 90% of the time was spent on one single line that matches the data from the files against the data in the database.</p>
<p>The data-loading system looks something like this.  Names and a few details have been changed to protect <span style="text-decoration: line-through;">the innocent</span> the corporate intellectual property, of course, but this is the general idea of what was going on.  See how many problems you can spot in this code.  (Bear in mind, this was the original, faster version.)</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">procedure</span> TVerificationDataModule<span class="sy1">.</span><span class="me1">LoadFile</span><span class="br0">&#40;</span><span class="kw1">const</span> filename<span class="sy1">:</span> <span class="kw4">string</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;otherParams<span class="sy1">:</span> TOtherData<span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;lines<span class="sy1">:</span> TStringList<span class="sy1">;</span>
&nbsp; &nbsp;fileData<span class="sy1">:</span> TObjectList
&nbsp; &nbsp;dbData<span class="sy1">:</span> TOrmObjectList<span class="sy1">;</span>
&nbsp; &nbsp;i<span class="sy1">:</span> <span class="kw4">integer</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;lines <span class="sy1">:</span><span class="sy3">=</span> TStringList<span class="sy1">.</span><span class="me1">Create</span><span class="sy1">;</span>
&nbsp; &nbsp;fileData <span class="sy1">:</span><span class="sy3">=</span> TObjectList<span class="sy1">.</span><span class="me1">Create</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">try</span>
&nbsp; &nbsp; &nbsp; lines<span class="sy1">.</span><span class="me1">LoadFromFile</span><span class="br0">&#40;</span>filename<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> I <span class="sy1">:</span><span class="sy3">=</span> 0 <span class="kw1">to</span> lines<span class="sy1">.</span><span class="me1">Count</span> <span class="sy3">-</span> 1 <span class="kw1">do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileData<span class="sy1">.</span><span class="me1">Add</span><span class="br0">&#40;</span>parseLine<span class="br0">&#40;</span>lines<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">finally</span>
&nbsp; &nbsp; &nbsp; lines<span class="sy1">.</span><span class="me1">Free</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>

&nbsp; &nbsp;dbData <span class="sy1">:</span><span class="sy3">=</span> GetRelevantDBData<span class="br0">&#40;</span>otherParams<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">try</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">for</span> i <span class="sy1">:</span><span class="sy3">=</span> 0 <span class="kw1">to</span> fileData<span class="sy1">.</span><span class="me1">Count</span> <span class="sy3">-</span> 1 <span class="kw1">do</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MatchFileDataAgainstDB<span class="br0">&#40;</span>fileData<span class="br0">&#91;</span>i<span class="br0">&#93;</span> <span class="kw1">as</span> TFileData<span class="sy1">,</span> dbData<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">finally</span>
&nbsp; &nbsp; &nbsp; dbData<span class="sy1">.</span><span class="me1">Free</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span>

<span class="kw1">procedure</span> TVerificationDataModule<span class="sy1">.</span><span class="me1">MatchFileDataAgainstDB</span><span class="br0">&#40;</span>fileData<span class="sy1">:</span> TFileData<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dbData<span class="sy1">:</span> TOrmObjectList<span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;i<span class="sy1">:</span> <span class="kw4">integer</span><span class="sy1">;</span>
&nbsp; &nbsp;dbItem<span class="sy1">:</span> TOrmVerificationObject<span class="sy1">;</span>
&nbsp; &nbsp;updateProcedure<span class="sy1">:</span> IStoredProcedureRecord<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;<span class="kw1">for</span> i <span class="sy1">:</span><span class="sy3">=</span> 0 <span class="kw1">to</span> dbData<span class="sy1">.</span><span class="me1">Count</span> <span class="sy3">-</span> 1 <span class="kw1">do</span>
&nbsp; &nbsp;<span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; dbItem <span class="sy1">:</span><span class="sy3">=</span> dbData<span class="br0">&#91;</span>i<span class="br0">&#93;</span> <span class="kw1">as</span> TOrmVerificationObject<span class="sy1">;</span>

&nbsp; &nbsp; &nbsp; <span class="co1">//90% of time is spent on this next line:</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> fileData<span class="sy1">.</span><span class="me1">param1</span> <span class="sy3">=</span> dbItem<span class="sy1">.</span><span class="me1">param1</span> <span class="kw1">and</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileData<span class="sy1">.</span><span class="me1">param2</span> <span class="sy3">=</span> dbItem<span class="sy1">.</span><span class="me1">param2</span> <span class="kw1">and</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileData<span class="sy1">.</span><span class="me1">param3</span> <span class="sy3">=</span> dbItem<span class="sy1">.</span><span class="me1">param3</span> <span class="kw1">and</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;fileData<span class="sy1">.</span><span class="me1">param4</span> <span class="sy3">=</span> dbItem<span class="sy1">.</span><span class="me1">param4</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">begin</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;updateProcedure <span class="sy1">:</span><span class="sy3">=</span> CreateStoredProc<span class="br0">&#40;</span><span class="st0">'VERIFICATION_DATA_LOADER'</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;updateProcedure<span class="sy1">.</span><span class="me1">param1</span> <span class="sy1">:</span><span class="sy3">=</span> fileData<span class="sy1">.</span><span class="me1">param3</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;updateProcedure<span class="sy1">.</span><span class="me1">param2</span> <span class="sy1">:</span><span class="sy3">=</span> fileData<span class="sy1">.</span><span class="me1">param4</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;updateProcedure<span class="sy1">.</span><span class="me1">param3</span> <span class="sy1">:</span><span class="sy3">=</span> fileData<span class="sy1">.</span><span class="me1">param5</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;updateProcedure<span class="sy1">.</span><span class="me1">param4</span> <span class="sy1">:</span><span class="sy3">=</span> fileData<span class="sy1">.</span><span class="me1">param6</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;updateProcedure<span class="sy1">.</span><span class="me1">param5</span> <span class="sy1">:</span><span class="sy3">=</span> fileData<span class="sy1">.</span><span class="me1">param7</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;updateProcedure<span class="sy1">.</span><span class="me1">Execute</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">if</span> updateProcedure<span class="sy1">.</span><span class="me1">ResultCode</span> &amp;lt<span class="sy1">;</span>&amp;gt<span class="sy1">;</span> GOOD_RESULT <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">raise</span> Exception<span class="sy1">.</span><span class="me1">Create</span><span class="br0">&#40;</span><span class="st0">'Something went wrong'</span><span class="br0">&#41;</span><span class="sy1">;</span>

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dbData<span class="sy1">.</span><span class="kw3">Delete</span><span class="br0">&#40;</span>i<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="kw1">end</span><span class="sy1">;</span>
&nbsp; &nbsp;<span class="kw1">end</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>Then I profiled the newer version and got very similar results, except that it was spending even more time in the <strong>if</strong> statement to match the objects against each other.  Close to 99% now.  So what had changed?  I looked back through version control and found that the SQL that generates the result set that goes into dbData had been changed between versions.  A new table was added to simplify the big mess of joins, but they forgot one of the <strong>on</strong> criteria, so it was returning three times as many results as it should have.  There&#8217;s your factor of three right there.  Easy enough to fix.  But that still doesn&#8217;t address the quality of the original code.  A couple things jumped right out at me, and I wondered if I could bring the time down below the original mark.</p>
<p>The first thing came out of the SQL profiler.  I kept seeing a call to sp_procedure_params_rowset, an <a href="http://msdn.microsoft.com/en-us/library/ms187961.aspx">undocumented procedure in SQL Server</a> that the connection object uses internally to get information about the expected parameters for a stored procedure, immediately followed by a call to the VERIFICATION_DATA_LOADER proc.  This seemed a bit silly to me.  The signature of the stored procedure isn&#8217;t going to change!  Turns out that was called internally by the CreateStoredProc function, which was being called every time it went to save some data to the database, in order to create the proper object.</p>
<p>So I moved the call to CreateStoredProc out to the main procedure and set it up as an extra parameter to pass into MatchFileDataAgainstDB.  It would reuse the same basic stored procedure object and reassign its parameters for each call, so you get the same net effect, but with 50% less database hits.  Unfortunately, this didn&#8217;t yield a 50% increase in performance.  SQL Server can cache the results of redundant queries, so this call wasn&#8217;t taking much time at all to process repeatedly, but the transport layer overhead was still a factor, and removing this redundant call sped the overall process up by about 20%.</p>
<p>But the big one was in the matching, where the profiler said the system was spending the majority of its time.  It doesn&#8217;t exactly look like a speed bottleneck, because it&#8217;s stored inside a method call, but what it is is a linear search inside of a loop, with both lists containing a few thousand elements each.  But how do you make something like this run faster?  I could try sorting the second list and using a binary search, but have you ever written a binary search?  It&#8217;s a bunch of extra code, and it&#8217;s often confusing and hard to read.  I couldn&#8217;t use a TDictionary to index the second list, because I need to match against 4 items, not just 1.  So instead I used a very simple trick that&#8217;s been around for decades but I don&#8217;t tend to see very often these days: list comparison.</p>
<p>The general algorithm goes like this:</p>
<ol>
<li>Sort both lists by the same criteria.  This must also be the same as the matching criteria.</li>
<li>Start at the top of both lists.  Pick the first item from each and compare them.</li>
<li>If they match, handle the case and advance the index for both lists.</li>
<li>If they don&#8217;t match, loop through, advancing the index for the list with the &#8220;lesser&#8221; value each time, until a match is found.</li>
<li>When you reach the end of either list, you&#8217;re done.  (Unless you want to handle any leftovers from the other list.)</li>
</ol>
<p>This is a very simple and very useful algorithm for reconciling two sets of data, and I&#8217;ve managed to find all sorts of uses for it.  Unlike a double-nested loop, which basically runs in <a href="http://en.wikipedia.org/wiki/Big_O_notation">quadratic time</a>,  this is guaranteed to run in linear time and never walk either list more than once.  I managed to adapt this algorithm to the existing code, and suddenly processing the input files, which had previously taken at least a minute each, takes between 2 and 6 seconds per file.  Now loading the data takes about the same amount of time as performing the calculations, instead of an order of magnitude longer.</p>
<p>Lessons learned:</p>
<ul>
<li>Profilers, especially non-invasive ones, are invaluable for finding what&#8217;s going on in your app.  I&#8217;d have probably noticed that double-nested loop soon enough, but I would never have found the stored procedure issue without SQL Server Profiler to point it out.</li>
<li>Pulling things out of loops—especially other loops!—is a great way to increase performance.</li>
<li>Reducing algorithmic time complexity is by far the best optimization for large data sets.</li>
<li>Linear, single-threaded techniques are still relevant.  A lot of people are talking these days about parallel programming and how the meaning of optimization has changed in today&#8217;s world.  They&#8217;re right, to a certain extent, but as hard as I try I can&#8217;t think of any way to parallelize this check that would make it faster than a simple list comparison.  The only thing I know of with the potential to be faster than this is a hash table lookup, which could be parallelized, but it won&#8217;t work particularly well when you need to look up your values based on more than one index value.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/42/real-world-optimization/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
