gabr42 / OmniThreadLibrary

A simple and powerful multithreading library for Delphi
http://www.omnithreadlibrary.com
Other
465 stars 140 forks source link

Fix for TOmnivalue casting to specific interface using generics #128

Closed HHasenack closed 5 years ago

HHasenack commented 5 years ago

I am using the Parallel.ForEach<T>(TEnumerator<T>)where T is a specific interface, derived from, but not equal to IInterface.

Somwhere in the code, the value is cast back to the original type in order to return it to the execute method, but fails with an ETypeCastError using the TValue.AsType because embarcadero obviously forgot to try to try casting the interface to the required one, or otherwise because the otl library internally stored the interface a IInterface rather then the actual specified interface type.

Nevertheless, I came up with a solution that works perfectly, check for my HH markings changed in otlCommon.pas I don't expect the solution to break any existing code, but I better let you be the judge of that!

function TOmniValue.CastTo<T>: T;
var
  ds      : integer;
  maxValue: uint64;
  ti      : PTypeInfo;
// HH special support for spciufic interface type
  lIntf:IInterface;
  lValue:TValue;
// HH end special support
begin
  ds := 0;
  ti := System.TypeInfo(T);
  if assigned(ti) then
    if (ti = System.TypeInfo(byte)) or (ti = System.TypeInfo(shortint)) then
      ds := 1
    else if (ti = System.TypeInfo(word)) or (ti = System.TypeInfo(smallint)) then
      ds := 2
    else
      ds := TOmniValue_DataSize[ti^.Kind];
  if ds = 0 then begin // complicated stuff
    if ti.Kind = tkRecord then
      Result := TOmniRecordWrapper<T>(CastToRecord.Value).Value
    else
      {$IFDEF OTL_ERTTI}
// HH special support for casting to a specific interface type
      if (ti.Kind = tkInterface) and Supports(AsInterface,TGUID(ti.TypeData.IntfGuid),lIntf) then
      begin
        TValue.Make(@lIntf,ti,lValue);
        Result := lValue.AsType<T>;
      end
      else
// HH end special support
      Result := AsTValue.AsType<T>
      {$ELSE}
      raise Exception.Create('Only casting to simple types is supported in Delphi 2009')
      {$ENDIF OTL_ERTTI}
  end
  else begin // simple types
    if ds < 8 then begin
      maxValue := uint64($FF) SHL ((ds-1) * 8);
      if ovData > maxValue then
        raise EOmniValueConv.CreateFmt('Value %d is too big to fit into %s', [ovData, ti^.Name]);
    end;
    Move(ovData, Result, ds);
  end;
end; { TOmniValue.CastTo }
gabr42 commented 5 years ago

Much appreciated! I'll import your fixes/improvements as soon as I find some time.