开发者

Retrieve TProc from Generic Container

Just discovered something rather funny:

var
  Queue  : TQueue <TProc>;
  MyProc : TProc;
...
MyProc := Queue.Dequeue;

I think you s开发者_运维技巧ee what is intendend here. However, the compiler thinks I want to store the Queue.Dequeue method (type "procedure of object") in MyProc and reports an error

E2010 Incompatible Types: 'TProc' und 'Procedure of object'

The workaround I came up with goes like this

MyProc := TProc (Pointer (Queue.Dequeue));

Is there a more elegant solution?


There's a bit of syntactical ambiguity there about whether the name "Dequeue" refers to the function itself, or the function's return value. And since you're dealing with an anonymous method pointer which you can assign a normal function to, it's trying to interpret that as a function assignment, not a function result assignment. Casting it to a pointer is the wrong solution, as that would force the function assignment to go through, which would then cause all sorts of fun errors when you attempt to invoke MyProc.

The correct way to fix it is by removing the syntactical ambiguity. Put an empty parenthesis after Dequeue, so that the compiler is sure that you're calling the function and not simply referencing it by name, and then it'll work.

MyProc := Queue.Dequeue();


As Mason said, there's an ambiguity in the Delphi syntax. Where TFoo.Bar is a method, it's not clear that FooValue.Bar means to refer to the result of calling TFoo.Bar, or a method pointer (or reference) TFoo.Bar itself (with implied Self argument of FooValue).

In the comments of Mason's answer, Rob Kennedy seems to suggest that the compiler simply figure this out based on the types of everything involved. This isn't simple; the compiler already does a lot of work to figure out whether you mean to refer to a method pointer value or a method call. It actually parses expressions in a different way when the expected receiver is a method pointer (or reference, or function pointer) type. The effort is especially involved when overloads are brought into the picture: the compiler scans through every overload candidate and checks for method pointer types in every parameter position, and then parses arguments differently depending on whether or not that parameter position contains a function pointer in one of the overloads. Then, if an overload that expects a function pointer isn't matched, the compiler changes the parse tree from function pointer to method call. The overloading mechanism itself needs to figure out which to use when its doing value to parameter comparisons. It's pretty messy, and it would be great if we didn't make it messier.

A prefix-style operator like @ or Addr() isn't much help in resolving this ambiguity, not least because functions may return function pointers, and so on; how many @ do you need to inhibit implicit (no () necessary) calling to grab the right value out? So when anonymous methods were introduced, a change in the expression parsing was made: I introduced the possibility of using () to force an invocation.

You can read more about it here:

http://blog.barrkel.com/2008/03/odd-corner-of-delphi-procedural.html

and here:

http://blog.barrkel.com/2008/03/procedurally-typed-expressions-redux.html

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜