Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi XE2 generics in TRemotable classes

For reasons not really relevant to the question I want to use generics in my TRemotable classes. I've found that Soap.OPToSOAPDomConv.pas has some problems with this. It's using the old RTTI which I guess can't handle generics so the classes is not serialized into xml.

I've managed to change Soap.OPToSOAPDomConv.pas so that it works with generics. My main question is if it is considered ok to do changes in the Delphi source files? If it isn't, is there a better way to do this? As long as it's just me using it I guess there is no big problems, but it's hard to distribute the source to others, and then there's also future changes in Delphi to consider. The rest of this lenghty post is just details about what I'm actually doing :-)

I've changed this in Soap.OPToSOAPDomConv.pas (row 3759)

if SerializeProps then
begin
  { Serialized published properties }
  Count := GetTypeData(Instance.ClassInfo)^.PropCount;
  if Count > 0 then
  begin
    CheckedElemURI := False;
    GetMem(PropList, Count * SizeOf(Pointer));
    try
      GetPropInfos(Instance.ClassInfo, PropList);

To: (not the prettiest implementation I guess)

New variables in the procedure:

Context: TRttiContext;       
RttiProperty:  TRttiProperty;

Row 3759:

if SerializeProps then
begin
  { Serialized published properties }
  Count := 0;
  for RttiProperty in Context.GetType(Instance.ClassInfo).GetProperties do
  begin
    if RttiProperty.Visibility = mvPublished then //The old method only read published
      Count := Count + 1;                         //RTTI scoping [mvPublished] requires changes to
  end;                                            //soap.InvRegistry
  begin
    CheckedElemURI := False;
    GetMem(PropList, Count * SizeOf(Pointer));
    try
      I := 0;
      for RttiProperty in Context.GetType(Instance.ClassInfo).GetProperties do
        if RttiProperty.Visibility = mvPublished then
        begin
          PropList[I] := TRttiInstanceProperty(RttiProperty).PropInfo;
          I := I + 1;
        end;

Some details as to what I am doing is probably helpful. The background is that the imported wsdl from the SOAP Web Service generates a huge unit, that consists of about 2000 classes and 300k rows of code. The web service design is out of my control. The WSDL Importer makes all of these classes visible in RTTI, which consumes the RTTI space and the unit won't compile.

I've refactored the code here and there and now have a working implementation. While refactoring I found that I could cut away some 50000 rows of redundant code by using generics. Since Delphi won't compile the imported wsdl "as is" anyway I will have to maintain the unit manually whenever new methods becomes available in the web service, so I want to make it as readable as possible.

Basically I'm changing according to below. The sample has very simplified classes, and the sample actually has more lines in the refactored code, but considering the original classes has a lot of procedures etc this method really makes the unit a lot more readable, and it's also easier to compose the classes.

TLCar = class(TRemotable)
private
  FEngine: string;
  FName: string;
published
  property Name: string read FName write FName;
  property Engine: string read FEngine write FEngine;
end;

TLBicycle = class(TRemotable)
private
  FPedals: string;
  FName: string;
published
  property Name: string read FName write FName;
  property Pedals: string read FPedals write FPedals;
end;

TListCarRequest = class(TRemotable)
private
  FreturnedTags: TLCar;
published
  property returnedTags: TLCar read FreturnedTags write FreturnedTags;
end;

TListBiCycleRequest = class(TRemotable)
private
  FreturnedTags: TLBicycle;
published
  property returnedTags: TLBicycle read FreturnedTags write FreturnedTags;

To:

TCommonReturnedTags = class(TRemotable)
private
  FName: string;
published
  property Name: string read FName write FName;
end;

TLCar = class(TCommonReturnedTags)
private
  FEngine: string;
published
  property Engine: string read FEngine write FEngine;
end;

TLBicycle = class(TCommonReturnedTags)
private
  FPedals: string;
published
  property Pedals: string read FPedals write FPedals;
end;

TGenericListRequest<T: TCommonReturnedTags, constructor> = class(TRemotable)
private
  FreturnedTags: T;
published
  property returnedTags: T read FreturnedTags write FreturnedTags;
end;

TListCarRequest = class(TGenericListRequest<TLCar>)
end;

TListBiCycleRequest = class(TGenericListRequest<TLBicycle>)
end;

Kind Regards,

Dan

like image 940
dahook Avatar asked Feb 18 '26 03:02

dahook


1 Answers

There are two things to consider when making modifications such as this. First of all, can the change have impact on existing functionality. As in this case I would say that it is safe because the functionality is new to this operation, so there shouldn't be any unexpected behavior. Second part is the evolution development environment. Problem with the evolution of environment is that bindings between actions may change and that can lead to unexpected things. At this moment it is alright to assume that XE2 to has had it's share of updates. If it wouldn't, you would have to keep an eye when patching or updating. The second kind of change like the one from XE2 to XE3 can be handled better. Just put the following in top of Soap.OPToSOAPDomConv.pas:

{$IFNDEF VER230}
   {$MESSAGE ERROR 'Intended to be used with XE2'}
{$ENDIF}

When get error while compiling you might remember vaguely that there was something about that file... So in short, it is not bad things as far as try to evaluate impact and adapt to environment changes. Hopefully this was what you wanted to know.

like image 139
Waldo Avatar answered Feb 20 '26 01:02

Waldo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!