Combining generics with different constraints

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.

Answers


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);

Need Your Help

Do I need to manually dispose of a java tree structure when it is no longer needed?

java memory data-structures tree

I construct lots of object trees in my applications, where each node is your typical tree node (reference to parent and a list of references to children). These trees are temporary, meaning that I ...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.