{"id":507,"date":"2009-09-14T23:21:02","date_gmt":"2009-09-14T11:21:02","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=507"},"modified":"2009-09-14T23:30:40","modified_gmt":"2009-09-14T11:30:40","slug":"mind-the-gaps","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/507\/","title":{"rendered":"Mind the Gap(s)"},"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\"> 4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>This is what I love about Delphi. \u00a0After almost 15 years of Delphi&#8217;ing there&#8217;s still new things to learn, and I don&#8217;t just mean new features in the latest releases. \u00a0I mean, basic, fundamental things that have been there for years, just undiscovered (by me). \u00a0In this case the discovery was a little annoying, but never-the-less\u00a0educational.<\/p>\n<p><!--more--><\/p>\n<p>In the case in question I was creating some library code for myself to help with some resource manipulation code that I have in mind. \u00a0As part of this I wanted an enum type to represent the normal resource types found in Windows code:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  type\r\n    TResourceType = (\r\n                      rtCURSOR = 1,\r\n                      rtBITMAP,\r\n                      rtICON,\r\n                      rtMENU,\r\n                      rtDIALOG,\r\n                      rtSTRING,\r\n                      rtFONTDIR,\r\n                      rtFONT,\r\n                      rtACCELERATOR,\r\n                      rtRCDATA,\r\n                      rtMESSAGETABLE,\r\n                      rtGROUP_CURSOR,\r\n                      rtGROUP_ICON = 14,\r\n                      rtVERSION = 16,\r\n                      rtDLGINCLUDE,\r\n                      rtPLUGPLAY = 19,\r\n                      rtVXD,\r\n                      rtANICURSOR,\r\n                      rtANIICON,\r\n                      rtHTML,\r\n                      rtMANIFEST\r\n                    );\r\n    TResourceTypes = set of TResourceType;\r\n<\/pre>\n<p>I used specific ordinal assignments to some of the enum members to create the necessary &#8220;gaps&#8221; for the resource type values that aren&#8217;t (currently) meaningful in Windows and ensure that the ordinal values of the enum members corresponded exactly to Windows resource type values.<\/p>\n<p>The enum member names are also carefully chosen so that they may be quickly converted to the symbolic names of the corresponding Windows resource type identifiers, e.g.:<\/p>\n<p style=\"padding-left: 30px;\">rtCURSOR        &gt;&gt;  RT_CURSOR<br \/>\nrtGROUP_ICON  &gt;&gt; RT_GROUP_ICON<br \/>\netc<\/p>\n<p>The plan was to have a function to take a <strong>TResourceType<\/strong> value, convert to the enum name and change the &#8220;rt&#8221; prefix to &#8220;RT_&#8221;.  This is not exactly rocket science and I did not anticipate any problems.<\/p>\n<p>So I was somewhat surprised to find myself thwarted by a compilation error in the code:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n      sName := GetEnumName(TypeInfo(TResourceType), Ord(aValue));\r\n<\/pre>\n<p><span style=\"color: #ff0000;\"><strong>ERROR: <span style=\"color: #000000;\">E2134 &#8211; Type &#8216;TResourceType&#8217; has no typeinfo&#8217;<\/span><\/strong><\/span><\/p>\n<p>It took me a while to chase this down &#8211; my initial thought was to suspect that there was some other definition of a <strong>TResourceType<\/strong> type that was hiding my type.  That was soon ruled out and a big of further digging eventually revealed the answer.<\/p>\n<p>It turns out that assigning specific ordinal values to enum members (if they are different to the ordinals that the compiler would normally assign automatically) defeats the RTTI system and prevents the creation of type info for the type. \u00a0Sadly this doesn&#8217;t seem to be mentioned in the documentation, not even for the <strong>E2134<\/strong> error itself which would seem to be an ideal help topic to contain this useful piece of information.<\/p>\n<p>The solution in my case was quite simple. \u00a0The number of &#8220;unused&#8221; members was small so I could change my enum type to:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  type\r\n    TResourceType = (\r\n                      rtNotUsed1,    \/\/ = 0\r\n                      rtCURSOR,\r\n                      rtBITMAP,\r\n                      rtICON,\r\n                      rtMENU,\r\n                      rtDIALOG,\r\n                      rtSTRING,\r\n                      rtFONTDIR,\r\n                      rtFONT,\r\n                      rtACCELERATOR,\r\n                      rtRCDATA,\r\n                      rtMESSAGETABLE,\r\n                      rtGROUP_CURSOR,\r\n                      rtNotUsed2,    \/\/ = 13\r\n                      rtGROUP_ICON,\r\n                      rtNotUsed3,    \/\/ = 15\r\n                      rtVERSION,\r\n                      rtDLGINCLUDE,\r\n                      rtNotUsed4,    \/\/ = 18\r\n                      rtPLUGPLAY,\r\n                      rtVXD,\r\n                      rtANICURSOR,\r\n                      rtANIICON,\r\n                      rtHTML,\r\n                      rtMANIFEST\r\n                    );\r\n    TResourceTypes = set of TResourceType;\r\n<\/pre>\n<p>Notice the additional <strong>rtNotUsedN<\/strong> members which fill in the gaps.<\/p>\n<p>This created a slight problem for me in that now I had some undesirable names &#8220;polluting&#8221; my enum space.  In this case I was not too concerned about that &#8211; they are self documenting after all (&#8220;NotUsed&#8221; is pretty unambiguous on that score).<\/p>\n<p>But there were occasions in my code where I was going to wish to test some <strong>TResourceType<\/strong> value to ensure that it was valid.<\/p>\n<p>A simple pair of sets helped out with this:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  const\r\n    rtInvalid = &#x5B;rtNotUsed1, rtNotUsed2, rtNotUsed3, rtNotUsed4];\r\n    rtValidResourceTypes  = &#x5B;Low(TResourceType)..High(TResourceType)] - rtInvalid;\r\n<\/pre>\n<p>Which allowed me to perform runtime checking of some arbitrary <strong>TResourceType<\/strong> value:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n    if NOT (aValue in rtValidResourceTypes) then\r\n      \/\/ Invalid resource type ... !\r\n<\/pre>\n<p>Or of course alternatively:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n    if (aValue in rtInvalid) then\r\n      \/\/ Invalid resource type ... !\r\n<\/pre>\n<p>According to taste (you can probably tell which I prefer as the choice of names for the sets yields one which reads quite fluently and naturally, whilst the other is awkward and stilted in comparison, albeit more brief).  \ud83d\ude42<\/p>\n<h2>We Don&#8217;t Know What We Don&#8217;t Know<\/h2>\n<p>It&#8217;s really quite astonishing to me that in almost 15 years of working with Delphi I&#8217;ve not previously encountered this limitation of the type information system (as it relates to enum types).  So astonishing that I thought it worth not just blogging about but also posting to <a href=\"http:\/\/stackoverflow.com\/questions\/1420562\/why-do-i-get-type-has-no-typeinfo-error-with-an-enum-type\">stackoverflow<\/a> as a question with a ready made answer.<\/p>\n<p>I&#8217;ve only recently started using stackoverflow seriously &#8211; I hope this sort of self-answering isn&#8217;t an abuse of the system.  I thought it a useful way of adding to the &#8220;knowledgebase&#8221;.<\/p>\n<p>In any event, Barry Kelly, a CodeGear compiler engineer who works on the Delphi compiler itself no less (some guys get all the luck!) responded to my stackoverflow question+answer by explaining some of the internal details of the RTTI system that makes typeinfo  difficult\/impossible for such non-contiguous enum types, and offered the opinion that it probably wasn&#8217;t worth going to too much bother for since the times when it would be useful were so rare.<\/p>\n<p>I&#8217;d have to agree with him.<\/p>\n<p>It&#8217;s taken me 15 years to stumble across it &#8211; I think I can manage without a &#8220;fix&#8221; in this case.<\/p>\n<h2>Footnote<\/h2>\n<p>If this had been a somewhat simpler case where I simply wanted the initial member of the enum to have a value such as 1 (one) rather than the usual 0 (zero), I could have achieved type safety with a simple subrange type (thanks to Ulrich for pointing this out on stackoverflow) renaming the underlying type and using the subrange type as the &#8220;concrete&#8221; representation of my enum throughout my code (having established the principle, in this final example I have artifically reduced the number of members in the enum type, simply to keep it short and concise):<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  type\r\n    TResourceTypeValue = (\r\n                            rtUnused,\r\n                            rtCURSOR,\r\n                            rtBITMAP,\r\n                            rtICON,\r\n                            rtMENU\r\n                 );\r\n    TResourceType = rtCURSOR..High(TResourceTypeValue);\r\n    TResourceTypes = set of TResourceType;\r\n<\/pre>\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\"> 4<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span>This is what I love about Delphi. \u00a0After almost 15 years of Delphi&#8217;ing there&#8217;s still new things to learn, and I don&#8217;t just mean new features in the latest releases. \u00a0I mean, basic, fundamental things that have been there for years, just undiscovered (by me). \u00a0In this case the discovery was a little annoying, but [&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,88,87,86],"class_list":["post-507","post","type-post","status-publish","format-standard","hentry","category-delphi","tag-delphi","tag-enums","tag-rtti","tag-typeinfo"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-8b","jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/507","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=507"}],"version-history":[{"count":21,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/507\/revisions"}],"predecessor-version":[{"id":527,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/507\/revisions\/527"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=507"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=507"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=507"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}