The Wiert Corner – irregular stream of stuff

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

  • My badges

  • Twitter Updates

    • RT @jpaternotte: "De kans op trombose op de fieldlabvlucht naar Rhodos is 100 keer groter dan bij prik met AstraZeneca."… 1 hour ago
    • RT @pmvdlinden: Vandaag in NRC. Eén van de vele voorbeelden van het uit de bocht gevlogen strenge asielbeleid. https://t.co/2PP04p2LzA 1 hour ago
    • RT @JenMsft: Canada's relationship with metric is the textbook example of "It's Complicated" https://t.co/oGduBdwPIk 1 hour ago
    • RT @WIRED: Justine Haupt's goal was simple: strip a phone down to its absolute essentials while providing a legitimate excuse to not text f… 1 hour ago
    • RT @Foone: this is so dumb and obvious from how I was yelling about it the other day: ALL THE NTFS ARE IS A JSON FILE POINTING AT A URL. TH… 1 hour ago
  • My Flickr Stream

  • Pages

  • All categories

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

    Join 2,314 other followers

Delphi: combining intrinsic functions and inline to have no-code checks on concrete generic instantiation

Posted by jpluimers on 2021/03/04

The title might sound like a lot of gibberish, but the mechanism it describes helps solving a problem when using generics: lack of generic constraints in the compiler.

For instance, you cannot constrain on enumeration types (C# could not do this either: you could not do T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum, but [WayBack] since C# 7.3, you can do ... where T : Enum) unless you [WayBack] did some real hackery.

For Delphi, you still cannot do the constraint, but with some hackery, you can avoid code generation. Spring4d uses this in [WayBack] Spring.pas, from which I copied these fragments:

class function TType.Kind<T>: TTypeKind;
{$IFDEF DELPHIXE7_UP}
begin
  Result := System.GetTypeKind(T);
{$ELSE}
var
  typeInfo: PTypeInfo;
begin
  typeInfo := System.TypeInfo(T);
  if typeInfo = nil then
    Exit(tkUnknown);
  Result := typeInfo.Kind;
{$ENDIF}
end;

class procedure Guard.CheckTypeKind<T>(expectedTypeKind: TTypeKind;
  const argumentName: string);
begin
  if TType.Kind<T> <> expectedTypeKind then
    RaiseArgumentException(TType.Kind<T>, argumentName);
end;

class function TEnum.IsValid<T>(const value: Integer): Boolean;
var
  data: PTypeData;
begin
  Guard.CheckTypeKind<T>(tkEnumeration, 'T');
  data := GetTypeData(TypeInfo(T));
  Result := (value >= data.MinValue) and (value <= data.MaxValue);
end;

When <T> is an enumeration type, any code for that call is eliminated by the compiler.

Related

Concrete versus generic type:

–jeroen

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: