I have this atomic optimistic initializer class:
type
Atomic<T: IInterface> = class
type TFactory = reference to function: T;
class function Initialize(var storage: T; factory: TFactory): T;
end;
class function Atomic<T>.Initialize(var storage: T; factory: TFactory): T;
var
tmpIntf: T;
begin
if not assigned(storage) then begin
tmpIntf := factory();
if InterlockedCompareExchangePointer(PPointer(@storage)^, PPointer(@tmpIntf)^, nil) = nil then
PPointer(@tmpIntf)^ := nil;
end;
Result := storage;
end;
Now I would like to implement the same pattern for objects.
type
Atomic<T: class> = class
type TFactory = reference to function: T;
class function Initialize(var storage: T; factory: TFactory): T;
end;
class function Atomic<T>.Initialize(var storage: T; factory: TFactory): T;
var
tmpIntf: T;
begin
if not assigned(storage) then begin
tmpIntf := factory();
if InterlockedCompareExchangePointer(PPointer(@storage)^, PPointer(@tmpIntf)^, nil) = nil then
tmpIntf.Free;
end;
Result := storage;
end;
I can do those two in two separate classes, but I would really like to put both initializers under the same umbrella. IOW, I would ideally like to use this as
var
o: TObject;
i: IInterface;
Atomic<TObject>.Initialize(o, CreateObject);
Atomic<IInterface>.Initialize(i, CreateInterface);
I can't find any good solution for this. The only idea I got is to declare class as Atomic<T>
(without constraints) and then somehow (don't yet know how) check the RTTI of T in runtime and proceed accordingly.
I don't like this idea much and I'm looking for a better approach.
Multiple interface constraints can be specified. The constraining interface can also be generic.
You can specify one or more constraints on the generic type using the where clause after the generic type name. The following example demonstrates a generic class with a constraint to reference types when instantiating the generic class.
The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.
Using this, the programmer will improve the performance of an application. In addition to performance, generics provide type-safety and higher quality code because you get to reuse data processing algorithms without duplicating type-specific code. Generics are also known as Parametric polymorphism.
It seems you cannot specify constraints of the type "class or interface". Therefore the easiest solution seems to be to drop the constraint (you can enforce it at runtime, using RTTI).
For the RTTI approach, you can use the TypeInfo
function:
uses
..., TypInfo;
class function Atomic<T>.Initialize(var storage: T; factory: TFactory): T;
var
tmpT: T;
begin
if not assigned(PPointer(@storage)^) then begin
tmpT := factory();
if InterlockedCompareExchangePointer(PPointer(@storage)^, PPointer(@tmpT)^, nil) = nil then begin
case PTypeInfo(TypeInfo(T))^.Kind of
tkInterface:
PPointer(@tmpT)^ := nil;
tkClass:
TObject(tmpT).Free;
else
raise Exception.Create('Atomic<T>.Initialize: Unsupported type');
end;
end;
end;
Result := storage;
end;
One strongly-typed solution is to wrap both generic classes into another class to provide common namespace for the operation
type
Atomic = class
type
Intf<T: IInterface> = class
type TFactory = reference to function: T;
class function Initialize(var storage: T; factory: TFactory): T;
end;
Obj<T: class> = class
type TFactory = reference to function: T;
class function Initialize(var storage: T; factory: TFactory): T;
end;
end;
Usage would then be:
var
o: TObject;
i: IInterface;
Atomic.Obj<TObject>.Initialize(o, CreateObject);
Atomic.Intf<IInterface>.Initialize(i, CreateInterface);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With