The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,676 other followers

Delphi “type types”: similar types but not the same type identity, some examples.

Posted by jpluimers on 2013/03/12

Few people know about a Delphi language feature that has been present since Delphi 1: prepending the type definition with a type keyword to make the type getting a new identity.

Each time I use it, I have to do some browsing for the consequences, and this time I wrote down some notes and created a small example program (source is also below).

This time I needed it when writing class wrappers on top of the Delphi bindings for WebSphere MQ.

WebSphere MQ has Queues where you can put and get messages. It also has Queue Managers to which you connect, and that provide queuing services and manages queues.

Both Queues and Queue Managers have names that can be up to 48 (single byte) characters long.
Those names mean totally different things, so though the have similar data types, they have a different identity.

The same holds for 20 byte character arrays (they can be used as names for ChannelNameShortConnectionName and MCAName). The 264 byte character array is so far used for ConnectionName only.

Distinguishing those types: That’s what “type types” in Delphi are all about.

The DocWiki tries to explain this in the Type Compatibility and Identity (Delphi) topic:

  • Type Identity: if you add the keyword “type” in a declaration, that type gets a new identity
  • Type Compatibility: there are rules about type compatibility
  • Assignment Compatibility: variables of incompatible types can be assigned from an expression if the type of that expression is compatible when the type of the variable.

Type Identity has to do with RTTI: in the example below, MQCHAR48, TMQQueueManagerName and TMQQueueName have different identities, so there is different RTTI generated for them.

Assignment compatibility has to do both with the RTTI, and how relaxed some fundamental types are handled by the compiler.

The documentation on assignment incompatibility is incomplete:

  • There is a very important difference between string constants and string variables:
    • You can assign the various character array type values to a string variable, but not vice versa
    • You can assign an implicit string constant to  any of the character array type variables
  • The character array type variables are not assignment compatible with each other

The latter was very important to me for two reasons:

  1. now I can make sure you can not assign TMQQueue.Name to TMQQueueManager.Name (even if instances of these two have similar names),
  2. and the different RTTI guarantees I can hook different property editors to each property.

A few notes:

  • This is not limited to my examples that uses character arrays (as that’s what I needed in my particular case).
    You can do the same with other types like integers, characters. pointers, etc.
  • For integer type types the assignment compatibility is less restrict than for character arrays (example “TImageIndex = type Integer;”)
  • There is a very special usage when doing “type AnsiString(CodePage)“. Without the “type” the result would not have identity and would be assignment compatible. With CodePages, you want to limit assignment compatibility.
    These types declared in the RTL each have their own identity:

–jeroen

via:

program TypeIdentity;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils;

type
  Range20 = 0..19;
  Range48 = 0..47;
  Range264 = 0..263;
  SingleByteChar = AnsiChar; // Single-Byte Character; WebSphereMQ does not support Unicode
  MQCHAR = SingleByteChar;
  MQCHAR20 = array[Range20] of MQCHAR;
  MQCHAR48 = array[Range48] of MQCHAR;
  MQCHAR264 = array[Range264] of MQCHAR;

  TMQConnectionName = type MQCHAR264;
  TMQChannelName = type MQCHAR20;
  TMQQueueManagerName = type MQCHAR48;
  TMQQueueName = type MQCHAR48;

const
  SYSTEM_DEFAULT_MODEL_QUEUE = 'SYSTEM.DEFAULT.MODEL.QUEUE'; // Default model queue.

  SYSTEM_DEFAULT_MODEL_QUEUE_Name: TMQQueueName = ('S','Y','S','T','E','M','.','D','E','F','A','U','L','T','.','M','O','D','E','L','.','Q','U','E','U','E',
    ' ',' ', // http://publib.boulder.ibm.com/infocenter/wmqv7/v7r0/index.jsp?topic=%2Fcom.ibm.mq.csqzaj.doc%2Fsc10300_.htm
    ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
    ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ');

function PadRight(const Value: AnsiString; const ResultLength: Integer): AnsiString;
var
  Index: Integer;
begin
  Result := Value;
  SetLength(Result, ResultLength);
  for Index := Length(Value)+1 to ResultLength do
    Result[Index] := ' ';
end;

function ToMQQueueName(const Value: AnsiString): TMQQueueName;
var
  PaddedValue: AnsiString;
  ResultSize: Integer;
begin
  ResultSize := SizeOf(Result);
  PaddedValue := PadRight(Value, ResultSize);
  Move(PaddedValue[1], Result, ResultSize);
end;

var
  S: AnsiString;
  QueueName: TMQQueueName;
  QueueManagerName: TMQQueueManagerName;
  QueueName48: MQCHAR48;

begin
  try
    S := SYSTEM_DEFAULT_MODEL_QUEUE;
    S := SYSTEM_DEFAULT_MODEL_QUEUE_Name;

    QueueName := SYSTEM_DEFAULT_MODEL_QUEUE;
    QueueName := SYSTEM_DEFAULT_MODEL_QUEUE_Name;

    QueueManagerName := SYSTEM_DEFAULT_MODEL_QUEUE;
    QueueManagerName := SYSTEM_DEFAULT_MODEL_QUEUE_Name; // 68

    QueueName48 := SYSTEM_DEFAULT_MODEL_QUEUE;
    QueueName48 := SYSTEM_DEFAULT_MODEL_QUEUE_Name; // 71

    QueueManagerName := QueueName;   // 73
    QueueName := QueueManagerName;   // 74

    QueueName48 := QueueManagerName; // 76
    QueueManagerName := QueueName48; // 77

    S := QueueName;
    S := QueueManagerName;
    S := QueueName48;

    QueueName := ToMQQueueName(S);

    QueueName := S;         // 85
    QueueManagerName := S;  // 86
    QueueName48 := S;       // 87
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

[DCC Error] TypeIdentity.dpr(68): E2010 Incompatible types: 'TMQQueueManagerName' and 'TMQQueueName'
[DCC Error] TypeIdentity.dpr(71): E2010 Incompatible types: 'MQCHAR48' and 'TMQQueueName'
[DCC Error] TypeIdentity.dpr(73): E2010 Incompatible types: 'TMQQueueManagerName' and 'TMQQueueName'
[DCC Error] TypeIdentity.dpr(74): E2010 Incompatible types: 'TMQQueueName' and 'TMQQueueManagerName'
[DCC Error] TypeIdentity.dpr(76): E2010 Incompatible types: 'MQCHAR48' and 'TMQQueueManagerName'
[DCC Error] TypeIdentity.dpr(77): E2010 Incompatible types: 'TMQQueueManagerName' and 'MQCHAR48'
[DCC Error] TypeIdentity.dpr(85): E2010 Incompatible types: 'TMQQueueName' and 'AnsiString'
[DCC Error] TypeIdentity.dpr(86): E2010 Incompatible types: 'TMQQueueManagerName' and 'AnsiString'
[DCC Error] TypeIdentity.dpr(87): E2010 Incompatible types: 'MQCHAR48' and 'AnsiString'

–EOF–

One Response to “Delphi “type types”: similar types but not the same type identity, some examples.”

  1. Cool Jeroen, thx.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: