Using SmartPointer as result data type in function requires invoke call explicitly

StackOverflow https://stackoverflow.com/questions/22406474

  •  14-06-2023
  •  | 
  •  

문제

I am using the SmartPointer in http://members.adug.org.au/2011/12/05/smart-pointers/

I defined a IStringList:

type
  IStringList = ISmartPtr<TStringList>;

I may then use as follow without memory leak prompt:

var S: IStringList;
begin
  S := TSmartPtr<TStringList>.Create();
  S.Add('abc');
end;

If I use the IStringList as result data type in a function:

function GetList: IStringList;
begin
  Result := TSmartPtr<TStringList>.Create();
  Result.Add('abc');   // E2010
end;

I get a compiler error:

[dcc32 Error] Unit2.pas(31): E2010 Incompatible types: 'SmartPointer.ISmartPtr<System.Classes.TStringList>' and 'Procedure of object'

A workaround solution would be:

  Result.Invoke.Add('abc');

But that defeat the purpose of syntax cleanliness of using SmartPointer. Is there a solution?

도움이 되었습니까?

해결책

It's quite an interesting one. For whatever reason, the compiler treats Result differently from other variables. I see no good reason for that, so this feels like a compiler bug.

I see a few workarounds:

  1. Declare a local variable, use that, and finish the function by assigning that local variable to Result.
  2. Using parens to call the function: Result().Add('abc').
  3. Using a better smart pointer. For instance, a smart pointer that is not based on method invocation would be a good start. A generic record with an implicit conversion operator would presumably work.
  4. Find a version of the compiler without the bug, if indeed it is a compiler bug.
  5. Give up on smart pointers and write traditional style code. Every time I contemplate using smart pointers in Delphi I conclude that I'd rather see the explicit try/finally blocks and understand when my objects are created and destroyed.

FWIW, you can greatly simplify the issue by stripping out the smart pointer code, and removing the generic types. Here is the cleanest SSCCE that I can concoct:

{$APPTYPE CONSOLE}

type
  TFunc = reference to function: TObject;

procedure Foo;
var
  F: TFunc;
begin
  F.ClassName; // compiles
end;

function Bar: TFunc;
begin
  Result().ClassName; // compiles
  Result.ClassName; // [dcc32 Error] E2003 Undeclared identifier: 'ClassName'
end;

begin
end.

I'm now convinced that this is a compiler bug.

It is presumably related to the rather unusual language feature of Delphi that means the function call parentheses can be omitted for a function that has no parameters. This convenience sometimes brings with it ambiguity. However, in this case there is no ambiguity. The . operator has no meaning when applied to a TFunc<TObject> and so the only way to interpret these statements is that the function is called, and the . operator applied to the returned value.

Bug report: QC123218.

다른 팁

{ I'm not allowed to comment... -.-' Feel free making it one. }

I'm afraid there is no way without defeating the purpose of smart pointers. I tried to solve your problem as part of trying to find a way to assign an anonymous method to an interface variable or parameter (SO question from July 2013). Coming back to it every now and then or asking around didn't help in finding a solution.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top