{"id":254,"date":"2008-08-26T21:56:08","date_gmt":"2008-08-26T09:56:08","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=254"},"modified":"2008-08-26T21:56:08","modified_gmt":"2008-08-26T09:56:08","slug":"an-exchange-for-all","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/254\/","title":{"rendered":"An Exchange() For All"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">3<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>Following on from yesterday&#8217;s post, Barry Kelly (CodeGear engineer) kindly clarified a few points, one of which was that Generics support in Delphi 2009 won&#8217;t extent to unit procedures, only class methods, so speculation about a possible generic implementation of a <strong>Swap()<\/strong>\/<strong>Exchange()<\/strong> routine was rendered largely academic.<\/p>\n<p>Not to be dissuaded I came up with this solution to meet my current and quite possibly future needs.<br \/>\n<!--more--><\/p>\n<h3>Untyped Is Not Enough<\/h3>\n<p>I decided that the untyped parameter approach &#8211; despite it&#8217;s risks &#8211; was the one for me, avoiding a library full of overloads.\u00a0 To recap, this is the original untyped parameter implementation:<\/p>\n<pre class=\"delphi\">  procedure Exchange(var A, B);\r\n  var\r\n    aa: Integer absolute A;\r\n    bb: Integer absolute B;\r\n    i: Integer;\r\n  begin\r\n    i  := aa;\r\n    aa := bb;\r\n    bb := i;\r\n  end;<\/pre>\n<p>The problem with this implementation is that it does not support parameters that are not 32-bits in size.  This isn&#8217;t a problem for the vast majority of cases (<em>my<\/em> cases &#8211; ymmv).  It means that it does handle Cardinal, Integer, String (yes, String), object references etc etc.\u00a0 It doesn&#8217;t handle Doubles or TDateTime though, for example.<\/p>\n<p>So, I added a refinement so that this routine can safely accomodate parameters of any size, as long as we correctly identify that size when we make the call:<\/p>\n<pre class=\"delphi\">  procedure Exchange(var A, B; aSize: Integer);\r\n  var\r\n    a8: Byte absolute A;\r\n    b8: Byte absolute B;\r\n    a16: Word absolute A;\r\n    b16: Word absolute B;\r\n    a32: LongWord absolute A;\r\n    b32: LongWord absolute B;\r\n    a64: Int64 absolute A;\r\n    b64: Int64 absolute B;\r\n    aE: Extended absolute A;\r\n    bE: Extended absolute B;\r\n    i8: Byte;\r\n    i16: Word;\r\n    i32: LongWord;\r\n    i64: Int64;\r\n    iE: Extended;\r\n    p: Pointer;\r\n  begin\r\n    case aSize of\r\n      sizeof(Byte)      : begin\r\n                            i8 := a8;\r\n                            a8 := b8;\r\n                            b8 := i8;\r\n                          end;\r\n\r\n      sizeof(Word)      : begin\r\n                            i16 := a16;\r\n                            a16 := b16;\r\n                            b16 := i16;\r\n                          end;\r\n\r\n      sizeof(LongWord)  : begin\r\n                            i32 := a32;\r\n                            a32 := b32;\r\n                            b32 := i32;\r\n                          end;\r\n\r\n      sizeof(Int64)     : begin\r\n                            i64 := a64;\r\n                            a64 := b64;\r\n                            b64 := i64;\r\n                          end;\r\n\r\n      sizeof(Extended)  : begin\r\n                            iE := aE;\r\n                            aE := bE;\r\n                            bE := iE;\r\n                          end;\r\n    else\r\n      GetMem(p, aSize);\r\n      try\r\n        CopyMemory(p,           Pointer(@A), aSize);\r\n        CopyMemory(Pointer(@A), Pointer(@B), aSize);\r\n        CopyMemory(Pointer(@B), p,           aSize);\r\n      finally\r\n        FreeMem(p);\r\n      end;\r\n    end;\r\n  end;<\/pre>\n<p>This is something of a step up in apparent complexity compared to the original &#8211; the specific handling of certain parameter sizes is a performance optimisation &#8211; the all-purpose <strong>CopyMem()<\/strong> approach is some 20x slower than the local variable implementation for 4 byte values, for example.<\/p>\n<p>If anyone out there has some inline ASM that could improve things further, that would be welcome.\u00a0 I can&#8217;t seem to figure out the XCHG opcode!<\/p>\n<p>Speaking of 4-byte parameters, as I say, most parameters used with this routine are 4-bytes, so the interface declaration for the routine declares this as the default <strong>aSize<\/strong>:<\/p>\n<pre class=\"delphi\">  procedure Exchange(var A, B; aSize: Integer = 4);<\/pre>\n<p>In use of course things are still very straightforward:<\/p>\n<pre class=\"delphi\">  var\r\n    a, b: Integer;\r\n    topleft, bottomright: TPoint;\r\n    s1, s2: String;\r\n  begin\r\n    Exchange(a, b);\r\n    Exchange(s1, s2);\r\n    Exchange(topleft, bottomright, sizeof(topleft));\r\n  end;<\/pre>\n<p>Don&#8217;t get me wrong &#8211; this is far from perfect.  The compiler isn&#8217;t going to pick you up if you don&#8217;t provide <strong>aSize<\/strong> when you should or if you provide the wrong size in the <strong>aSize<\/strong> parameter although using <strong>sizeof()<\/strong> on one of the <em>params<\/em>, rather than a type name will help avoid that mistake.<\/p>\n<p>Still, the idea of untyped parameters might be off-putting to some people, but bear in mind that <strong>FreeAndNIL()<\/strong> accepts an untyped parameter.<\/p>\n<p>But, as with anything I post, no-one&#8217;s going to come knocking on your door demanding to know why you aren&#8217;t using my code &#8211; it&#8217;s your choice.<\/p>\n<p>\ud83d\ude42<\/p>\n<p>Whether or not you choose to use this routine yourself, quite possibly the techniques used might trigger some insight into some other problem you are facing.<\/p>\n<h3>Options For The Future<\/h3>\n<p>For this particular problem, the lack of generic support for unit procedures and (as things stand at least) the lack of type inferencing mean that even an implementation based on these new language features are going to have unappealing (to me at least) downsides.<\/p>\n<p>Fixing things to suit this one procedure would be going too far.\u00a0 But maybe it would be useful to have such a routine &#8220;built in&#8221; to the compiler, perhaps as an operator:<\/p>\n<pre class=\"delphi\"> \u00a0 a :=: b;\u00a0 <\/pre>\n<p>Which looks kind-a neat, pretty obvious and still &#8220;Pascally&#8221; to my eye at least.<\/p>\n<p>I doubt I&#8217;m the first one to suggest it, and it strikes me that it should be very straightforward to implement.\u00a0 The compiler need only enforce that <strong>a<\/strong> and <strong>b<\/strong> are simple variables (not properties or function calls, for example) of the same type and generate the necessary inline code<\/p>\n<p>This is the sort of thing that is possibly regarded as so trivial as to not be worth bothering with, even though implementing it would itself also be trivial.\u00a0 But I wonder, would it be something that a lot of Delphi developers would find useful?<\/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\">3<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> Following on from yesterday&#8217;s post, Barry Kelly (CodeGear engineer) kindly clarified a few points, one of which was that Generics support in Delphi 2009 won&#8217;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 dissuaded I came up with [&hellip;]<\/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-46","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":244,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/244\/","url_meta":{"origin":254,"position":0},"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":[]},{"id":644,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/644\/","url_meta":{"origin":254,"position":1},"title":"When is an interfaced object not an interfaced object?","date":"29 Jul 2010","format":false,"excerpt":"Answer: When it is merely the container for an interface. After a long series of observation and opinion pieces, I thought it about time I posted something a little more technical, so here we go. I think it is a well known practice to store references to objects in the\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1240,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1240\/","url_meta":{"origin":254,"position":2},"title":"What&#8217;s in a Word &#8230; ?","date":"21 Sep 2012","format":false,"excerpt":"In an exchange with David Heffernan both on SO and in the comments here on Te Waka, I had cause to climb in my own personal \"Wayback Machine\" and further investigate an apparent change in compiler behaviour between Delphi 2007 and 2009. This change was first identified as the result\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":254,"position":3},"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":2605,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2605\/","url_meta":{"origin":254,"position":4},"title":"The Varying (and Variant) Value of True","date":"21 Mar 2017","format":false,"excerpt":"My most recent posts have prompted a bit of discussion, and it seems some concern, regarding the implementation of Boolean values in Delphi. The concern at least I think is unwarranted, as long as you avoid explicitly comparing a Boolean value to the True constant and allow the compiler to\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1207,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1207\/","url_meta":{"origin":254,"position":5},"title":"Adventures in Syntax: Something Old, Something New etc&#8230;","date":"20 Sep 2012","format":false,"excerpt":"As the post title says, this will be a brief detour through some features of the Pascal language and a presentation of some (theoretical) alternatives that could have been introduced instead. That is, some are real but little known syntax, others are what I think might be preferable to the\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\/254"}],"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=254"}],"version-history":[{"count":8,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/254\/revisions"}],"predecessor-version":[{"id":262,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/254\/revisions\/262"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=254"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=254"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=254"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}