{"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=\"span-reading-time 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=\"span-reading-time 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_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[4],"tags":[292],"class_list":["post-254","post","type-post","status-publish","format-standard","hentry","category-delphi","tag-delphi"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-46","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/254","targetHints":{"allow":["GET"]}}],"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}]}}