<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>TURBU Tech &#187; RTTI</title>
	<atom:link href="http://tech.turbu-rpg.com/tag/rtti/feed" rel="self" type="application/rss+xml" />
	<link>http://tech.turbu-rpg.com</link>
	<description>My thoughts on Delphi programming in general, and particularly on the technical aspects of developing the TURBU engine and editor.</description>
	<lastBuildDate>Fri, 27 Jan 2012 19:53:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>RTTI Script alpha release</title>
		<link>http://tech.turbu-rpg.com/382/rtti-script-alpha-release</link>
		<comments>http://tech.turbu-rpg.com/382/rtti-script-alpha-release#comments</comments>
		<pubDate>Fri, 11 Nov 2011 16:06:57 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=382</guid>
		<description><![CDATA[Way back at the start of this year, I wrote about how I&#8217;d been working on a new Object Pascal-based script engine built around Delphi&#8217;s extended RTTI system.  And then it got real quiet, because more immediate concerns took priority for me.  But I&#8217;ve gotten to the point in my game engine development where I [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://tech.turbu-rpg.com/284/dynamic-class-creation-moving-beyond-the-theoretical">Way back at the start of this year</a>, I wrote about how I&#8217;d been working on a new Object Pascal-based script engine built around Delphi&#8217;s extended RTTI system.  And then it got real quiet, because more immediate concerns took priority for me.  But I&#8217;ve gotten to the point in my game engine development where I really need to put in the scripting system, so I spent the last couple weeks finishing up the implementation and tuning the performance a little, and now I&#8217;ve got an alpha version ready.  If anyone would like to try it out, they can find it <a href="http://code.google.com/p/rtti-script/source/checkout">on Google Code</a>.</p>
<p><span id="more-382"></span>Note: <strong>This is an alpha version</strong>.  There are probably plenty of bugs in it.  There are still features I haven&#8217;t gotten around to implementing.  The sight of the code would probably send Barry Kelly fleeing from the room, screaming in horror.  So use at your own risk, and I take no responsibility if this causes your system to crash, your data to come out all wrong, or <a href="http://www.catb.org/~esr/jargon/html/N/nasal-demons.html">demons to fly out your nose</a>.  Specifically, the <strong>program</strong> keyword is not implemented yet, (only units,) <strong>var</strong> parameters don&#8217;t work yet, and a handful of features aren&#8217;t implemented in the script executor.</p>
<p>Having said that, here&#8217;s a few cool features I&#8217;ve come up with:</p>
<ul>
<li><strong>Class creation:</strong>  Defining a new class inside the script code is not supported yet, but the basic framework is there, and script units implement new classes with their routines as methods, accessible to the script executor through the RTTI system.  I&#8217;ll add class creation pretty soon.</li>
<li><strong>Automatic registration:</strong> Ever try to register a class with the script engine in PascalScript?  It&#8217;s a painful process involving generating a registration unit that basically redeclares your entire class, and then has to be kept in sync or things break.  (And if you get the dependency order wrong, it fails silently,  leading to weird errors further down the line.)  I figured that was way too complicated, so for RTTIScript, all you have to do is register a class reference, and it uses the RTTI system to figure out the internal members and dependencies for you.</li>
<li><strong>Execution environment:</strong> You&#8217;ll often have a bunch of little standalone routines and global variables that you want to import into a script to have available at all times, to provide an interface between the script and your program.  So I introduced the concept of the &#8220;environment:&#8221; You create a class and register it with the compiler, and it treats all the class&#8217;s public methods and properties as globals for the script.  (This also means that you can use property getters and setters to have the script&#8217;s interaction with its &#8220;global variables&#8221; invoke side effects.)</li>
<li><strong>Script saving:</strong> I know <a href="http://delphitools.info/2010/10/26/why-no-bytecode-format/">Eric Grange thinks that a fast compiler is good enough</a> and a bytecode version of a script is not necessary, but I don&#8217;t agree.  By his own tests, even a very fast script compiler can take 2 seconds to compile a 100,000 line script.  I&#8217;ve actually got an example of one that big in a project that the author wants to convert over once the TURBU project is ready.  Adding a two-second lag into the loading process and making the player wait while a level loads, which should feel instantaneous, is unacceptable.  I don&#8217;t have script saving implemented yet, but it&#8217;s definitely on the to-do list.</li>
</ul>
<p>Anyway, the alpha version is available for any <del>guinea pigs</del> curious developers out there who&#8217;d like to take a look at it.  It requires Delphi XE, it&#8217;s pretty fast, and when I get the rest of the features set up it&#8217;s going to be awesome!  Feel free to check it out, and to <a href="http://code.google.com/p/rtti-script/issues/list">submit a bug report</a> if you find some errors. (Which I&#8217;m sure plenty of people will.)</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/382/rtti-script-alpha-release/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>XE2: TValue is much faster now</title>
		<link>http://tech.turbu-rpg.com/363/xe2-tvalue-is-much-faster-now</link>
		<comments>http://tech.turbu-rpg.com/363/xe2-tvalue-is-much-faster-now#comments</comments>
		<pubDate>Wed, 07 Sep 2011 17:35:58 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi XE2]]></category>
		<category><![CDATA[RTTI]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=363</guid>
		<description><![CDATA[About a year and a half ago, I reported on how slow the original implementation of TValue in Delphi 2010 was, touching off a storm of comments and various other blog posts as other Delphi community members conducted similar experiments.  One thing that came out of it was a suggestion by Robert Love on how [...]]]></description>
			<content:encoded><![CDATA[<p>About a year and a half ago, <a href="http://tech.turbu-rpg.com/100/tvalue-is-very-slow">I reported on how slow the original implementation of TValue in Delphi 2010 was</a>, touching off a storm of comments and various other blog posts as other Delphi community members conducted similar experiments.  One thing that came out of it was a suggestion by Robert Love on how to improve performance by adding code to optimize for the most common cases.<span id="more-363"></span></p>
<p>I expanded on his idea and sent a suggested implementation to Barry Kelly.  Unfortunately, it didn&#8217;t make it into XE, but it&#8217;s in XE2 now.  It&#8217;s not exactly the code I suggested&#8211;a few things have changed in the way TValue works internally since D2010, and there&#8217;s at least one bugfix that I can see&#8211;but the basic idea is there.  When storing values into a TValue and retrieving them again, there&#8217;s special code to set and retrieve the most common types directly, without having to go into the generic RTTI-based (much slower) conversion routines.</p>
<p>I reran the timing test under XE2, and the results were much better this time:</p>
<p>Variants: 2140<br />
TValues: 2895</p>
<p>Still about 30% slower than Variants, but at least it&#8217;s not 2000% slower anymore.  Thanks, Barry, and thanks to Robert too for the original suggestion!</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/363/xe2-tvalue-is-much-faster-now/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Firebird and booleans: one more hurdle</title>
		<link>http://tech.turbu-rpg.com/332/firebird-and-booleans-one-more-hurdle</link>
		<comments>http://tech.turbu-rpg.com/332/firebird-and-booleans-one-more-hurdle#comments</comments>
		<pubDate>Tue, 03 May 2011 02:30:55 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>
		<category><![CDATA[RTTI Surgery]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=332</guid>
		<description><![CDATA[I wrote my last post about enabling booleans in Firebird after several hours of poking around in database code trying to get my query to execute without errors.  Once it worked, everything seemed great.  But I missed an important step: I hadn&#8217;t tried to write anything back to the database yet. Both Firebird DBX drivers [...]]]></description>
			<content:encoded><![CDATA[<p>I wrote my last post about enabling booleans in Firebird after several hours of poking around in database code trying to get my query to execute without errors.  Once it worked, everything seemed great.  But I missed an important step: I hadn&#8217;t tried to write anything <em>back</em> to the database yet.</p>
<p><span id="more-332"></span>Both Firebird DBX drivers blew up on me when I tried to do this, in different ways.  The Embarcadero driver may or may not have had a problem with the booleans, but I never really got the chance to test that out because the object I was trying to write contained a blob field, and I got an exception from Firebird: &#8220;Incorrect values within sqlda structure.&#8221;  A bit of Googling revealed that this is a known issue with writing blobs to Firebird that&#8217;s been in the Embarcadero driver for several years now. Apparently it&#8217;s still around.  Not much I can do about that without the source code.</p>
<p>So I tried Chau Chee Yang&#8217;s driver.  It didn&#8217;t have any trouble with the blobs, but it did choke on writing a Boolean field.  I traced into the call into the database driver and looked at the assembly, and as near as I can tell, his implementation of the DBXWritableRow_SetBoolean function is:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">begin</span> 
&nbsp;<span class="kw3">assert</span><span class="br0">&#40;</span><span class="kw2">false</span><span class="br0">&#41;</span><span class="sy1">;</span> 
&nbsp;result <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span> 
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>Not hard to guess why.  Firebird doesn&#8217;t support booleans, so no need to waste time writing a method for it.  However, this <em>is</em> something I can fix without access to the driver source, with a little bit more RTTI surgery.</p>
<p>The standard version of the BOOLEAN domain is implemented as a special-case version of a smallint.  So what we need is a way to write the value back out as a smallint instead of a boolean.  When the driver DLL gets loaded by the DBExpress engine, it builds a method table object containing a bunch of function pointers to various functions in the DLL.  One of them is the aforementioned DBXWritableRow_SetBoolean.  There&#8217;s also a DBXWritableRow_SetInt16 that deals with writing out smallints.  So a redirector function is easy enough to write:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">var</span>
&nbsp; LSetSmallint<span class="sy1">:</span> TDBXWritableRow_SetInt16<span class="sy1">;</span>

<span class="kw1">function</span> Passthrough_SetBoolean<span class="br0">&#40;</span>Handle<span class="sy1">:</span> TDBXWritableRowHandle<span class="sy1">;</span>
&nbsp; Ordinal<span class="sy1">:</span> TInt32<span class="sy1">;</span> Value<span class="sy1">:</span> <span class="kw4">LongBool</span><span class="br0">&#41;</span><span class="sy1">:</span> TDBXErrorCode<span class="sy1">;</span> <span class="kw1">stdcall</span><span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; <span class="kw1">if</span> value <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp;result <span class="sy1">:</span><span class="sy3">=</span> LSetSmallint<span class="br0">&#40;</span>handle<span class="sy1">,</span> ordinal<span class="sy1">,</span> 1<span class="br0">&#41;</span>
&nbsp; <span class="kw1">else</span> result <span class="sy1">:</span><span class="sy3">=</span> LSetSmallint<span class="br0">&#40;</span>handle<span class="sy1">,</span> ordinal<span class="sy1">,</span> 0<span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>(Yes, I need that if statement in there instead of simply calling Ord(value).  As a LongBool, any nonzero value is considered True, and sometimes you&#8217;ll get things other than 1, for whatever reason.)  LSetSmallint represents the current driver&#8217;s DBXWritableRow_SetInt16 function.  Now you&#8217;ve just got to insert it into the method table.  It&#8217;s buried under several inaccessible layers of encapsulation, of course, but the RTTI objects can dig it out.</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="kw1">procedure</span> FixConnection<span class="br0">&#40;</span>connection<span class="sy1">:</span> TSqlConnection<span class="br0">&#41;</span><span class="sy1">;</span>
<span class="kw1">var</span>
&nbsp; &nbsp;ctx<span class="sy1">:</span> TRttiContext<span class="sy1">;</span>
&nbsp; &nbsp;cls<span class="sy1">:</span> TRttitype<span class="sy1">;</span>
&nbsp; &nbsp;fld<span class="sy1">:</span> TRttiField<span class="sy1">;</span>
&nbsp; &nbsp;driver<span class="sy1">:</span> TDBXDriver<span class="sy1">;</span>
&nbsp; &nbsp;table<span class="sy1">:</span> TDBXMethodTable<span class="sy1">;</span>
<span class="kw1">begin</span>
&nbsp; &nbsp;cls <span class="sy1">:</span><span class="sy3">=</span> ctx<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span>connection<span class="sy1">.</span><span class="me1">DBXConnection</span><span class="sy1">.</span><span class="me1">ClassType</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;driver <span class="sy1">:</span><span class="sy3">=</span> cls<span class="sy1">.</span><span class="me1">GetField</span><span class="br0">&#40;</span><span class="st0">'FDriverDelegate'</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">GetValue</span><span class="br0">&#40;</span>connection<span class="sy1">.</span><span class="me1">DBXConnection</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsType</span>&lt;TDBXDriver&gt;<span class="sy1">;</span>

&nbsp; &nbsp;cls <span class="sy1">:</span><span class="sy3">=</span> ctx<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span>driver<span class="sy1">.</span><span class="me1">classtype</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;driver <span class="sy1">:</span><span class="sy3">=</span> cls<span class="sy1">.</span><span class="me1">GetField</span><span class="br0">&#40;</span><span class="st0">'FDriver'</span><span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">GetValue</span><span class="br0">&#40;</span>driver<span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsType</span>&lt;TDBXDynalinkDriver&gt;<span class="sy1">;</span>

&nbsp; &nbsp;fld <span class="sy1">:</span><span class="sy3">=</span> ctx<span class="sy1">.</span><span class="me1">GetType</span><span class="br0">&#40;</span>TDBXDynalinkDriver<span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">GetField</span><span class="br0">&#40;</span><span class="st0">'FMethodTable'</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp;table <span class="sy1">:</span><span class="sy3">=</span> fld<span class="sy1">.</span><span class="me1">GetValue</span><span class="br0">&#40;</span>driver<span class="br0">&#41;</span><span class="sy1">.</span><span class="me1">AsType</span>&lt;TDBXMethodTable&gt;<span class="sy1">;</span>
&nbsp; &nbsp;table<span class="sy1">.</span><span class="me1">FDBXWritableRow_SetBoolean</span> <span class="sy1">:</span><span class="sy3">=</span> Passthrough_SetBoolean<span class="sy1">;</span>
&nbsp; &nbsp;LSetSmallint <span class="sy1">:</span><span class="sy3">=</span> table<span class="sy1">.</span><span class="me1">FDBXWritableRow_SetInt16</span><span class="sy1">;</span>
<span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>This has to be run after you open the connection but before you try to write any booleans out.</p>
<p>BTW I emailed Chau Chee Yang about all this.  He wrote back that he plans to support booleans in his driver &#8220;as soon as possible.&#8221;  So let&#8217;s hope that he finds the time soon, and that Embarcadero is able to free up some of their limited development resources to fix up their driver issues.  Tricks like this may be fun to dig around and build workarounds for, but in the end RTTI surgery is kind of an ugly hack that violates encapsulation, and it would be nice to not have to use it.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/332/firebird-and-booleans-one-more-hurdle/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Adding boolean support to Firebird+DBX</title>
		<link>http://tech.turbu-rpg.com/324/adding-boolean-support-to-firebirddbx</link>
		<comments>http://tech.turbu-rpg.com/324/adding-boolean-support-to-firebirddbx#comments</comments>
		<pubDate>Sun, 01 May 2011 04:38:14 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>
		<category><![CDATA[RTTI Surgery]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[sql]]></category>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=296</guid>
		<description><![CDATA[Over the past couple weeks, I&#8217;ve been working on refining and testing my RTTI generation and the scripting system I&#8217;ve been building on top of it, which I&#8217;ve decided to call RTTI Script.  I think I&#8217;m finally starting to get something ready for public consumption.  I set up a Google Code repository this morning for [...]]]></description>
			<content:encoded><![CDATA[<p>Over the past couple weeks, I&#8217;ve been working on refining and testing my RTTI generation and the scripting system I&#8217;ve been building on top of it, which I&#8217;ve decided to call RTTI Script.  I think I&#8217;m finally starting to get something ready for public consumption.  I set up a Google Code repository this morning for <a href="http://code.google.com/p/rtti-script/">RTTI Script</a>, but so far all it contains is the RTTI generation code.  The actual compiler and script executor still need some work.<span id="more-296"></span></p>
<p>I&#8217;m able to build and run some simple scripts, but I don&#8217;t have all the language features implemented yet, and I&#8217;m waiting on a bugfix for an issue in <a href="http://alex.ciobanu.org/?p=430">Collections</a> that&#8217;s causing trouble for me.  (A big thanks to Alex for putting up with all my bug reports and feature requests, BTW!)</p>
<p>RTTI generation probably sounds intimidating, but the framework for it all condenses down to about 1200 lines of code spread across 4 units.  There are a couple more minor things I still need to add to it, but it&#8217;s not all that complicated when you get down to it.  Here&#8217;s a quick overview of the four units and what they do:</p>
<p><strong>vmtStructure</strong>: A very simple unit that redefines the VMT of a class and several of the RTTI structures from TypInfo.pas in ways that are easier to work with.  A lot of it is based on <a href="http://hallvards.blogspot.com/2006/03/hack-8-explicit-vmt-calls.html">work by Hallvard Vassbotn</a>.</p>
<p><strong>vmtBuilder</strong>: This unit has four routines for creating parts of the VMT and RTTI tables.  The RTTI structures can be very fiddly; they pack data in ways that is not actually supported by the Object Pascal language, such as inline variable-length arrays.  Reading them (or writing them) requires a lot of pointer-based math, so I wrote up these routines to encapsulate the scary work.  Just pass in the data that the structures are supposed to contain, and they&#8217;ll arrange it for you.</p>
<p>The vmtBuilder unit also introduces an interface called IBuffer, which provides a simple way to pack arbitrary data together.  It&#8217;s mostly just for internal use by the other RTTI-creation routines.</p>
<p><strong>newClass</strong>: Here&#8217;s where the real fun begins.  The TNewClass class contains a bunch of methods for defining your new class itself.  You create the TNewClass, call the Add*Field methods (AddIntegerField, AddObjectField, etc.) in order, call AddMethod for each method, and then call the ClassPtr method, which takes all the information you&#8217;ve fed it and turns it into a new VMT and accompanying RTTI tables.  You can optionally pass a TPrivateHeap instance to ClassPtr, which will place the new data into the private heap instead of the standard application memory.  (I put that in because it&#8217;s very useful for dealing with an arcane but important detail of the RTTI system.)</p>
<p>TNewClass currently does not support creating properties, because they&#8217;re just syntactic sugar and I was focused on real functionality.  This will change once my script compiler gets to the point that it requires them.  It also doesn&#8217;t support creating classes that implement interfaces.  That would add a lot of complexity and I don&#8217;t think it&#8217;s worth the effort.  But the whole thing&#8217;s open source, so if someone needs it and feels like creating a patch for it, they&#8217;d be welcome to.</p>
<p><strong>rttiPackage</strong>: Here&#8217;s the part where I stop drawing on Hallvard&#8217;s work and instead owe a big debt of gratitude to Barry Kelly for explaining some of the trickier details.  If you only want to create a new class, you could stop with the three units above.  But if you want to integrate it into the RTTI system so you can get at its members, (which is basically the only way to use it since they aren&#8217;t available at compile time,) you need a way to feed all this new RTTI you&#8217;ve created into RTTI.pas.  That&#8217;s what the rttiPackage unit does.</p>
<p>It contains two new interfaces, INewUnit and IRttiPackage, and classes that implement them.  INewUnit can contain any number of TNewClass instances, and it organizes them so the RTTI system thinks they were all declared in the same unit.  Then IRttiPackage is the construct that pulls everything together.  It collects INewUnit instances and creates and registers a new TLibModule record, which makes RTTI.pas think you&#8217;ve loaded a new runtime package, so it loads the RTTI tables for the (fake) new package, containing the data for your new classes.</p>
<p>IRttiPackage.Install, the method that sets up all the data, can take an optional reference to a procedure that allows you to alter the code address of your new classes&#8217; methods at install time.  This was to support the script engine.  When the script compiler builds the metadata for the methods, the actual code address doesn&#8217;t exist yet because the RTTI hasn&#8217;t been loaded, so it sets the code address as the offset within the script.  (This will also make it a lot easier to save the script and load it later.)  Then when the RTTI table gets loaded as the script engine is starting up, it can use TRttiMethod.CreateImplementation to build a bridge between native Delphi land and the script engine, and set the implementation stub&#8217;s address to the official code address for the new method.  It&#8217;s kind of an ugly trick, but it works, and it&#8217;s the only way I could find to make it work.</p>
<p>I mentioned in my last post that the compiler will require Delphi XE because it uses this trick. I&#8217;ll have to revise that further.  There&#8217;s a glitch in the setup code for Method Implementations in XE RTM that can cause bogus FPU exceptions if you use it enough. This was fixed in XE Update 1, so that will be the minimum requirements for the script compiler when it&#8217;s ready.  The RTTI generation code itself, though, will work under D2010, if anyone has other uses for it.</p>
<p>More to come soon&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/296/rtti-generation-code-now-available/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Dynamic class creation: moving beyond the theoretical</title>
		<link>http://tech.turbu-rpg.com/284/dynamic-class-creation-moving-beyond-the-theoretical</link>
		<comments>http://tech.turbu-rpg.com/284/dynamic-class-creation-moving-beyond-the-theoretical#comments</comments>
		<pubDate>Sat, 22 Jan 2011 00:35:19 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Class Creation]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>
		<category><![CDATA[Scripting]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=284</guid>
		<description><![CDATA[A few years back, I ran across this post by Hallvard Vassbotn.  (It&#8217;s a shame he stopped blogging, because he always had some very interesting stuff about the technical details of how stuff in Delphi works.)  At the bottom was a paragraph that really fascinated me: On a more technical level it suffices to say [...]]]></description>
			<content:encoded><![CDATA[<p>A few years back, I ran across <a href="http://hallvards.blogspot.com/2007/05/hack17-virtual-class-variables-part-ii.html">this  post</a> by Hallvard Vassbotn.  (It&#8217;s a shame he stopped blogging, because he always had some very interesting stuff about the technical details of how stuff in Delphi works.)  At the bottom was a paragraph that really fascinated me:</p>
<blockquote><p>On a more technical level it suffices to say that they use custom and  extremely compact and fast data structures, tricks and hacks to be able  to represent millions and millions of objects within the constraints of a  32-bit Windows system. Throw in the use of Physical Address Extensions,  storing per-class information in &#8220;virtual&#8221; class vars to reduce object  instance size, creation of classes and their VMTs dynamically at runtime  (!!), pointer packing, multithreading, the list just goes on and on.</p></blockquote>
<p><span id="more-284"></span>I figured if Hallvard&#8217;s friend can create classes and their VMTs dynamically at runtime, then anyone could, with the right level of technical knowledge about how a VMT works.  My CodeRage presentation in 2009 was about theoretical research in that area, demonstrating that it could be done.  But then I just sorta sat on it.  It was a cool trick, but I didn&#8217;t have anything useful I could do with it, and  I had more important things to work on, like my game engine.</p>
<p>But a few months ago I started ran into some serious issues with making the scripting for my game engine work.  I&#8217;ve been using PascalScript, but its support for units is badly broken (and there&#8217;s no good way to fix it without basically rearchitecting the whole system) and its GOTO support is just broken enough to cause lots of trouble for me.  (I need to support GOTOs properly.  Don&#8217;t ask.) <img src='http://tech.turbu-rpg.com/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> </p>
<p>So I looked at DWS, which has been getting a lot of hype lately with all the recent development.  But it also has bad unit support, no GOTOs at all, and no way to save a compiled script.  I raised each of these issues with Eric and he gave reasons why he has no intention to fix each of them.</p>
<p>There&#8217;s one interesting thing DWS can do, though: you can define new classes and use them in the script engine.  Being able to do this in my game engine would allow for a lot of customization opportunities.  But a bit of testing demonstrated that, like the holographic Professor Moriarty on Star Trek: The Next Generation, these newly-defined objects are purely virtual constructs that can&#8217;t step out into the real world.  If you try to bring a reference to one of these script objects into Delphi-land, you get a <strong>nil</strong> instead.  This greatly reduces the opportunity for customization.</p>
<p>So I figured I may as well try my hand at my own version, drawing on my class-creation research.  For the last few months, with a lot of hard work (and a fair bit of technical assistance from Barry Kelly on the arcane details of the RTTI system) I&#8217;ve been putting together a new Object Pascal-based script compiler.  It&#8217;s not completely finished yet, but the features that will set it apart are up and running:</p>
<ul>
<li>Unit-based compilation.  All your code doesn&#8217;t have to be in one unit.  It also doesn&#8217;t expect that you&#8217;re building a single script to be executed directly.  You <em>can</em> do that, but you can also run a valid compile cycle made up entirely of one or more units, with no <strong>program</strong> unit and no main routine, and use it as a library, calling individual routines from the script engine.  Units can be compiled and saved individually, similar to Delphi&#8217;s DCU system, and linked with other units to form a script program.</li>
<li>Dynamic, RTTI-based creation of new classes.  You can define a new class in the script engine and the compiler will generate a new VMT and all the extended RTTI necessary to use this class from native Delphi code just like any other.  For example, you can create a class that descends from a native type, override a virtual method, pass an instance out of the script engine to a variable reference, call the virtual method, and the script engine will be invoked to run it.  (This particular trick require the TMethodImplementation class to set up, so Delphi XE or later will be required.)</li>
</ul>
<p>The plan is to use the RTTI capabilities to achieve the most seamless integration possible between scripts and native code.  In my next few posts I&#8217;ll be going over some details as to how I made this all work.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/284/dynamic-class-creation-moving-beyond-the-theoretical/feed</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Smaller, cleaner RTTI coming?</title>
		<link>http://tech.turbu-rpg.com/274/smaller-cleaner-rtti-coming</link>
		<comments>http://tech.turbu-rpg.com/274/smaller-cleaner-rtti-coming#comments</comments>
		<pubDate>Fri, 31 Dec 2010 02:47:51 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Class Creation]]></category>
		<category><![CDATA[crystal ball]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=274</guid>
		<description><![CDATA[One of the biggest complaints about the extended RTTI introduced in Delphi 2010 is the way it adds so much to the size of your EXE.  Well, in a recent StackOverflow answer, Barry Kelly hinted that the format of the basic RTTI structures in TypInfo.pas are &#8220;more likely to change from version to version now [...]]]></description>
			<content:encoded><![CDATA[<p>One of the biggest complaints about the extended RTTI introduced in Delphi 2010 is the way it adds so much to the size of your EXE.  Well, in <a href="http://stackoverflow.com/questions/4562982/rtti-delphi-create-as-tvalue-an-n-dimensional-matrix/4567252#4567252">a recent StackOverflow answer</a>, Barry Kelly hinted that the format of the basic RTTI structures in TypInfo.pas are &#8220;more likely to change from version to version now that it has a much higher level abstraction in the Rtti unit.&#8221;<span id="more-274"></span></p>
<p>I mentioned in a comment that I wouldn&#8217;t like to see much change in the RTTI data structures because it would make <a href="http://tech.turbu-rpg.com/3/dynamic-class-creation">dynamic class creation</a> code difficult to maintain, and I&#8217;ve been doing some serious work in that area recently. Barry responded with the rationale:</p>
<blockquote><p>It&#8217;s less a case of completely changing, than  having more data appended on the end. It&#8217;s a real PITA to jam in extra  data without breaking code. As it is, the RTTI format contributes a  fairly substantial size penalty largely because its encoding can&#8217;t be  optimized much. It would be nice to consider something like e.g. .NET  metadata tables, which use a conciser representation. Instead, Delphi  RTTI is left with every xref taking up a minimum of 4 bytes, and  frequently with redundant strings for names that could be pooled. But  that can&#8217;t happen within the current framework.</p></blockquote>
<p>He mentions two things here: changing the way references work to make the RTTI take up less space,  and making it less difficult to update the RTTI structures with new information.  I could get behind something like that, especially since the update would almost by necessity involve cleaning up the type definitions in the process.  It&#8217;s a real pain to work with some of the existing RTTI structures, because you get stuff like this:</p>
<pre>
<div class="codesnip-container" >
<div class="delphi codesnip" style="font-family:monospace;"><span class="coMULTI">{ vmtFieldTable entry in VMT }</span>
&nbsp; PVmtFieldTable <span class="sy3">=</span> <span class="sy2">^</span>TVmtFieldTable<span class="sy1">;</span>
&nbsp; TVmtFieldTable <span class="sy3">=</span> <span class="kw1">packed</span> <span class="kw1">record</span>
&nbsp; &nbsp; Count<span class="sy1">:</span> <span class="kw4">Word</span><span class="sy1">;</span> <span class="co1">// Published fields</span>
&nbsp; &nbsp; ClassTab<span class="sy1">:</span> PVmtFieldClassTab<span class="sy1">;</span>
&nbsp; &nbsp;<span class="coMULTI">{Entry: array[1..Count] of TVmtFieldEntry;
&nbsp; &nbsp; ExCount: Word;
&nbsp; &nbsp; ExEntry: array[1..ExCount] of TVmtFieldExEntry;}</span>
&nbsp; <span class="kw1">end</span><span class="sy1">;</span></div>
</div>
</pre>
<p>Why are those last three fields commented out?  Because they&#8217;re declared as static arrays, but the upper bound isn&#8217;t known at compile time and changes from one instance to another.  Basically, they contain dynamic arrays inserted inline into the record, which is something that Delphi&#8217;s type system doesn&#8217;t support.  And since the type system doesn&#8217;t support it and the compiler can&#8217;t locate the members of the records, extracting data from a TVmtFieldTable (or several other structures that do similar things) is a real pain.</p>
<p>RTTI that&#8217;s easier to work with <em>and</em> takes up less space in the EXE?  If they can pull it off, I&#8217;d love to see it!</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/274/smaller-cleaner-rtti-coming/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>First look at Delphi XE</title>
		<link>http://tech.turbu-rpg.com/181/first-look-at-delphi-xe</link>
		<comments>http://tech.turbu-rpg.com/181/first-look-at-delphi-xe#comments</comments>
		<pubDate>Wed, 01 Sep 2010 05:41:01 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi XE]]></category>
		<category><![CDATA[Generics]]></category>
		<category><![CDATA[RTTI]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=181</guid>
		<description><![CDATA[This week&#8217;s just getting started, and it&#8217;s already had more than enough awesomeness to pack into a typical month.  The new version of Delphi came out yesterday.  Metroid: Other M and The Way of Kings, a new Brandon Sanderson book, were both released today.  (I have a feeling I&#8217;m going to be more distracted than [...]]]></description>
			<content:encoded><![CDATA[<p>This week&#8217;s just getting started, and it&#8217;s already had more than enough awesomeness to pack into a typical month.  The new version of Delphi came out yesterday.  <em>Metroid: Other M</em> and <em>The Way of Kings,</em> a new <a href="http://tvtropes.org/pmwiki/pmwiki.php/Main/BrandonSanderson">Brandon Sanderson</a> book, were both released today.  (I have a feeling I&#8217;m going to be more distracted than usual for a while&#8230;) But as awesome as Metroid games and anything by Sanderson tend to be, (if you&#8217;re into fantasy <em>at all</em>, check out <a href="http://www.amazon.com/Elantris-Brandon-Sanderson/dp/0765350378/">Elantris</a> and <a href="http://www.amazon.com/Mistborn-Trilogy-Boxed-Brandon-Sanderson/dp/076536543X">Mistborn</a> and prepare to be blown away,) this is a programming blog, and I&#8217;m supposed to be talking about Delphi.  So here&#8217;s the good, the bad, and the annoying about my first impressions with Delphi XE.</p>
<p><span id="more-181"></span>The first thing I knew about the release was when I received my SA email yesterday afternoon, as opposed to having to wait a few days or even weeks to get my upgrade as has happened in previous years.  Big improvement there, and I hope Embarcadero keeps it up for subsequent releases. <strong>Good.</strong></p>
<p>No OSX support, which we all thought was going to be in this release up until a few weeks ago. <strong>Bad.</strong> But it&#8217;s not there because the people at Embarcadero preferred to focus on quality and not release something that wasn&#8217;t ready. <strong>Good.</strong> No one wants to see (or pay for) another D8.</p>
<p>So I open it up and start browsing through some code.  My settings from D2010 didn&#8217;t get migrated forward. <strong>Annoying.</strong> There really should be some way to import them as part of the installation process.</p>
<p>ErrorInsight is the same old same old, with false positives all over the place because it can&#8217;t find basic standard library units. <strong>Annoying.</strong> But at least it doesn&#8217;t choke on methods of objects from a generic list anymore. <strong>Good.</strong></p>
<p>Speaking of Generics, huge improvement here.  I was able to go through my codebase and remove all the annoying workarounds for generics-related glitches, and it compiles and runs properly now! <strong>Good.</strong></p>
<p>When it&#8217;s time to debug, I immediately noticed a few improvements.  We finally, finally, after all these years, have debug DCPs available for the standard libraries.  (For those not familiar with them, DCPs are to Delphi packages as DCUs are to units.)  That means that people like me who build stuff with packages are able to trace into the RTL when debugging.  <strong>Good.</strong></p>
<p>Also, something that always drove me up the wall about the debugger has been fixed.  When you put the cursor in an expression and hit CTRL-F7, it pops up the Evaluate/Modify window and evaluates whatever you had the cursor on.  But if you moved the cursor and hit CTRL-F7 again without closing the Evaluate/Modify window, it would focus the Evaluate/Modify window, but it wouldn&#8217;t evaluate the new expression you had selected.  You&#8217;d have to close the window and hit CTRL-F7 again to get a new evaluation.  (Or copy/paste or type it in, of course.)  That always bugged me, but it&#8217;s been fixed in XE.  <strong>Good.</strong></p>
<p>And while we&#8217;re on the subject of Evaluate/Modify, you can now select more than whatever the limit used to be (80 characters?) and have your entire selection copied when you hit CTRL-F7. <strong>Good.</strong></p>
<p>But while poking around in there, I found a new way to crash the IDE.  Try to evaluate a call that will open a form, and it locks up Evaluate/Modify.  Then hit CTRL-F2 to kill the debugger, and it brings the whole program down. <strong>Bad.</strong> (Not sure if this was in earlier versions, though. I&#8217;ve never run across it before.)</p>
<p>Oh, there&#8217;s a new regular expression library in the RTL.  Some people will probably find this a good thing.  I personally see it as <strong>Annoying.</strong> Delphi code is supposed to be easy to read.  That&#8217;s an explicit design goal going all the way back to Wirth.  But PCREs are anything but, and having it right there in the standard library will just encourage people to use them in code that I might end up having to debug some day.</p>
<p>In addition to Generics bugs getting cleaned up, the Generics.Defaults and Generics.Collections units got a bit of polish.  There were some minor tweaks to speed up various low-level comparison functions in Generics.Defaults.  Over in Generics.Collections, all the collections have gained a ToArray method, TStack&lt;T&gt; and TQueue&lt;T&gt; gained a Capacity, and there&#8217;s a new TThreadedQueue&lt;T&gt; class that looks a lot like the <a href="http://17slon.com/blogs/gabr/2010/01/three-steps-to-blocking-collection-1.html">Blocking Collection</a> that Primoz Gabrijelcic set up for OmniThreadLibrary, only without all the Omni stuff.  These new features look <strong>Good.</strong></p>
<p>The Math unit has been given overloads for Single, Double and Extended precision versions of just about all its calculation routines, which is <strong>Good</strong> because there&#8217;s no more implicit conversions to introduce rounding errors.  However, I had this bite me when compiling some code at work.  Someone was passing an array of integers to Math.Sum, which worked before, but now the compiler gave an ambiguous overload error for that. <strong>Annoying,</strong> but not too hard to fix, since there&#8217;s a SumInt function right after Sum.  Not sure why the original author of this code didn&#8217;t use it in the first place.  Oh well&#8230;</p>
<p>In RTTI.pas, there&#8217;s a new class called TMethodImplementation.  You create it and give it data that describes the signature of a method, then pass it an anonymous method that takes an array of TValues and returns a TValue, and it creates a little thunk of machine code that builds a function with the correct signature that invokes your anonymous method.  Very cool, lots of potential&#8230; except that you can&#8217;t use it.  The constructor&#8217;s private, and there&#8217;s a public constructor that raises an exception, so you can&#8217;t get at it via TObject.Create.  It&#8217;s apparently for internal use only.  <strong>Annoying.</strong> I asked Allen Bauer about this, and he said that it&#8217;s because they weren&#8217;t sure they had the final form of the class worked out yet, and they didn&#8217;t want to make it publicly available yet in a form that they&#8217;d end up having to preserve and support forever afterwards.</p>
<p>There&#8217;s also a new class called TVirtualMethodInterceptor that <em>is </em>publicly available, and uses TMethodImplementation internally to allow you to replace virtual methods on a class.  I&#8217;m a bit curious as to what this is useful for, but apparently the team thought it was good for something, because a lot of work went into setting it up. <strong>Good?</strong></p>
<p>Oh, and apparently certain aspects of the compiler have slowed down.  Most things will compile about the same speed or even a little faster, but for really large projects (millions of lines) with complex interdependencies between units, you&#8217;ll notice some slowdown, and it&#8217;ll apparently get worse the larger your project is.  I timed it at work, on a project of about 3.5 million lines of code.  It builds in about 2 minutes on D2010, closer to 3 minutes on XE.  <strong>Bad,</strong> but still a heck of a lot better than the C family could do.  And apparently it has something to do with making Generics work right, so I can tolerate that.  Hopefully they&#8217;ll find some way to regain some of that lost speed in updates, though.</p>
<p>Oh, and there are little {$IFDEF POSIX} and {$IFDEF MACOS} tags scattered throughout the RTL and VCL, even more than there were in Delphi 2010.  This is <strong>Good</strong> if you&#8217;re looking forward to OSX or Linux cross-platform development in the next release(s).  I haven&#8217;t seen any 64-bit ifdefs, but that could be because it doesn&#8217;t require much change at the Pascal level, the way a completely different OS platform does. (UPDATE: According to PhiS, there&#8217;s a lot of {$IFDEF CPUX64} tags in System.pas.  I was poking around the libraries with BeyondCompare last night but I didn&#8217;t bother to look in there because I knew that, with all the cross-platform stuff and all the ASM they&#8217;ve got in System.pas, the diff view would be a big, confusing mess no matter what.)</p>
<p>Well, it&#8217;s getting late and I haven&#8217;t had time to dig through too much of the new stuff yet, (and with a new 1000-page novel and a new video game, it might be a while,) but overall this is looking like a pretty good release.  Not a huge leap forward like the last two were, but a lot of polish and incremental improvements.  I think this is going to be a pretty good release overall.</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/181/first-look-at-delphi-xe/feed</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Adding non-data fields to a client dataset</title>
		<link>http://tech.turbu-rpg.com/122/adding-non-data-fields-to-a-client-dataset</link>
		<comments>http://tech.turbu-rpg.com/122/adding-non-data-fields-to-a-client-dataset#comments</comments>
		<pubDate>Wed, 21 Apr 2010 18:56:08 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>
		<category><![CDATA[RTTI Surgery]]></category>

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

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

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

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

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

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

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

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=104</guid>
		<description><![CDATA[This morning on StackOverflow, someone asked a question about invoking constructors through extended RTTI. It happened that I&#8217;d had to work out a way to do that a couple weeks ago, and it took me about 5 minutes to find the code, strip out a few specific details and type up a solution.  I got [...]]]></description>
			<content:encoded><![CDATA[<p>This morning on StackOverflow, someone asked <a href="http://stackoverflow.com/questions/2500542/delphi-2010-whatever-happened-to-trtticonstructor">a question about invoking constructors through extended RTTI</a>.  It happened that I&#8217;d had to work out a way to do that a couple weeks ago, and it took me about 5 minutes to find the code, strip out a few specific details and type up a solution.  I got an accepted response, a few upvotes, and a very interesting comment from the author:</p>
<p><span id="more-104"></span>&#8220;Thanks again Mason! You (and the other folks here at SO) pretty much  close the documentation gap. Thank you so much. + 1&#8243;</p>
<p>Now, StackOverflow is a wonderful resource.  I&#8217;ve been using it and loving it ever since <a href="http://www.delphi.org">Jim McKeeth</a> introduced me to it about a year and a half ago.  But I&#8217;ve never really thought of it that way.  It makes me wonder.</p>
<p>StackOverflow has been a huge success ever since it was launched.  It&#8217;s what Experts-Exchange should have been.  But I sort of got the impression that it&#8217;s supposed to be for asking somewhat difficult technical questions, whatever &#8220;somewhat difficult&#8221; happens to mean to you at your current level of competence.  If Delphi users are instead using it to compensate for bad documentation, maybe there&#8217;s a more appropriate way to go about doing that?</p>
<p>I know that Embarcadero has a documentation wiki set up.  Ideally, that&#8217;s where documentation issues ought to be resolved.  Perhaps there would be some way to create a request/answer system for improving the quality of the documentation on the wiki?  There would need to be some way to create documentation requests, a way to respond to them and tie your responses to doc wiki pages, and some way to actually bring users in and get them to participate.  Does anyone have any thoughts as to how this could be accomplished?</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/104/closing-the-documentation-gap/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

