Translating Modula2 into D: variant records, pointer

Frits van Bommel fvbommel at REMwOVExCAPSs.nl
Tue Jan 9 10:17:47 PST 2007


BLS wrote:
> Hi Frits,
>> I think you can mostly fake this in D using private 
>> (differently-named) union members and property methods that assert if 
>> 'terminal' has the wrong value.
>> You can even make it so that the original names directly alias the 
>> private names in a non-debug build, and only use the checking version 
>> in a debug build[1].
> Can you please offer an example based on ...
> Modula
> 
> TYPE  NodePtr = POINTER TO Node;
>         Node = RECORD suc, alt: NodePtr;
>          CASE terminal: BOOLEAN OF
>            TRUE:  tsym: CHAR |
>            FALSE: nSym: HeadPtr
>          END
>        END;

-----
struct Header{};    // to make it compile as-is

struct Node
{
     Node* suc;
     Node* alt;
     bool terminal;

     union
     {
         private char tsym_;
         private Header* nSym_;
     }

     debug
     {
         // Property versions, with full error checking
         // (This block is used if -debug is passed to the compiler)
         char tsym()
         in
         {
             assert(terminal == false);
         }
         body
         {
             return tsym_;
         }

         char tsym(char newval)
         out
         {
             assert(terminal == false);
         }
         body
         {
             terminal = false;
             return tsym_ = newval;
         }

         Header* nSym()
         in
         {
             assert(terminal == true);
         }
         body
         {
             return nSym_;
         }

         Header* nSym(Header* newval)
         out
         {
             assert(terminal == true);
         }
         body
         {
             terminal = true;
             return nSym_ = newval;
         }
     } else {
         // Hmm.. No way to automatically set 'terminal' in this
         // implementation. Damn.
         // (This block is used if -debug is NOT passed to the compiler)
         alias tsym_ tsym;
         alias nSym_ nSym;
     }
}
-----

It's unfortunately a bit wordy. It's also completely untested beyond the 
fact that it compiles ;).

I noticed a limitation of the optimization opportunity I mentioned:
In the debug version (with property setters) you can automatically 
adjust the value of 'terminal' depending on the last-set property.
This isn't possible with direct aliasing as far as I can see.
If this is a problem, you might want to always use the code in the first 
block. I see no reason the compiler can't inline the functions if passed 
the appropriate optimization flags, so it shouldn't really matter much.

Some notes:
* The first block of code (right after 'debug') is compiled in if -debug 
is passed to the compiler. Otherwise, the second (short) block is compiled.
* The 'in' and 'out' blocks are removed by the compiler if -release is 
present on the compiler command line.
* As mentioned in my previous post, tsym_ and nSym_ are accessible from 
code in the same module even though they are private.
* Another implementation option is to also rename 'terminal' and make it 
private, and then only provide a property getter function so the code 
using the Node type can't set it without also setting the appropriate 
union member.
* If you only ever set the union members right after constructing the 
Node instance, you may also want to think about classes and inheritance 
if you're OOP-inclined.


More information about the Digitalmars-d-learn mailing list