TMutable
This commit is contained in:
+112
-5
@@ -8,14 +8,53 @@ uses
|
||||
Myc.Lazy;
|
||||
|
||||
type
|
||||
TMycNullLazy<T> = class(TInterfacedObject, IMycLazy<T>)
|
||||
TMycNullMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable)
|
||||
protected
|
||||
function GetChanged: TSignal;
|
||||
function GetValue: T;
|
||||
end;
|
||||
|
||||
TMycMutableBase<T> = class(TInterfacedObject, TMutable<T>.IMutable)
|
||||
strict private
|
||||
FChanged: TEvent;
|
||||
FChangeState: TSignal.TSubscription;
|
||||
function GetChanged: TSignal;
|
||||
protected
|
||||
function GetValue: T; virtual; abstract;
|
||||
public
|
||||
constructor Create(const AChanged: TSignal);
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
TMycFuncMutable<T> = class(TMycMutableBase<T>)
|
||||
private
|
||||
FProc: TFunc<T>;
|
||||
protected
|
||||
function GetValue: T; override;
|
||||
public
|
||||
constructor Create(const AChanged: TSignal; const AProc: TFunc<T>);
|
||||
end;
|
||||
|
||||
TMycWriteableMutable<T> = class(TInterfacedObject, TMutable<T>.IMutable, TMutable<T>.IWriteableMutable)
|
||||
private
|
||||
FValue: T;
|
||||
FChanged: TEvent;
|
||||
protected
|
||||
function GetChanged: TSignal;
|
||||
function GetValue: T;
|
||||
public
|
||||
constructor Create(const AValue: T);
|
||||
procedure SetValue(const Value: T);
|
||||
end;
|
||||
|
||||
TMycNullLazy<T> = class(TInterfacedObject, TLazy<T>.ILazy)
|
||||
protected
|
||||
function GetChanged: TState;
|
||||
public
|
||||
function Pop(out Res: T): Boolean;
|
||||
end;
|
||||
|
||||
TMycLazyBase<T> = class(TInterfacedObject, IMycLazy<T>)
|
||||
TMycLazyBase<T> = class(TInterfacedObject, TLazy<T>.ILazy)
|
||||
strict private
|
||||
FChanged: TFlag;
|
||||
FChangeState: TSignal.TSubscription;
|
||||
@@ -39,15 +78,72 @@ type
|
||||
|
||||
TMycChainedLazy<T> = class(TMycLazyBase<T>)
|
||||
private
|
||||
FValue: IMycLazy<T>;
|
||||
FValue: TLazy<T>.ILazy;
|
||||
protected
|
||||
function GetValue: T; override;
|
||||
public
|
||||
constructor Create(const AValue: IMycLazy<T>);
|
||||
constructor Create(const AValue: TLazy<T>.ILazy);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TMycNullMutable<T> }
|
||||
|
||||
function TMycNullMutable<T>.GetChanged: TSignal;
|
||||
begin
|
||||
Result := TSignal.Null;
|
||||
end;
|
||||
|
||||
function TMycNullMutable<T>.GetValue: T;
|
||||
begin
|
||||
Result := Default(T);
|
||||
end;
|
||||
|
||||
{ TMycMutableBase<T> }
|
||||
|
||||
constructor TMycMutableBase<T>.Create(const AChanged: TSignal);
|
||||
begin
|
||||
inherited Create;
|
||||
FChanged := TEvent.CreateEvent;
|
||||
FChangeState := AChanged.Subscribe(FChanged);
|
||||
end;
|
||||
|
||||
destructor TMycMutableBase<T>.Destroy;
|
||||
begin
|
||||
FChangeState.Unsubscribe;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TMycMutableBase<T>.GetChanged: TSignal;
|
||||
begin
|
||||
Result := FChanged.Signal;
|
||||
end;
|
||||
|
||||
{ TMycWriteableMutable<T> }
|
||||
|
||||
constructor TMycWriteableMutable<T>.Create(const AValue: T);
|
||||
begin
|
||||
inherited Create;
|
||||
FValue := AValue;
|
||||
FChanged := TEvent.CreateEvent;
|
||||
end;
|
||||
|
||||
function TMycWriteableMutable<T>.GetChanged: TSignal;
|
||||
begin
|
||||
Result := FChanged.Signal;
|
||||
end;
|
||||
|
||||
function TMycWriteableMutable<T>.GetValue: T;
|
||||
begin
|
||||
Result := FValue;
|
||||
end;
|
||||
|
||||
procedure TMycWriteableMutable<T>.SetValue(const Value: T);
|
||||
begin
|
||||
FValue := Value;
|
||||
FChanged.Notify;
|
||||
end;
|
||||
|
||||
{ TMycNullLazy<T> }
|
||||
|
||||
function TMycNullLazy<T>.GetChanged: TState;
|
||||
@@ -107,7 +203,7 @@ end;
|
||||
|
||||
{ TMycChainedLazy<T> }
|
||||
|
||||
constructor TMycChainedLazy<T>.Create(const AValue: IMycLazy<T>);
|
||||
constructor TMycChainedLazy<T>.Create(const AValue: TLazy<T>.ILazy);
|
||||
begin
|
||||
inherited Create(AValue.Changed);
|
||||
FValue := AValue;
|
||||
@@ -119,4 +215,15 @@ begin
|
||||
raise Exception.Create('Lazy chain already popped');
|
||||
end;
|
||||
|
||||
constructor TMycFuncMutable<T>.Create(const AChanged: TSignal; const AProc: TFunc<T>);
|
||||
begin
|
||||
inherited Create(AChanged);
|
||||
FProc := AProc;
|
||||
end;
|
||||
|
||||
function TMycFuncMutable<T>.GetValue: T;
|
||||
begin
|
||||
Result := FProc();
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
+44
-31
@@ -12,8 +12,8 @@ type
|
||||
// This state is always considered "set".
|
||||
TMycNullSignal = class(TInterfacedObject, TSignal.ISignal)
|
||||
public
|
||||
function Subscribe(Subscriber: IMycSubscriber): Pointer;
|
||||
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
||||
function Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||
procedure Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||
end;
|
||||
|
||||
// TMycNullState implements a "null object" pattern for IMycState.
|
||||
@@ -27,29 +27,31 @@ type
|
||||
// A state that acts as an event. It's state ist always set and it will always Trigger it's subscribers, if it gets notified by itself.
|
||||
TMycEvent = class(TInterfacedObject, TSignal.ISignal, TEvent.IEvent)
|
||||
strict private
|
||||
FSubscribers: TMycNotifyList<IMycSubscriber>;
|
||||
FSubscribers: TMycNotifyList<TSignal.ISubscriber>;
|
||||
protected
|
||||
function GetSignal: TSignal;
|
||||
function GetIsSet: Boolean;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
function Subscribe(Subscriber: IMycSubscriber): Pointer;
|
||||
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
||||
procedure Trigger;
|
||||
function Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||
procedure Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||
function Notify: Boolean;
|
||||
end;
|
||||
|
||||
TMycNullEvent = class(TMycNullSignal, TEvent.IEvent)
|
||||
private
|
||||
procedure Trigger;
|
||||
function GetSignal: TSignal;
|
||||
function Notify: Boolean;
|
||||
end;
|
||||
|
||||
// TMycLatch implements a countdown latch.
|
||||
// It is initialized with a count. Calls to its Notify method (as an IMycSubscriber)
|
||||
// It is initialized with a count. Calls to its Notify method (as an TSignal.ISubscriber)
|
||||
// decrement this count. When the count reaches zero, the latch transitions to the "set"
|
||||
// state (IsSet becomes true), notifies its current subscribers once, and then remains set.
|
||||
TMycLatch = class(TInterfacedObject, TState.IState, TLatch.ILatch)
|
||||
strict private
|
||||
FSubscribers: TMycNotifyList<IMycSubscriber>; // List of subscribers waiting for this latch to be set.
|
||||
FSubscribers: TMycNotifyList<TSignal.ISubscriber>; // List of subscribers waiting for this latch to be set.
|
||||
private
|
||||
[volatile]
|
||||
FCount: Integer; // The internal countdown value for the latch.
|
||||
@@ -63,10 +65,10 @@ type
|
||||
destructor Destroy; override;
|
||||
|
||||
// ISignal implementation
|
||||
function Subscribe(Subscriber: IMycSubscriber): Pointer;
|
||||
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
||||
function Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||
procedure Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||
|
||||
// IMycSubscriber implementation for TMycLatch itself.
|
||||
// TSignal.ISubscriber implementation for TMycLatch itself.
|
||||
// Decrements the internal count. If the count reaches zero,
|
||||
// registered subscribers are notified, and the latch becomes permanently set.
|
||||
function Notify: Boolean;
|
||||
@@ -83,7 +85,7 @@ type
|
||||
// Subscribers are notified of relevant state changes.
|
||||
TMycFlag = class(TInterfacedObject, TState.IState, TFlag.IFlag)
|
||||
strict private
|
||||
FSubscribers: TMycNotifyList<IMycSubscriber>; // List of subscribers interested in state changes of this dirty flag.
|
||||
FSubscribers: TMycNotifyList<TSignal.ISubscriber>; // List of subscribers interested in state changes of this dirty flag.
|
||||
private
|
||||
[volatile]
|
||||
FFlag: Boolean; // Internal state: true if dirty/set, false if clean/reset.
|
||||
@@ -98,10 +100,10 @@ type
|
||||
// Factory method to create a new TMycFlag instance.
|
||||
class function CreateDirty: TFlag.IFlag; static;
|
||||
|
||||
function Subscribe(Subscriber: IMycSubscriber): Pointer;
|
||||
procedure Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
||||
function Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||
procedure Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||
|
||||
// IMycSubscriber implementation for TMycFlag.
|
||||
// TSignal.ISubscriber implementation for TMycFlag.
|
||||
// Sets this flag to dirty (true). Notifies subscribers if it transitioned from clean to dirty.
|
||||
function Notify: Boolean;
|
||||
|
||||
@@ -145,17 +147,23 @@ begin
|
||||
Result := true;
|
||||
end;
|
||||
|
||||
procedure TMycEvent.Trigger;
|
||||
function TMycEvent.GetSignal: TSignal;
|
||||
begin
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function TMycEvent.Notify: Boolean;
|
||||
begin
|
||||
FSubscribers.Lock;
|
||||
try
|
||||
FSubscribers.Notify(function(Subscriber: IMycSubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||
FSubscribers.Notify(function(Subscriber: TSignal.ISubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||
finally
|
||||
FSubscribers.Release;
|
||||
end;
|
||||
Result := true;
|
||||
end;
|
||||
|
||||
function TMycEvent.Subscribe(Subscriber: IMycSubscriber): Pointer;
|
||||
function TMycEvent.Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||
begin
|
||||
Result := nil;
|
||||
if not Assigned(Subscriber) then
|
||||
@@ -170,7 +178,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycEvent.Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
||||
procedure TMycEvent.Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||
begin
|
||||
if Tag = nil then
|
||||
exit;
|
||||
@@ -227,7 +235,7 @@ begin
|
||||
|
||||
if shouldNotifySubscribers then
|
||||
begin
|
||||
FSubscribers.Notify(function(Subscriber: IMycSubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||
FSubscribers.Notify(function(Subscriber: TSignal.ISubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||
end;
|
||||
|
||||
// Returns true if the latch has not yet been set by this Notify call (count > 0).
|
||||
@@ -254,7 +262,7 @@ begin
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function TMycLatch.Subscribe(Subscriber: IMycSubscriber): Pointer;
|
||||
function TMycLatch.Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||
var
|
||||
alreadySet: Boolean;
|
||||
begin
|
||||
@@ -281,7 +289,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycLatch.Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
||||
procedure TMycLatch.Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||
begin
|
||||
if Tag = nil then
|
||||
exit;
|
||||
@@ -361,9 +369,9 @@ begin
|
||||
|
||||
if wasPreviouslyClean then // Only notify subscribers if state changed from clean to dirty.
|
||||
begin
|
||||
FSubscribers.Notify(function(Subscriber: IMycSubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||
FSubscribers.Notify(function(Subscriber: TSignal.ISubscriber): Boolean begin Result := Subscriber.Notify; end);
|
||||
end;
|
||||
// The return value of this Notify (as an IMycSubscriber) indicates if this
|
||||
// The return value of this Notify (as an TSignal.ISubscriber) indicates if this
|
||||
// TMycFlag instance itself would want more notifications if it were subscribed to something.
|
||||
// Here, it reflects whether a state change to dirty occurred due to this call.
|
||||
Result := wasPreviouslyClean;
|
||||
@@ -372,7 +380,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMycFlag.Subscribe(Subscriber: IMycSubscriber): Pointer;
|
||||
function TMycFlag.Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||
begin
|
||||
if not Assigned(Subscriber) then
|
||||
exit(nil);
|
||||
@@ -394,7 +402,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycFlag.Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
||||
procedure TMycFlag.Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||
begin
|
||||
if Tag = nil then
|
||||
exit;
|
||||
@@ -407,12 +415,17 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycNullEvent.Trigger;
|
||||
function TMycNullEvent.GetSignal: TSignal;
|
||||
begin
|
||||
// nop
|
||||
Result := TSignal.Null;
|
||||
end;
|
||||
|
||||
function TMycNullSignal.Subscribe(Subscriber: IMycSubscriber): Pointer;
|
||||
function TMycNullEvent.Notify: Boolean;
|
||||
begin
|
||||
Result := false;
|
||||
end;
|
||||
|
||||
function TMycNullSignal.Subscribe(Subscriber: TSignal.ISubscriber): Pointer;
|
||||
begin
|
||||
// Since the state is always set, notify the subscriber immediately.
|
||||
if Assigned(Subscriber) then
|
||||
@@ -423,7 +436,7 @@ begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
procedure TMycNullSignal.Unsubscribe(Tag: TMycNotifyList<IMycSubscriber>.TTag);
|
||||
procedure TMycNullSignal.Unsubscribe(Tag: TMycNotifyList<TSignal.ISubscriber>.TTag);
|
||||
begin
|
||||
// Unsubscribing from a null state has no effect.
|
||||
end;
|
||||
|
||||
@@ -36,7 +36,7 @@ type
|
||||
// Exceptions during Job execution are caught; the first one is stored
|
||||
// and can be re-raised in the main thread via WaitFor or Teardown.
|
||||
// Raises ETaskException if the factory is already terminated.
|
||||
function Run(Job: TProc): IMycSubscriber;
|
||||
function Run(Job: TProc): TSignal.ISubscriber;
|
||||
|
||||
procedure EnqueueJob(const Job: TProc);
|
||||
|
||||
@@ -80,7 +80,7 @@ type
|
||||
function CreateThread(const Proc: TProc): TState.IState;
|
||||
procedure HandleException;
|
||||
procedure EnqueueJob(const Job: TProc);
|
||||
function Run(Job: TProc): IMycSubscriber;
|
||||
function Run(Job: TProc): TSignal.ISubscriber;
|
||||
function CreateTask(const Gate: TState; const Proc: TProc): TSignal.TSubscription;
|
||||
procedure WaitFor(State: TState.IState);
|
||||
procedure Teardown;
|
||||
@@ -98,7 +98,7 @@ type
|
||||
implementation
|
||||
|
||||
type
|
||||
TMycPendingJob = class(TInterfacedObject, IMycSubscriber)
|
||||
TMycPendingJob = class(TInterfacedObject, TSignal.ISubscriber)
|
||||
private
|
||||
[volatile]
|
||||
FJob: TProc; // The job to execute when count reaches zero
|
||||
@@ -112,7 +112,7 @@ type
|
||||
property Owner: TMycTaskFactory read FOwner;
|
||||
end;
|
||||
|
||||
TMycTaskWait = class sealed(TInterfacedObject, IMycSubscriber)
|
||||
TMycTaskWait = class sealed(TInterfacedObject, TSignal.ISubscriber)
|
||||
private
|
||||
[volatile]
|
||||
FSemaphore: TSemaphore; // System.SyncObjs.TSemaphore to be released on notification
|
||||
@@ -303,7 +303,7 @@ begin
|
||||
TaskManager := nil;
|
||||
end;
|
||||
|
||||
function TMycTaskFactory.Run(Job: TProc): IMycSubscriber;
|
||||
function TMycTaskFactory.Run(Job: TProc): TSignal.ISubscriber;
|
||||
begin
|
||||
if FTerminated <> 0 then
|
||||
raise ETaskException.Create('Task factory terminated');
|
||||
|
||||
@@ -81,7 +81,6 @@ end;
|
||||
function TFuture<T>.Chain<S>(const Proc: TFunc<T, S>): TFuture<S>;
|
||||
begin
|
||||
var Cap := FFuture;
|
||||
|
||||
Result := TFuture<S>.Construct(FFuture.Done, function: S begin Result := Proc(Cap.Result); end);
|
||||
end;
|
||||
|
||||
|
||||
+111
-21
@@ -7,14 +7,49 @@ uses
|
||||
Myc.Signals;
|
||||
|
||||
type
|
||||
IMycMutable<T> = interface
|
||||
function GetChanged: TState;
|
||||
TMutable<T> = record
|
||||
type
|
||||
IMutable = interface
|
||||
{$REGION 'property access'}
|
||||
function GetChanged: TSignal;
|
||||
function GetValue: T;
|
||||
property Changed: TState read GetChanged;
|
||||
{$ENDREGION}
|
||||
property Changed: TSignal read GetChanged;
|
||||
property Value: T read GetValue;
|
||||
end;
|
||||
|
||||
IMycLazy<T> = interface
|
||||
IWriteableMutable = interface(IMutable)
|
||||
procedure SetValue(const Value: T);
|
||||
end;
|
||||
|
||||
{$REGION 'private'}
|
||||
strict private
|
||||
class var
|
||||
FNull: IMutable;
|
||||
class constructor CreateClass;
|
||||
|
||||
private
|
||||
FMutable: IMutable;
|
||||
function GetChanged: TSignal; inline;
|
||||
{$ENDREGION}
|
||||
public
|
||||
constructor Create(const AMutable: IMutable);
|
||||
class operator Initialize(out Dest: TMutable<T>);
|
||||
class operator Implicit(const A: IMutable): TMutable<T>; overload;
|
||||
class operator Implicit(const A: TMutable<T>): IMutable; overload;
|
||||
|
||||
class property Null: IMutable read FNull;
|
||||
|
||||
class function Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>; overload; static;
|
||||
|
||||
class function CreateWriteable(const Init: T): IWriteableMutable; overload; static;
|
||||
|
||||
property Changed: TSignal read GetChanged;
|
||||
end;
|
||||
|
||||
TLazy<T> = record
|
||||
type
|
||||
ILazy = interface
|
||||
{$REGION 'property access'}
|
||||
function GetChanged: TState;
|
||||
{$ENDREGION}
|
||||
@@ -22,26 +57,30 @@ type
|
||||
property Changed: TState read GetChanged;
|
||||
end;
|
||||
|
||||
TLazy<T> = record
|
||||
private
|
||||
FLazy: IMycLazy<T>;
|
||||
function GetChanged: TState.IState; inline;
|
||||
{$REGION 'private'}
|
||||
strict private
|
||||
class var
|
||||
FNull: IMycLazy<T>;
|
||||
FNull: ILazy;
|
||||
class constructor CreateClass;
|
||||
|
||||
private
|
||||
FLazy: ILazy;
|
||||
function GetChanged: TState; inline;
|
||||
{$ENDREGION}
|
||||
public
|
||||
constructor Create(const ALazy: IMycLazy<T>);
|
||||
constructor Create(const ALazy: ILazy);
|
||||
class operator Initialize(out Dest: TLazy<T>);
|
||||
class operator Implicit(const A: ILazy): TLazy<T>; overload;
|
||||
class operator Implicit(const A: TLazy<T>): ILazy; overload;
|
||||
|
||||
class function Construct(const Changing: TState.IState; const Proc: TFunc<T>): IMycLazy<T>; overload; static;
|
||||
class function Construct(const Value: IMycLazy<T>): IMycLazy<T>; overload; static;
|
||||
class function Construct(const Changing: TState; const Proc: TFunc<T>): TLazy<T>; overload; static;
|
||||
class function Construct(const Value: ILazy): TLazy<T>; overload; static;
|
||||
|
||||
class operator Implicit(const A: IMycLazy<T>): TLazy<T>; overload;
|
||||
class operator Implicit(const A: TLazy<T>): IMycLazy<T>; overload;
|
||||
class property Null: ILazy read FNull;
|
||||
|
||||
function Pop(out Res: T): Boolean; inline;
|
||||
|
||||
property Changed: TState.IState read GetChanged;
|
||||
property Changed: TState read GetChanged;
|
||||
end;
|
||||
|
||||
implementation
|
||||
@@ -49,14 +88,60 @@ implementation
|
||||
uses
|
||||
Myc.Core.Lazy;
|
||||
|
||||
constructor TLazy<T>.Create(const ALazy: IMycLazy<T>);
|
||||
{ TMutable<T> }
|
||||
|
||||
constructor TMutable<T>.Create(const AMutable: IMutable);
|
||||
begin
|
||||
FMutable := AMutable;
|
||||
if not Assigned(FMutable) then
|
||||
FMutable := FNull;
|
||||
end;
|
||||
|
||||
class constructor TMutable<T>.CreateClass;
|
||||
begin
|
||||
FNull := TMycNullMutable<T>.Create;
|
||||
end;
|
||||
|
||||
class function TMutable<T>.Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>;
|
||||
begin
|
||||
Result := TMycFuncMutable<T>.Create(Changing, Proc);
|
||||
end;
|
||||
|
||||
class function TMutable<T>.CreateWriteable(const Init: T): IWriteableMutable;
|
||||
begin
|
||||
Result := TMycWriteableMutable<T>.Create(Init);
|
||||
end;
|
||||
|
||||
function TMutable<T>.GetChanged: TSignal;
|
||||
begin
|
||||
Result := FMutable.Changed;
|
||||
end;
|
||||
|
||||
class operator TMutable<T>.Implicit(const A: TMutable<T>): IMutable;
|
||||
begin
|
||||
Result := A.FMutable;
|
||||
end;
|
||||
|
||||
class operator TMutable<T>.Implicit(const A: IMutable): TMutable<T>;
|
||||
begin
|
||||
Result.Create(A);
|
||||
end;
|
||||
|
||||
class operator TMutable<T>.Initialize(out Dest: TMutable<T>);
|
||||
begin
|
||||
Dest.FMutable := FNull;
|
||||
end;
|
||||
|
||||
{ TLazy<T> }
|
||||
|
||||
constructor TLazy<T>.Create(const ALazy: ILazy);
|
||||
begin
|
||||
FLazy := ALazy;
|
||||
if not Assigned(FLazy) then
|
||||
FLazy := FNull;
|
||||
end;
|
||||
|
||||
class function TLazy<T>.Construct(const Changing: TState.IState; const Proc: TFunc<T>): IMycLazy<T>;
|
||||
class function TLazy<T>.Construct(const Changing: TState; const Proc: TFunc<T>): TLazy<T>;
|
||||
begin
|
||||
Result := TMycFuncLazy<T>.Create(Changing, Proc);
|
||||
end;
|
||||
@@ -66,12 +151,12 @@ begin
|
||||
FNull := TMycNullLazy<T>.Create;
|
||||
end;
|
||||
|
||||
class function TLazy<T>.Construct(const Value: IMycLazy<T>): IMycLazy<T>;
|
||||
class function TLazy<T>.Construct(const Value: ILazy): TLazy<T>;
|
||||
begin
|
||||
Result := TMycChainedLazy<T>.Create(Value);
|
||||
end;
|
||||
|
||||
function TLazy<T>.GetChanged: TState.IState;
|
||||
function TLazy<T>.GetChanged: TState;
|
||||
begin
|
||||
Result := FLazy.Changed;
|
||||
end;
|
||||
@@ -81,14 +166,19 @@ begin
|
||||
Result := FLazy.Pop(Res);
|
||||
end;
|
||||
|
||||
class operator TLazy<T>.Implicit(const A: IMycLazy<T>): TLazy<T>;
|
||||
class operator TLazy<T>.Implicit(const A: ILazy): TLazy<T>;
|
||||
begin
|
||||
Result.Create(A);
|
||||
end;
|
||||
|
||||
class operator TLazy<T>.Implicit(const A: TLazy<T>): IMycLazy<T>;
|
||||
class operator TLazy<T>.Implicit(const A: TLazy<T>): ILazy;
|
||||
begin
|
||||
Result := A.FLazy;
|
||||
end;
|
||||
|
||||
class operator TLazy<T>.Initialize(out Dest: TLazy<T>);
|
||||
begin
|
||||
Dest.FLazy := FNull;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
+58
-47
@@ -6,16 +6,16 @@ uses
|
||||
System.SysUtils;
|
||||
|
||||
type
|
||||
IMycSubscriber = interface
|
||||
TSignal = record
|
||||
type
|
||||
ISubscriber = interface
|
||||
function Notify: Boolean;
|
||||
end;
|
||||
|
||||
TSignal = record
|
||||
type
|
||||
TSubscriptionTag = Pointer;
|
||||
|
||||
ISignal = interface
|
||||
function Subscribe(Subscriber: IMycSubscriber): TSubscriptionTag;
|
||||
function Subscribe(Subscriber: ISubscriber): TSubscriptionTag;
|
||||
procedure Unsubscribe(Tag: TSubscriptionTag);
|
||||
end;
|
||||
|
||||
@@ -45,38 +45,11 @@ type
|
||||
class operator Implicit(const A: ISignal): TSignal; overload;
|
||||
class operator Implicit(const A: TSignal): ISignal; overload;
|
||||
|
||||
function Subscribe(Subscriber: IMycSubscriber): TSubscription; inline;
|
||||
function Subscribe(Subscriber: ISubscriber): TSubscription; inline;
|
||||
|
||||
class property Null: ISignal read FNull;
|
||||
end;
|
||||
|
||||
TEvent = record
|
||||
type
|
||||
IEvent = interface(TSignal.ISignal)
|
||||
procedure Trigger;
|
||||
end;
|
||||
|
||||
{$REGION 'private'}
|
||||
strict private
|
||||
class var
|
||||
FNull: IEvent;
|
||||
class constructor ClassCreate;
|
||||
|
||||
private
|
||||
FEvent: IEvent;
|
||||
{$ENDREGION}
|
||||
public
|
||||
constructor Create(const AEvent: IEvent);
|
||||
class operator Initialize(out Dest: TEvent);
|
||||
class operator Implicit(const A: IEvent): TEvent; overload;
|
||||
class operator Implicit(const A: TEvent): IEvent; overload;
|
||||
|
||||
class property Null: IEvent read FNull;
|
||||
|
||||
function Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription; inline;
|
||||
procedure Unsubscribe(Tag: Pointer); inline;
|
||||
end;
|
||||
|
||||
TState = record
|
||||
type
|
||||
IState = interface(TSignal.ISignal)
|
||||
@@ -108,18 +81,54 @@ type
|
||||
|
||||
class property Null: IState read FNull;
|
||||
|
||||
function Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription; inline;
|
||||
procedure Unsubscribe(Tag: Pointer); inline;
|
||||
function Subscribe(Subscriber: TSignal.ISubscriber): TSignal.TSubscription; inline;
|
||||
|
||||
property IsSet: Boolean read GetIsSet;
|
||||
end;
|
||||
|
||||
// An event is a stateless signal that forwards all notifications to the subscribers.
|
||||
TEvent = record
|
||||
type
|
||||
IEvent = interface(TSignal.ISubscriber)
|
||||
{$REGION 'property access'}
|
||||
function GetSignal: TSignal;
|
||||
{$ENDREGION}
|
||||
property Signal: TSignal read GetSignal;
|
||||
end;
|
||||
|
||||
{$REGION 'private'}
|
||||
strict private
|
||||
class var
|
||||
FNull: IEvent;
|
||||
class constructor ClassCreate;
|
||||
|
||||
private
|
||||
FEvent: IEvent;
|
||||
function GetSignal: TSignal; inline;
|
||||
{$ENDREGION}
|
||||
public
|
||||
constructor Create(const AEvent: IEvent);
|
||||
class operator Initialize(out Dest: TEvent);
|
||||
class operator Implicit(const A: IEvent): TEvent; overload;
|
||||
class operator Implicit(const A: TEvent): IEvent; overload;
|
||||
|
||||
class function CreateEvent: TEvent; static;
|
||||
|
||||
class property Null: IEvent read FNull;
|
||||
|
||||
function Notify: Boolean; inline;
|
||||
|
||||
property Signal: TSignal read GetSignal;
|
||||
end;
|
||||
|
||||
// A latch is a specific type of event that, once set, remains set (non-resettable).
|
||||
// It becomes set when the internal countdown reaches zero.
|
||||
TLatch = record
|
||||
type
|
||||
ILatch = interface(IMycSubscriber)
|
||||
ILatch = interface(TSignal.ISubscriber)
|
||||
{$REGION 'property access'}
|
||||
function GetState: TState;
|
||||
{$ENDREGION}
|
||||
property State: TState read GetState;
|
||||
end;
|
||||
|
||||
@@ -153,8 +162,10 @@ type
|
||||
// TFlag represents a resettable flag, typically indicating if a State is "dirty" (requiring attention) or "clean".
|
||||
TFlag = record
|
||||
type
|
||||
IFlag = interface(IMycSubscriber)
|
||||
IFlag = interface(TSignal.ISubscriber)
|
||||
{$REGION 'property access'}
|
||||
function GetState: TState;
|
||||
{$ENDREGION}
|
||||
function Reset: Boolean;
|
||||
property State: TState read GetState;
|
||||
end;
|
||||
@@ -260,16 +271,11 @@ begin
|
||||
Result := FState.IsSet;
|
||||
end;
|
||||
|
||||
function TState.Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription;
|
||||
function TState.Subscribe(Subscriber: TSignal.ISubscriber): TSignal.TSubscription;
|
||||
begin
|
||||
Result := TSignal.TSubscription.Create(FState, FState.Subscribe(Subscriber));
|
||||
end;
|
||||
|
||||
procedure TState.Unsubscribe(Tag: Pointer);
|
||||
begin
|
||||
FState.Unsubscribe(Tag);
|
||||
end;
|
||||
|
||||
class operator TState.Implicit(const A: TState): IState;
|
||||
begin
|
||||
Result := A.FState;
|
||||
@@ -299,14 +305,19 @@ begin
|
||||
FNull := TMycNullEvent.Create();
|
||||
end;
|
||||
|
||||
function TEvent.Subscribe(Subscriber: IMycSubscriber): TSignal.TSubscription;
|
||||
class function TEvent.CreateEvent: TEvent;
|
||||
begin
|
||||
Result := TSignal.TSubscription.Create(FEvent, FEvent.Subscribe(Subscriber));
|
||||
Result := TMycEvent.Create;
|
||||
end;
|
||||
|
||||
procedure TEvent.Unsubscribe(Tag: Pointer);
|
||||
function TEvent.GetSignal: TSignal;
|
||||
begin
|
||||
FEvent.Unsubscribe(Tag);
|
||||
Result := FEvent.Signal;
|
||||
end;
|
||||
|
||||
function TEvent.Notify: Boolean;
|
||||
begin
|
||||
Result := FEvent.Notify;
|
||||
end;
|
||||
|
||||
class operator TEvent.Implicit(const A: TEvent): IEvent;
|
||||
@@ -443,7 +454,7 @@ begin
|
||||
FSignal := FNull;
|
||||
end;
|
||||
|
||||
function TSignal.Subscribe(Subscriber: IMycSubscriber): TSubscription;
|
||||
function TSignal.Subscribe(Subscriber: ISubscriber): TSubscription;
|
||||
begin
|
||||
Result := TSubscription.Create(FSignal, FSignal.Subscribe(Subscriber));
|
||||
end;
|
||||
|
||||
@@ -30,7 +30,7 @@ uses
|
||||
System.Generics.Collections;
|
||||
|
||||
type
|
||||
TMycExecMock = class(TInterfacedObject, IMycSubscriber)
|
||||
TMycExecMock = class(TInterfacedObject, TSignal.ISubscriber)
|
||||
private
|
||||
FProc: TProc;
|
||||
public
|
||||
|
||||
+21
-21
@@ -13,8 +13,8 @@ type
|
||||
[TestFixture]
|
||||
TTestMycCoreLazy = class(TObject)
|
||||
private
|
||||
procedure Helper_ConsumeInitialPop(const ALazy: IMycLazy<Integer>; InitialExpectedValue: Integer); overload;
|
||||
procedure Helper_ConsumeInitialPop(const ALazy: IMycLazy<string>; const InitialExpectedValue: string); overload;
|
||||
procedure Helper_ConsumeInitialPop(const ALazy: TLazy<Integer>.ILazy; InitialExpectedValue: Integer); overload;
|
||||
procedure Helper_ConsumeInitialPop(const ALazy: TLazy<String>.ILazy; const InitialExpectedValue: string); overload;
|
||||
public
|
||||
[Setup]
|
||||
procedure Setup;
|
||||
@@ -65,7 +65,7 @@ uses
|
||||
|
||||
{ TTestMycCoreLazy Helper Methods }
|
||||
|
||||
procedure TTestMycCoreLazy.Helper_ConsumeInitialPop(const ALazy: IMycLazy<Integer>; InitialExpectedValue: Integer);
|
||||
procedure TTestMycCoreLazy.Helper_ConsumeInitialPop(const ALazy: TLazy<Integer>.ILazy; InitialExpectedValue: Integer);
|
||||
var
|
||||
tempValue: Integer;
|
||||
popResult: Boolean;
|
||||
@@ -77,7 +77,7 @@ begin
|
||||
Assert.IsFalse(ALazy.GetChanged.IsSet, 'Changed.IsSet should be false after consuming initial pop');
|
||||
end;
|
||||
|
||||
procedure TTestMycCoreLazy.Helper_ConsumeInitialPop(const ALazy: IMycLazy<string>; const InitialExpectedValue: string);
|
||||
procedure TTestMycCoreLazy.Helper_ConsumeInitialPop(const ALazy: TLazy<String>.ILazy; const InitialExpectedValue: string);
|
||||
var
|
||||
tempValue: string;
|
||||
popResult: Boolean;
|
||||
@@ -102,10 +102,10 @@ end;
|
||||
// == Tests for TMycNullLazy<T> ==
|
||||
procedure TTestMycCoreLazy.TestNullLazy_GetChanged_IsAlwaysNullState;
|
||||
var
|
||||
nullLazy: IMycLazy<Integer>;
|
||||
nullLazy: TLazy<Integer>.ILazy;
|
||||
changedState: TState.IState;
|
||||
begin
|
||||
nullLazy := TMycNullLazy<Integer>.Create as IMycLazy<Integer>;
|
||||
nullLazy := TMycNullLazy<Integer>.Create as TLazy<Integer>.ILazy;
|
||||
Assert.IsNotNull(nullLazy, 'TMycNullLazy<Integer> instance should not be nil');
|
||||
changedState := nullLazy.GetChanged;
|
||||
Assert.AreSame(TState.Null, changedState, 'TMycNullLazy.GetChanged should return TState.Null');
|
||||
@@ -114,11 +114,11 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultIntegerAndTrue;
|
||||
var
|
||||
nullLazy: IMycLazy<Integer>;
|
||||
nullLazy: TLazy<Integer>.ILazy;
|
||||
value: Integer;
|
||||
result: Boolean;
|
||||
begin
|
||||
nullLazy := TMycNullLazy<Integer>.Create as IMycLazy<Integer>;
|
||||
nullLazy := TMycNullLazy<Integer>.Create as TLazy<Integer>.ILazy;
|
||||
Assert.IsNotNull(nullLazy, 'TMycNullLazy<Integer> instance should not be nil');
|
||||
value := 123;
|
||||
result := nullLazy.Pop(value);
|
||||
@@ -128,11 +128,11 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultStringAndTrue;
|
||||
var
|
||||
nullLazy: IMycLazy<string>;
|
||||
nullLazy: TLazy<String>.ILazy;
|
||||
value: string;
|
||||
result: Boolean;
|
||||
begin
|
||||
nullLazy := TMycNullLazy<string>.Create as IMycLazy<string>;
|
||||
nullLazy := TMycNullLazy<string>.Create as TLazy<String>.ILazy;
|
||||
Assert.IsNotNull(nullLazy, 'TMycNullLazy<string> instance should not be nil');
|
||||
value := 'test';
|
||||
result := nullLazy.Pop(value);
|
||||
@@ -142,11 +142,11 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestNullLazy_Pop_ReturnsDefaultInterfaceAndTrue;
|
||||
var
|
||||
nullLazy: IMycLazy<TState.IState>;
|
||||
nullLazy: TLazy<TState.IState>.ILazy;
|
||||
value: TState.IState;
|
||||
result: Boolean;
|
||||
begin
|
||||
nullLazy := TMycNullLazy<TState.IState>.Create as IMycLazy<TState.IState>;
|
||||
nullLazy := TMycNullLazy<TState.IState>.Create as TLazy<TState.IState>.ILazy;
|
||||
Assert.IsNotNull(nullLazy, 'TMycNullLazy<TState.IState> instance should not be nil');
|
||||
value := TState.Null;
|
||||
result := nullLazy.Pop(value);
|
||||
@@ -157,7 +157,7 @@ end;
|
||||
// == Tests for TMycFuncLazy<T> - Initial State by Design ==
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_GetChanged_IsAlwaysTrueAfterCreation;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
changedState: TState.IState;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
begin
|
||||
@@ -172,7 +172,7 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_FirstPop_AlwaysEvaluatesAndResetsChanged;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
value: Integer;
|
||||
result: Boolean;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
@@ -206,7 +206,7 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_AfterFirstPop_IfNoSourceChange_GetChangedIsFalse;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
initialProcValue: Integer;
|
||||
begin
|
||||
@@ -220,7 +220,7 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_AfterFirstPop_IfNoSourceChange_PopReturnsFalse_ValueUndefined;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
value: Integer;
|
||||
result: Boolean;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
@@ -249,7 +249,7 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_AfterSourceChange_GetChanged_IsSet;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
changedState: TState.IState;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
initialProcValue: Integer;
|
||||
@@ -266,7 +266,7 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_AfterSourceChange_Pop_ReturnsTrueAndProcResult_ResetsChanged;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
value: Integer;
|
||||
result: Boolean;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
@@ -304,7 +304,7 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_Pop_Twice_SourceUnchanged_SecondPopReturnsFalse_ValueUndefined;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
value: Integer;
|
||||
resultPop1, resultPop2: Boolean;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
@@ -336,7 +336,7 @@ end;
|
||||
// to TestFuncLazy_SourceInteraction_NotifyOnSetSourceDoesNotRetriggerLazy
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_SourceInteraction_NotifyOnSetSourceDoesNotRetriggerLazy;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
value: Integer;
|
||||
result: Boolean;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
@@ -417,7 +417,7 @@ end;
|
||||
|
||||
procedure TTestMycCoreLazy.TestFuncLazy_SourceIsInitiallySet_PopBehavesCorrectly;
|
||||
var
|
||||
funcLazy: IMycLazy<Integer>;
|
||||
funcLazy: TLazy<Integer>.ILazy;
|
||||
value: Integer;
|
||||
result: Boolean;
|
||||
sourceDirty: TFlag.IFlag;
|
||||
|
||||
+16
-16
@@ -137,7 +137,7 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestConstruct_InitialChanged_IsAlwaysTrue;
|
||||
var
|
||||
lazyIntf: IMycLazy<Integer>;
|
||||
lazyIntf: TLazy<Integer>.ILazy;
|
||||
lazyRec: TLazy<Integer>;
|
||||
procExecuted: Boolean;
|
||||
begin
|
||||
@@ -161,7 +161,7 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestConstruct_FirstPop_SucceedsAndResetsChanged;
|
||||
var
|
||||
lazyIntf: IMycLazy<Integer>;
|
||||
lazyIntf: TLazy<Integer>.ILazy;
|
||||
lazyRec: TLazy<Integer>;
|
||||
procExecuted: Boolean;
|
||||
expectedValue: Integer;
|
||||
@@ -185,7 +185,7 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestConstruct_StateInteraction_SignalTriggersChanged;
|
||||
var
|
||||
lazyIntf: IMycLazy<Integer>;
|
||||
lazyIntf: TLazy<Integer>.ILazy;
|
||||
lazyRec: TLazy<Integer>;
|
||||
expectedValue: Integer;
|
||||
begin
|
||||
@@ -203,7 +203,7 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestConstruct_StateInteraction_PopResetsChangedAfterSignal;
|
||||
var
|
||||
lazyIntf: IMycLazy<Integer>;
|
||||
lazyIntf: TLazy<Integer>.ILazy;
|
||||
lazyRec: TLazy<Integer>;
|
||||
val: Integer;
|
||||
popResult: Boolean;
|
||||
@@ -234,7 +234,7 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestConstruct_StateInteraction_NotifyOnAlreadySetSource_DoesNotRetrigger;
|
||||
var
|
||||
lazyIntf: IMycLazy<Integer>;
|
||||
lazyIntf: TLazy<Integer>.ILazy;
|
||||
lazyRec: TLazy<Integer>;
|
||||
val: Integer;
|
||||
popResult: Boolean;
|
||||
@@ -280,7 +280,7 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestConstruct_Destruction_UnsubscribesAndNoCrashOnSourceNotify;
|
||||
var
|
||||
lazyIntf: IMycLazy<Integer>;
|
||||
lazyIntf: TLazy<Integer>.ILazy;
|
||||
localChangingSignal: TFlag.IFlag; // Use a local signal for this test to control its lifetime
|
||||
begin
|
||||
localChangingSignal := TFlag.CreateFlag;
|
||||
@@ -293,7 +293,7 @@ begin
|
||||
var lazyRec: TLazy<Integer> := lazyIntf; // Wrap for initial pop
|
||||
ConsumeInitialPop(lazyRec, 1, 'TestConstruct_Destruction (Initial)');
|
||||
|
||||
lazyIntf := nil; // Release the IMycLazy interface. ARC should destroy the TMycFuncLazy object.
|
||||
lazyIntf := nil; // Release the ILazy interface. ARC should destroy the TMycFuncLazy object.
|
||||
// This should trigger its destructor, which should unsubscribe from localChangingSignal.
|
||||
|
||||
Assert.WillNotRaise(
|
||||
@@ -308,11 +308,11 @@ begin
|
||||
localChangingSignal := nil; // Clean up the local signal itself.
|
||||
end;
|
||||
|
||||
// == Tests for TLazy<T>.Create with a pre-existing (non-nil) IMycLazy ==
|
||||
// == Tests for TLazy<T>.Create with a pre-existing (non-nil) ILazy ==
|
||||
|
||||
procedure TTestMyLazy.TestCreateWithExistingLazy_DelegatesChangedCorrectly;
|
||||
var
|
||||
originalLazyIntf: IMycLazy<Integer>;
|
||||
originalLazyIntf: TLazy<Integer>.ILazy;
|
||||
wrappedLazyRec: TLazy<Integer>;
|
||||
expectedValue: Integer;
|
||||
begin
|
||||
@@ -335,7 +335,7 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestCreateWithExistingLazy_DelegatesPopCorrectly;
|
||||
var
|
||||
originalLazyIntf: IMycLazy<Integer>;
|
||||
originalLazyIntf: TLazy<Integer>.ILazy;
|
||||
wrappedLazyRec: TLazy<Integer>;
|
||||
val: Integer;
|
||||
popResult: Boolean;
|
||||
@@ -376,7 +376,7 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestImplicitOperator_FromInterfaceToRecord;
|
||||
var
|
||||
lazyIntf: IMycLazy<Integer>;
|
||||
lazyIntf: TLazy<Integer>.ILazy;
|
||||
lazyRec: TLazy<Integer>;
|
||||
expectedValue: Integer;
|
||||
begin
|
||||
@@ -384,7 +384,7 @@ begin
|
||||
lazyIntf := TLazy<Integer>.Construct(FChangingSignal.State, function: Integer begin Result := expectedValue; end);
|
||||
Assert.IsNotNull(lazyIntf, 'Interface should be assigned');
|
||||
|
||||
lazyRec := lazyIntf; // Implicit conversion: IMycLazy<T> to TLazy<T>
|
||||
lazyRec := lazyIntf; // Implicit conversion: ILazy<T> to TLazy<T>
|
||||
|
||||
// Verify by using the record
|
||||
Assert.IsTrue(lazyRec.Changed.IsSet, 'Record (from intf): Initial Changed.IsSet should be true');
|
||||
@@ -393,9 +393,9 @@ end;
|
||||
|
||||
procedure TTestMyLazy.TestImplicitOperator_FromRecordToInterface;
|
||||
var
|
||||
lazyIntfFromConstruct: IMycLazy<Integer>;
|
||||
lazyIntfFromConstruct: TLazy<Integer>.ILazy;
|
||||
lazyRec: TLazy<Integer>;
|
||||
lazyIntfFromRecord: IMycLazy<Integer>;
|
||||
lazyIntfFromRecord: TLazy<Integer>.ILazy;
|
||||
val: Integer;
|
||||
expectedValue: Integer;
|
||||
begin
|
||||
@@ -403,7 +403,7 @@ begin
|
||||
lazyIntfFromConstruct := TLazy<Integer>.Construct(FChangingSignal.State, function: Integer begin Result := expectedValue; end);
|
||||
lazyRec.Create(lazyIntfFromConstruct); // Explicitly create record
|
||||
|
||||
lazyIntfFromRecord := lazyRec; // Implicit conversion: TLazy<T> to IMycLazy<T>
|
||||
lazyIntfFromRecord := lazyRec; // Implicit conversion: TLazy<T> to ILazy<T>
|
||||
|
||||
// Verify by using the converted interface
|
||||
Assert.AreSame(lazyIntfFromConstruct, lazyIntfFromRecord, 'Converted interface should be the same as the original wrapped one');
|
||||
@@ -417,7 +417,7 @@ end;
|
||||
// == Tests for TLazy<T>.Pop specific behaviors (Res undefined) ==
|
||||
procedure TTestMyLazy.TestPop_AfterInitialAndNoSignal_ReturnsFalseAndResUndefined;
|
||||
var
|
||||
lazyIntf: IMycLazy<Integer>;
|
||||
lazyIntf: TLazy<Integer>.ILazy;
|
||||
lazyRec: TLazy<Integer>;
|
||||
val: Integer; // Value will not be checked as Pop returns false
|
||||
popResult: Boolean;
|
||||
|
||||
@@ -9,7 +9,7 @@ uses
|
||||
|
||||
type
|
||||
// Helper class to mock a subscriber (reuse or redefine if not in common unit)
|
||||
TMockSubscriber = class(TInterfacedObject, IMycSubscriber)
|
||||
TMockSubscriber = class(TInterfacedObject, TSignal.ISubscriber)
|
||||
public
|
||||
NotifyCount: Integer;
|
||||
NotifyShouldReturn: Boolean;
|
||||
@@ -17,7 +17,7 @@ type
|
||||
LastReturnedValueFromSource: Boolean; // To store what the source's Notify returned
|
||||
|
||||
constructor Create(AShouldReturn: Boolean = True);
|
||||
function Notify: Boolean; // IMycSubscriber implementation.
|
||||
function Notify: Boolean; // TSignal.ISubscriber implementation.
|
||||
end;
|
||||
|
||||
[TestFixture]
|
||||
@@ -164,7 +164,7 @@ var
|
||||
begin
|
||||
dirtyFlag := CreateAndPrepareDirtyFlag(False); // Start clean
|
||||
|
||||
notifyResult := dirtyFlag.Notify; // Call IMycSubscriber.Notify to make it dirty
|
||||
notifyResult := dirtyFlag.Notify; // Call TSignal.ISubscriber.Notify to make it dirty
|
||||
|
||||
Assert.IsTrue(dirtyFlag.State.IsSet, 'Flag should be dirty (IsSet) after Notify on clean flag.');
|
||||
Assert.IsTrue(notifyResult, 'Notify() should return True indicating a state change from clean to dirty.');
|
||||
@@ -177,7 +177,7 @@ var
|
||||
begin
|
||||
dirtyFlag := CreateAndPrepareDirtyFlag(True); // Start dirty
|
||||
|
||||
notifyResult := dirtyFlag.Notify; // Call IMycSubscriber.Notify again
|
||||
notifyResult := dirtyFlag.Notify; // Call TSignal.ISubscriber.Notify again
|
||||
|
||||
Assert.IsTrue(dirtyFlag.State.IsSet, 'Flag should remain dirty (IsSet).');
|
||||
Assert.IsFalse(notifyResult, 'Notify() should return False as flag was already dirty (no state change to dirty).');
|
||||
@@ -303,7 +303,7 @@ begin
|
||||
end;
|
||||
|
||||
// Category 3: Chaining Dirty Flags
|
||||
// In these tests, FlagX.Notify means calling the IMycSubscriber.Notify method on FlagX.
|
||||
// In these tests, FlagX.Notify means calling the TSignal.ISubscriber.Notify method on FlagX.
|
||||
// This makes FlagX dirty and potentially notifies its subscribers (like FlagY).
|
||||
|
||||
procedure TTestMycDirtyFlag.TestChain_A_Notifies_B_SimplePropagation;
|
||||
|
||||
@@ -9,14 +9,14 @@ uses
|
||||
|
||||
type
|
||||
// Helper class to mock a subscriber.
|
||||
TMockSubscriber = class(TInterfacedObject, IMycSubscriber)
|
||||
TMockSubscriber = class(TInterfacedObject, TSignal.ISubscriber)
|
||||
public
|
||||
NotifyCount: Integer;
|
||||
NotifyShouldReturn: Boolean; // Determines what this mock's Notify method returns
|
||||
WasCalled: Boolean;
|
||||
|
||||
constructor Create(AShouldReturn: Boolean = True); // Parameter name AShouldReturn for clarity
|
||||
function Notify: Boolean; // IMycSubscriber implementation.
|
||||
function Notify: Boolean; // TSignal.ISubscriber implementation.
|
||||
end;
|
||||
|
||||
[TestFixture]
|
||||
@@ -137,7 +137,7 @@ var
|
||||
returnedValueFromNotify: Boolean;
|
||||
begin
|
||||
latch := TLatch.CreateLatch(InitialCount);
|
||||
returnedValueFromNotify := latch.Notify; // This is TLatch.ILatch (as IMycSubscriber).Notify
|
||||
returnedValueFromNotify := latch.Notify; // This is TLatch.ILatch (as TSignal.ISubscriber).Notify
|
||||
|
||||
Assert.AreEqual(
|
||||
ExpectedReturn,
|
||||
@@ -282,18 +282,18 @@ var
|
||||
subHandle_B_listens_A, subHandle_Mock_listens_B: TSignal.TSubscription;
|
||||
begin
|
||||
latchA := TLatch.CreateLatch(1);
|
||||
latchB := TLatch.CreateLatch(1); // latchB is an IMycSubscriber
|
||||
latchB := TLatch.CreateLatch(1); // latchB is an TSignal.ISubscriber
|
||||
mockSubForB := TMockSubscriber.Create;
|
||||
|
||||
subHandle_Mock_listens_B := latchB.State.Subscribe(mockSubForB);
|
||||
// LatchA's state is an IMycSignal. LatchB (as IMycSubscriber) subscribes to it.
|
||||
// LatchA's state is an IMycSignal. LatchB (as TSignal.ISubscriber) subscribes to it.
|
||||
subHandle_B_listens_A := latchA.State.Subscribe(latchB);
|
||||
|
||||
Assert.IsFalse(latchA.State.IsSet, 'LatchA initially not set.');
|
||||
Assert.IsFalse(latchB.State.IsSet, 'LatchB initially not set.');
|
||||
Assert.AreEqual(0, mockSubForB.NotifyCount, 'MockSubForB initially not notified.');
|
||||
|
||||
latchA.Notify; // Call Notify on LatchA (as IMycSubscriber)
|
||||
latchA.Notify; // Call Notify on LatchA (as TSignal.ISubscriber)
|
||||
|
||||
Assert.IsTrue(latchA.State.IsSet, 'LatchA should be set after notify.');
|
||||
Assert.IsTrue(latchB.State.IsSet, 'LatchB should be set after being notified by LatchA.');
|
||||
|
||||
@@ -14,13 +14,13 @@ uses
|
||||
|
||||
type
|
||||
// Helper class to notify a latch when its Notify method is called.
|
||||
TLatchNotifierSubscriber = class(TInterfacedObject, IMycSubscriber)
|
||||
TLatchNotifierSubscriber = class(TInterfacedObject, TSignal.ISubscriber)
|
||||
private
|
||||
FGateLatch: TLatch.ILatch;
|
||||
public
|
||||
constructor Create(AGateLatch: TLatch.ILatch);
|
||||
// IMycSubscriber
|
||||
function Notify: Boolean; // Implements IMycSubscriber.Notify [cite: 46, 181, 299]
|
||||
// TSignal.ISubscriber
|
||||
function Notify: Boolean; // Implements TSignal.ISubscriber.Notify [cite: 46, 181, 299]
|
||||
end;
|
||||
|
||||
[TestFixture]
|
||||
@@ -251,7 +251,7 @@ var
|
||||
LPrerequisiteFuture1, LPrerequisiteFuture2: TFuture<Integer>.IFuture;
|
||||
LMainFuture: TFuture<string>.IFuture;
|
||||
LGateLatch: TLatch.ILatch;
|
||||
LSub1, LSub2: IMycSubscriber; // To hold the TLatchNotifierSubscriber instances
|
||||
LSub1, LSub2: TSignal.ISubscriber; // To hold the TLatchNotifierSubscriber instances
|
||||
LInitStateP1, LInitStateP2: TLatch.ILatch;
|
||||
Subscriptions: array[1..2] of TSignal.TSubscription; // To manage subscriptions
|
||||
const
|
||||
|
||||
+2
-2
@@ -108,7 +108,7 @@ procedure TMycTaskFactoryTests.TestRunDelayedJobExecutesAfterAllNotifies;
|
||||
var
|
||||
jobExecuted: Boolean;
|
||||
jobCompletedLatch: TLatch.ILatch;
|
||||
subscriber: IMycSubscriber;
|
||||
subscriber: TSignal.ISubscriber;
|
||||
begin
|
||||
jobExecuted := False;
|
||||
jobCompletedLatch := TLatch.CreateLatch(1); // Changed from Signals.CreateLatch
|
||||
@@ -124,7 +124,7 @@ begin
|
||||
|
||||
Assert.IsNotNull(subscriber, 'Run should return a subscriber for delayed job');
|
||||
// Use TMycLatch.Null for comparison
|
||||
Assert.AreNotEqual<IMycSubscriber>(TLatch.Null, subscriber, 'Subscriber should not be TMycLatch.Null for StartCount > 0');
|
||||
Assert.AreNotEqual<TSignal.ISubscriber>(TLatch.Null, subscriber, 'Subscriber should not be TMycLatch.Null for StartCount > 0');
|
||||
|
||||
subscriber.Notify;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user