<?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/category/delphi/rtti-delphi/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>Run-time stack information?</title>
		<link>http://tech.turbu-rpg.com/292/run-time-stack-information</link>
		<comments>http://tech.turbu-rpg.com/292/run-time-stack-information#comments</comments>
		<pubDate>Mon, 31 Jan 2011 16:15:08 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[crystal ball]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[RTTI]]></category>

		<guid isPermaLink="false">http://tech.turbu-rpg.com/?p=292</guid>
		<description><![CDATA[Just in case you haven&#8217;t listened to it yet, Jim McKeeth over at Delphi.org posted a new podcast last week. He did an interview with Allen Bauer that apparently ran for about two hours, so he split it up into two parts.  The second part isn&#8217;t up yet, but there&#8217;s a lot of interesting stuff [...]]]></description>
			<content:encoded><![CDATA[<p>Just in case you haven&#8217;t listened to it yet, Jim McKeeth over at Delphi.org posted <a href="http://delphi.org/2011/01/44-allen-bauer/">a new podcast</a> last week. He did an interview with Allen Bauer that apparently ran for about two hours, so he split it up into two parts.  The second part isn&#8217;t up yet, but there&#8217;s a lot of interesting stuff in the first one.  But one of the most interesting things actually came in the comments.</p>
<p><span id="more-292"></span>In the podcast, Allen mentioned certain features that were &#8220;holding the language back.&#8221;  Now, if you were to ask me what needs to be taken out of Delphi, I&#8217;d say <strong>with</strong>.  And I wouldn&#8217;t actually take it out; I&#8217;d change the syntax so it works like it does in VB, and I&#8217;d disallow nested <strong>with</strong> blocks.  Between those two changes, that should eliminate the ambiguities that make <strong>with</strong> troublesome and turn it into a very useful feature.</p>
<p>But Allen&#8217;s thinking on a bit of a lower level.  He mentioned <strong>absolute</strong>.  When I asked why <strong>absolute</strong> was problematic in the comments, he responded:</p>
<blockquote><p>“absolute” can cause significant type-safety holes by  allowing one to “overlay” a variable of one type on top of a variable of  another type. In the future this will cause significant confusion for  things like further enhanced meta-data and run-time stack information  leading up to a richer/safer run-time experience. This would certainly  interfere with the potential future inclusion of garbage collector, for  instance.</p></blockquote>
<p>This raises some serious philosophical questions about the future of Delphi.  From what I&#8217;ve seen, most of us tend to keep our diets rather low on kool-aid and don&#8217;t actually <em>want</em> garbage collection in Delphi.</p>
<p>But above that, mentioned ever-so-casually, is a term I don&#8217;t think I&#8217;ve ever seen before: <em>run-time stack information. </em>In fact, a Google search for &#8220;run-time stack information&#8221; (in quotes; without quotes we get nothing useful) turns up this podcast as the #1 result.  So what is run-time stack information?  It&#8217;s hard to say exactly, since I&#8217;m not Allen Bauer or a member of the Delphi team, but I can make a few educated guesses.</p>
<p>I&#8217;ll begin by assuming that the obvious similarity to &#8220;run-time type information&#8221; doesn&#8217;t end with the name.  So then Allen&#8217;s likely thinking about information about stack data that&#8217;s available at run-time, that would otherwise only be available in the source code and then get thrown away by the compiler.</p>
<p>So what goes on the stack?  Four things:  Parameters, local variables, implicit local variables, and stack frames.</p>
<p>Pretty much all the relevant data on parameters is already available through extended RTTI, at least for methods that have RTTI available.  So RTSI on parameters could probably be implemented simply by referencing the RTTI for the current function.  (Of course that would limit RTSI coverage to methods that actually have RTTI available.)  Not much of interest here.</p>
<p>At first glance, information on local variables would seem to be even less useful.  At its core, RTTI is about discovering things at runtime that can&#8217;t be statically verified at compile-time.  A routine already has all the information it needs about its own locals at compile-time.  Things get more interesting after looking at stack frames, though.</p>
<p>Implicit local variables can also go on the stack.  They&#8217;re for intermediate values. For example, if I write &#8220;x := x + y * z;&#8221; the compiler has to calculate y * z and store the result somewhere before it adds it to x.  Depending on various factors, that could go in a register or it could go on the stack.  Either way, I don&#8217;t think there&#8217;s much usefulness in creating data tables about implicit variables.</p>
<p>Stack frames are what makes the concept really interesting, though.  Here&#8217;s where we get information that&#8217;s not available at compile time.  Imagine if there was some way for a routine to determine what called it, and if that process could be continued for an arbitrary number of iterations.  We&#8217;d have an official API for examining the call stack from within Delphi.  There are various call stack examiners already available, especially as part of exception-logging packages, but they tend to depend on hacks and implementation details.  An official call-stack tracing API would be very useful, especially as Delphi starts to branch out into other platforms and architectures.</p>
<p>(Having said that, I&#8217;ve noticed that <a href="http://help.madshi.net/madExcept.htm">MadExcept</a> frequently gets the call stack right in places where Delphi&#8217;s debugger gets lost.  Maybe Embarcadero should talk to Mathias Rauen about the possibility of integrating some of his code into the debugger?)</p>
<p>Call stack RTSI is also what make local variable and parameter RTSI interesting.  A function knows everything it needs to know about its own locals, but what if it could examine the locals and parameters of functions further up the call stack?  Admittedly, there&#8217;s probably not much practical use for this for most cases, but it would make it theoretically possible to build a debugger into your program.</p>
<p>Of course, a debugger runs on debug data, which is traditionally <em>not</em> included in a program, and for a good reason.  To make that information available at runtime, it needs to be included in the binary, where anyone with the right tools could get at it.  This is the sort of stuff that makes .NET assemblies trivially decompilable, unless you use an obfuscator that eliminates the benefits of having that level of detailed metadata around in the first place.  Anyone want to see a Delphi Reflector?</p>
<p>So, what is &#8220;run-time stack information&#8221;?  How is the team planning to implement it?  Is it merely a concept, or do they have any work in progress on implementing it so far?  It&#8217;s hard to say.  These are just my best guesses about what it might be and what might be done with it.  But it sure sounds interesting&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/292/run-time-stack-information/feed</wfw:commentRss>
		<slash:comments>7</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 probably NOT coming</title>
		<link>http://tech.turbu-rpg.com/279/smaller-cleaner-rtti-probably-not-coming</link>
		<comments>http://tech.turbu-rpg.com/279/smaller-cleaner-rtti-probably-not-coming#comments</comments>
		<pubDate>Mon, 03 Jan 2011 19:36:59 +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=279</guid>
		<description><![CDATA[I got an email from Barry Kelly in response to my last post: I can&#8217;t comment on this article as it requires the commenter to be logged in, and registration is disabled. There are no plans for a more compact RTTI format. I&#8217;d love for there to be, but the backward compatibility concerns are simply [...]]]></description>
			<content:encoded><![CDATA[<p>I got an email from Barry Kelly in response to <a href="http://tech.turbu-rpg.com/274/smaller-cleaner-rtti-coming">my last post</a>:</p>
<blockquote><p>I can&#8217;t comment on this article as it requires the commenter to be<br />
logged in, and registration is disabled.</p>
<p>There are no plans for a more compact RTTI format. I&#8217;d love for there to<br />
be, but the backward compatibility concerns are simply too large.<br />
However, as more code relies on higher-level RTTI constructs, the scope<br />
for freedom there increases incrementally. That&#8217;s all I was trying to<br />
say.</p>
<p>&#8211; Barry<span id="more-279"></span></p></blockquote>
<p>*sigh*  I thought I&#8217;d fixed the comments thing.  Anyone&#8217;s supposed to be able to post here with just a name and email address.  That&#8217;s why registration is disabled: no one should <em>need</em> to register!  Looks like it got reset somehow during the last WordPress update. <img src='http://tech.turbu-rpg.com/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' /> </p>
<p>As for the RTTI, I guess I&#8217;m not surprised by that.  Backwards compatibility is really important to the Delphi team.  That&#8217;s why <a href="https://forums.embarcadero.com/thread.jspa?threadID=47958">monstrosities like nested WITH statements</a> are still with us.</p>
<p>I just hope Barry and the rest of the team finds time to fill in holes for XE2.  Specifically, it would be very nice to be able to use RTTI to access array properties.  Being able to get at standalone functions (not methods of objects) would be nice too.  That might require the addition of some sort of TRttiUnit class, which I definitely wouldn&#8217;t mind having either.</p>
<p>But that&#8217;s just an implementation detail.  What I really want are array properties and standalone methods, and a public interface to the TMethodImplementation class.  There&#8217;s a lot of potential in that class (introduced in the RTTI unit in Delphi XE) that&#8217;s unfortunately not available for public use.  When I asked Allen Bauer about it, he said the team wasn&#8217;t sure they wanted to have to support it in its current form, but it might be made more available in a future release.  I certainly hope so.</p>
<p>What do you think?  Are there any other holes in RTTI that need filling in, and what would you do with them if you had them available?</p>
]]></content:encoded>
			<wfw:commentRss>http://tech.turbu-rpg.com/279/smaller-cleaner-rtti-probably-not-coming/feed</wfw:commentRss>
		<slash:comments>10</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>RTTI errors and omissions in Delphi XE</title>
		<link>http://tech.turbu-rpg.com/194/rtti-errors-and-omissions-in-delphi-xe</link>
		<comments>http://tech.turbu-rpg.com/194/rtti-errors-and-omissions-in-delphi-xe#comments</comments>
		<pubDate>Fri, 24 Sep 2010 00:22:42 +0000</pubDate>
		<dc:creator>Mason Wheeler</dc:creator>
				<category><![CDATA[Dark Corners]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Delphi XE]]></category>
		<category><![CDATA[RTTI]]></category>

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

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

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

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

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

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

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

