{"id":586,"date":"2009-10-15T18:42:26","date_gmt":"2009-10-15T06:42:26","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=586"},"modified":"2009-10-15T19:46:11","modified_gmt":"2009-10-15T07:46:11","slug":"absolute-for-beginners","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/586\/","title":{"rendered":"Absolute (for) Beginners"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>I casually suggested the use of the &#8220;<strong>absolute<\/strong>&#8221; keyword in response to a question on the NZ DUG mailing list today. \u00a0I thought nothing of it but someone mentioned that it had been years since he&#8217;d seen anyone use it, so I thought maybe it was worth bringing to wider attention.<\/p>\n<p><!--more--><br \/>\n<strong>absolute<\/strong> is one of those language features that I&#8217;m sure will divide the community into those who think it&#8217;s a useful tool and those who think it should be avoided like a modern day <strong>goto<\/strong>. \u00a0I&#8217;m firmly in the former camp.<\/p>\n<p><strong>absolute<\/strong> allows you to direct the compiler to treat one or more variables as existing at the same memory location, effectively overlaying one (or more) on top of another.<\/p>\n<p>The potential for making mistakes as a result of using such a technique is, I think, fairly clear, but in my view is not that different from the dangers of unchecked typecasting (or rather pre-checked hard casting).  That is where a type check is performed and then once the type identity determined, then simply assumed for the following code.<\/p>\n<p>It might help to explain what I mean by that with an example.<\/p>\n<p>Imagine an event handler that responds to changes in a number of controls on the form where the precise identity of each control involved isn&#8217;t important, but the type of the control determines how the event responds.  There being sufficient commonality in the event handler that maintaining separate events is undesirable.  Let us say for example that all editable controls on the form should enforce uppercase text entry (please bear in mind that this is an example contrived to show the potential use of <strong>absolute<\/strong> &#8211; don&#8217;t nit-pick the example itself):<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nprocedure TMyForm.EditableChange(Sender: TObject);\r\nbegin\r\n  if Sender is TEdit then\r\n  begin\r\n    TEdit(Sender).Text := Uppercase(TEdit(Sender).Text);\r\n  end\r\n  else if Sender is TMemo then\r\n  begin\r\n     TMemo(Sender).Text := Uppercase(TMemo(Sender).Text);\r\n  end\r\n  else if (Sender is TComboBox) and (TComboBox(Sender).Style = csDropDown) then\r\n  begin\r\n     TComboBox(Sender).Text := Uppercase(TComboBox(Sender).Text);\r\n  end;\r\n\r\n  SetDirtyFlag(TRUE);\r\nend;\r\n<\/pre>\n<p>Now, one way of &#8220;cleaning this up&#8221; might be:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nprocedure TMyForm.EditableChange(Sender: TObject);\r\nvar\r\n  edit: TEdit;\r\n  memo: TMemo;\r\n  combo: TComboBox;\r\nbegin\r\n  if Sender is TEdit then\r\n  begin\r\n    edit := TEdit(Sender);\r\n    edit.Text := Uppercase(edit.Text);\r\n  end\r\n  else if Sender is TMemo then\r\n  begin\r\n    memo := TMemo(Sender);\r\n    memo.Text := Uppercase(memo.Text);\r\n  end\r\n  else if (Sender is TComboBox) then\r\n  begin\r\n    combo := TComboBox(Sender);\r\n    if (combo.Style = csDropDown) then\r\n    begin\r\n      combo.Text := Uppercase(combo.Text);\r\n    end;\r\n  end;\r\n\r\n  SetDirtyFlag(TRUE);\r\nend;\r\n<\/pre>\n<p>I don&#8217;t know about you, but I think the cure in this case is worse than the disease.  So now let&#8217;s see how &#8216;<strong>absolute<\/strong>&#8216; makes this attempt to clean up far more successful:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nprocedure TMyForm.EditableChange(Sender: TObject);\r\nvar\r\n  control: TObject;\r\n  edit: TEdit absolute control;\r\n  memo: TMemo absolute control;\r\n  combo: TComboBox absolute control;\r\nbegin\r\n  control := Sender;\r\n\r\n  if Sender is TEdit then\r\n  begin\r\n    edit.Text := Uppercase(edit.Text);\r\n  end\r\n  else if Sender is TMemo then\r\n  begin\r\n    memo.Text := Uppercase(memo.Text);\r\n  end\r\n  else if (Sender is TComboBox) and (combo.Style = csDropDown) then\r\n  begin\r\n    combo.Text := Uppercase(combo.Text);\r\n  end;\r\n\r\n  SetDirtyFlag(TRUE);\r\nend;\r\n<\/pre>\n<p>This is just as type-safe as each previous effort &#8211; type checking is being performed as required.  The difference is that the type-casting is now effectively being performed in the declaration of the various variables themselves.<\/p>\n<p>The <strong>absolute<\/strong> keyword tells the compiler to treat <strong>edit<\/strong>, <strong>memo<\/strong> and <strong>combo<\/strong> as simply different types of variable all located in the same location in memory as the <strong>control<\/strong> variable.<\/p>\n<p>But more than that &#8211; to my mind it also acts as a clear indicator in the declaration of those variables themselves as to how those variables are used in the function or procedure body that follows, in a way that is not at all clear (without carefully reading the details of the code itself) in the previous example.<\/p>\n<p>I <em>like<\/em> code that explains itself &#8211; it means I don&#8217;t have to write as much in the comments!<\/p>\n<p>Now, in fact things can be made just as clear and a little less verbose, since as well as &#8220;overlaying&#8221; <em>variables <\/em>on top of one another, the <strong>absolute<\/strong> keyword can be used to &#8220;overlay&#8221; variables on parameters themselves, so in this case we can eliminate that rather distracting <strong>control<\/strong> variable:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nprocedure TMyForm.EditableChange(Sender: TObject);\r\nvar\r\n  edit: TEdit absolute Sender;\r\n  memo: TMemo absolute Sender;\r\n  combo: TComboBox absolute Sender;\r\nbegin\r\n  if Sender is TEdit then\r\n  begin\r\n    edit.Text := Uppercase(edit.Text);\r\n  end\r\n  else if Sender is TMemo then\r\n  begin\r\n    memo.Text := Uppercase(memo.Text);\r\n  end\r\n  else if (Sender is TComboBox) and (combo.Style = csDropDown) then\r\n  begin\r\n    combo.Text := Uppercase(combo.Text);\r\n  end;\r\n\r\n  SetDirtyFlag(TRUE);\r\nend;\r\n<\/pre>\n<p>As I said, this is a not very nice and very contrived example, but I wanted to use something simple to introduce the idea before complicating things just a little.<\/p>\n<h2>Mutating the Identity of Schizophrenic Types<\/h2>\n<p>If you&#8217;ve ever had to work with resources in your applications &#8211; icons, bitmaps etc &#8211; you will no doubt be familiar with <strong>MAKEINTRESOURCE()<\/strong>.  This little function converts a 16-bit resource ID into a value that can be used in place of a pointer to a resource name in some functions.<\/p>\n<p>It also means that sometimes you will received a pointer to a resource name that is in fact not a pointer at all but one of these specially formatted resource ID values and needing to deal with it according to what the value contains &#8211; it could be a 16-bit integer or a pointer to a string.<\/p>\n<p>This is potentially fiddly, but <strong>absolute<\/strong> makes it simple and, again, clearly states the intent of your code without requiring you to explicitly implement code to execute that intent:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nfunction GetResourceIDAsString(const aID: PChar): String;\r\nvar\r\n  id: Integer absolute aID;\r\nbegin\r\n  if HiWord(aID) = 0 then\r\n    result := IntToStr(id)\r\n  else\r\n    result := aID;\r\nend;\r\n<\/pre>\n<p>Note: the test of the high 16-bits of the parameter passed could equally have been written as simply:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  if id &lt;= MAXWORD then\r\n<\/pre>\n<p>Notice how this technique preserves the type safety of the supplied parameter (resource ID&#8217;s are treated as <strong>PChar<\/strong>) without involving any messy typecasts in the implementation itself.  By contrast I think a common approach that you might encounter that didn&#8217;t use <strong>absolute<\/strong> would look something like:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nfunction GetResourceIDAsString(const aID: PChar): String;\r\nvar\r\n  dw: DWORD;\r\nbegin\r\n  dw := DWORD(aID);\r\n\r\n  if HiWord(dw) = 0 then\r\n    result := IntToStr(LoWord(dw))\r\n  else\r\n    result := aID;\r\nend;\r\n<\/pre>\n<p>Not a huge difference maybe (and a trivial, albeit representatively &#8216;real&#8217;, example in this case), but we have declared a variable and required a typecast for no good reason (in terms of the purpose of the function) than to allow us to work with the supplied value in a particular way.  I suspect that in such a case the temptation would be to actually do away with the variable and repeatedly typecast:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\nfunction GetResourceIDAsString(const aID: PChar): String;\r\nbegin\r\n  if HiWord(DWORD(aID)) = 0 then\r\n    result := IntToStr(LoWord(DWORD(aID)))\r\n  else\r\n    result := aID;\r\nend;\r\n<\/pre>\n<p>Which is shorter, no doubt but&#8230; well, perhaps some people like this sort of repetitive and heavily parenthesised code.  I personally don&#8217;t.<\/p>\n<h2>To End At The Beginning<\/h2>\n<p>As I said at the outset, this all started when I responded to a question on the NZ DUG mailing list. \u00a0The question in, uh, question was about how to take advantage of the <strong>for in<\/strong> language construct introduced in Delphi 2007. \u00a0The case involved trying to use <strong>for in<\/strong> to iterate over the contents of an instance of existing class derived from <strong>TObjectList <\/strong>&#8211; i.e. no custom enumerator had yet been provided (and bear in mind that generics weren&#8217;t available as the Delphi version in question does not support them in Win32 code).<\/p>\n<p>The question was asking why compilation errors resulted from code such as this (slightly simplified from the original question, the loop body in particular) and how to avoid them:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure PrintFormats( aFormatList: TAddressFormatList );\r\n  var\r\n    addrFormat: TAddressFormat;\r\n  begin\r\n    for addrFormat in addrFormats do\r\n      WriteLn( addrFormat.Description );\r\n  end;\r\n<\/pre>\n<p>Since <strong>TAddressFormatList<\/strong> derived from <strong>TObjectList<\/strong>, this was failing with an incompatible types error, since the enumerator for <strong>TObjectList<\/strong> yields <strong>Pointer<\/strong> values, not <strong>TAddressFormat<\/strong> references.<\/p>\n<p>The question was posed by someone who had seen someone demo the language feature by writing a <strong>for in<\/strong> loop to process the contents of a <strong>TStringList<\/strong> but who hadn&#8217;t perhaps fully appreciated the mechanism by which this language feature works and the infrastructure they needed to provide in order to make it work (which is of course provided by the VCL in the case of certain types already).<\/p>\n<p>You could of course simply do as the <strong>TObjectList <\/strong>required, and enumerate using a pointer variable, type-casting each yielded value in the body of the loop:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure PrintFormats( aFormatList: TAddressFormatList );\r\n  var\r\n    addrFormat: Pointer;\r\n  begin\r\n    for addrFormat in aFormatList do\r\n      WriteLn( TAddressFormat(addrFormat).Description );\r\n  end;\r\n<\/pre>\n<p>But with numerous uses of <strong>addrFormat <\/strong>in the loop this becomes messy.  So you might introduce another, strongly typed variable, do the typecast once into that variable and then use that other variable in the body of the loop:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure PrintFormats( aFormatList: TAddressFormatList );\r\n  var\r\n    addrFormatEnum: Pointer;\r\n    addrFormat: TAddressFormat;\r\n  begin\r\n    for addrFormatEnum in aFormatList do\r\n    begin\r\n      addrFormat := TAddressFormat(addrFormatEnum);\r\n      WriteLn(addrFormat.Description );\r\n    end;\r\n  end;\r\n<\/pre>\n<p>But we&#8217;ve already seen how <strong>absolute <\/strong>helps tidy this up even further and make the relationship between those variables absolutely [sic] crystal clear into the bargain:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure PrintFormats( aFormatList: TAddressFormatList );\r\n  var\r\n    addrFormatEnum: Pointer;\r\n    addrFormat: TAddressFormat absolute addrFormatEnum;\r\n  begin\r\n    for addrFormatEnum in aFormatList do\r\n    begin\r\n      WriteLn(addrFormat.Description );\r\n    end;\r\n  end;\r\n<\/pre>\n<p>That&#8217;s it.<\/p>\n<p>As I say, expect there will be a number of comments to this pointing out the dangers of this technique, but I believe those dangers to be no greater, and in some ways somewhat less, than those of unchecked typecasts, which is what <strong>absolute<\/strong> essentially replaces.  In it&#8217;s favour it helps eliminate &#8220;noise&#8221; and in my view makes the intent of variables clearer without requiring the reading of code to determine that usage.<\/p>\n<p>If nothing else, it may be another tool in your toolbox. \u00a0Or perhaps a tool that you didn&#8217;t even realise you had at your disposal.<\/p>\n<p>I also find it somewhat gratifying that this language feature, I think among one of the oldest to still survive in the language, can help make one of the newest language features more usable without having to spend time writing code that isn&#8217;t directly related to the business problem at hand (writing enumerators and working with enumerated lists without the benefit of generics or perhaps where a suitably typed enumerator simply hasn&#8217;t been provided).<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> I casually suggested the use of the &#8220;absolute&#8221; keyword in response to a question on the NZ DUG mailing list today. \u00a0I thought nothing of it but someone mentioned that it had been years since he&#8217;d seen anyone use it, so I thought maybe it was worth bringing to wider attention.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[4],"tags":[292],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-9s","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":254,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/254\/","url_meta":{"origin":586,"position":0},"title":"An Exchange() For All","date":"26 Aug 2008","format":false,"excerpt":"Following on from yesterday's post, Barry Kelly (CodeGear engineer) kindly clarified a few points, one of which was that Generics support in Delphi 2009 won't extent to unit procedures, only class methods, so speculation about a possible generic implementation of a Swap()\/Exchange() routine was rendered largely academic. Not to be\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":760,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/760\/","url_meta":{"origin":586,"position":1},"title":"Platform Independence Version Dependencies in the XE2 RTL","date":"14 Sep 2011","format":false,"excerpt":"So I have spent about a week now with XE2 and FireMonkey and thought I would share some of the experience so far. After an initial peek and poke around, the first order of business for me was to migrate some of my existing code to the new RTL. First\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1080,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1080\/","url_meta":{"origin":586,"position":2},"title":"Why Delphi Cannot (currently) Support WinRT","date":"23 Aug 2012","format":false,"excerpt":"Nor for that matter can any other unmanaged compiler except Microsoft's own C\/C++, apparently. Who would have thought that in a forum reply in a thread about and entitled \"HTML Builder\" you would stumble across crucial, technical information relating to the paucity of WinRT support in Delphi ? But it's\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1989,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1989\/","url_meta":{"origin":586,"position":3},"title":"Best Comment In The History of &#8230; well Ever (for ALL values of Ever) ?","date":"30 Oct 2013","format":false,"excerpt":"In the comments on Raymond Chen's blog post today, was a reference to a startling question on Stack-Overflow. As if the question itself wasn't (unintentionally) amusing enough, the accepted answer demonstrates a commitment to lost causes that is truly inspired and contains a number of (intentional and very clever) laugh\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1297,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1297\/","url_meta":{"origin":586,"position":4},"title":"Of Threads, Stacks and RAM &#8211; Part 1","date":"28 Nov 2012","format":false,"excerpt":"Roberto Schneiders recently drew my attention to the first post on his new blog (which I can recommend as a good read :) ), presenting the results of some performance testing of DataSnap that he had been involved with which proved to be very interesting (if initially somewhat disappointing). But\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2012-11-28-at-20.24.21-.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":244,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/244\/","url_meta":{"origin":586,"position":5},"title":"Generic Methods and Type Inferencing","date":"25 Aug 2008","format":false,"excerpt":"In the new Delphi forums recently, Barry Kelly responded to a question about lambda expression syntax in Tibur\u00f3n with this observation: This syntax needs type inference. Our compiler was not originally written to support type inference, but work to support type inference is orthogonal to supporting anonymous methods. ...\u00a0 you'll\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/586"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=586"}],"version-history":[{"count":6,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/586\/revisions"}],"predecessor-version":[{"id":589,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/586\/revisions\/589"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=586"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=586"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=586"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}