nullable.pp 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (C) 2020 Michael Van Canneyt
  4. member of the Free Pascal development team.
  5. Nullable generic type.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. }
  12. {$IFNDEF FPC_DOTTEDUNITS}
  13. unit nullable;
  14. {$ENDIF FPC_DOTTEDUNITS}
  15. {$mode objfpc}
  16. {$modeswitch advancedrecords}
  17. interface
  18. {$IFDEF FPC_DOTTEDUNITS}
  19. uses System.SysUtils;
  20. {$ELSE FPC_DOTTEDUNITS}
  21. uses sysutils;
  22. {$ENDIF FPC_DOTTEDUNITS}
  23. Type
  24. { TNullable }
  25. generic TNullable<T> = record
  26. private
  27. FValue: T;
  28. FHasValue: Boolean; // Default False
  29. function GetIsNull: Boolean;
  30. function GetValue: T;
  31. function GetValueOrDefault: T;
  32. procedure SetHasValue(AValue: Boolean);
  33. procedure SetValue(AValue: T);
  34. Public
  35. // Make things more readable
  36. Type
  37. TMyType = specialize TNullable<T>;
  38. // Clear value, no value present after this.
  39. procedure Clear;
  40. // Is a value present ?
  41. property HasValue: Boolean read FHasValue write SetHasValue;
  42. // Is No value present
  43. property IsNull: Boolean read GetIsNull;
  44. // return the value.
  45. property Value: T read GetValue write SetValue;
  46. // If a value is present, return it, otherwise return the default.
  47. property ValueOrDefault: T read GetValueOrDefault;
  48. // Return an empty value
  49. class function Empty: TMyType; static;
  50. // management operator
  51. class operator Initialize(var aSelf : TNullable);
  52. // Conversion.
  53. class operator Explicit(aValue: T): TMyType;
  54. class operator Explicit(aValue: TMyType): T;
  55. class operator := (aValue: T): TMyType;
  56. class operator := (aValue: TMyType): T;
  57. end;
  58. implementation
  59. {$IFDEF FPC_DOTTEDUNITS}
  60. uses System.RtlConsts,System.TypInfo;
  61. {$ELSE FPC_DOTTEDUNITS}
  62. uses rtlconsts,typinfo;
  63. {$ENDIF FPC_DOTTEDUNITS}
  64. { TNullable }
  65. function TNullable.GetIsNull: Boolean;
  66. begin
  67. Result:=Not HasValue;
  68. end;
  69. function TNullable.GetValue: T;
  70. begin
  71. if not FHasValue then
  72. raise EConvertError.CreateFmt(SErrCannotConvertNullToType,[PtypeInfo(TypeInfo(T))^.Name]);
  73. Result:=FValue;
  74. end;
  75. function TNullable.GetValueOrDefault: T;
  76. begin
  77. if HasValue then
  78. Result:=Value
  79. else
  80. Result:=Default(T);
  81. end;
  82. procedure TNullable.SetHasValue(AValue: Boolean);
  83. begin
  84. if FHasValue=AValue then Exit;
  85. if aValue then
  86. Value:=Default(T)
  87. else
  88. FHasValue:=False;
  89. end;
  90. procedure TNullable.SetValue(AValue: T);
  91. begin
  92. FValue:=aValue;
  93. FHasValue:=True;
  94. end;
  95. procedure TNullable.Clear;
  96. begin
  97. HasValue:=False;
  98. end;
  99. class operator TNullable.Initialize(var aSelf: TNullable);
  100. begin
  101. aSelf.FHasValue:=False;
  102. end;
  103. class function TNullable.Empty: TMyType; static;
  104. begin
  105. Result.HasValue:=False;
  106. end;
  107. class operator TNullable.Explicit(aValue: T): TMyType;
  108. begin
  109. Result.Value:=aValue;
  110. end;
  111. class operator TNullable.Explicit(aValue: TMyType): T;
  112. begin
  113. Result:=aValue.Value;
  114. end;
  115. class operator TNullable.:= (aValue: T): TMyType;
  116. begin
  117. Result.Value:=aValue;
  118. end;
  119. class operator TNullable.:= (aValue: TMyType): T;
  120. begin
  121. // We could use :=This is in line with TField's behaviour.
  122. Result:=aValue.Value;
  123. end;
  124. end.