Sometimes I define a function returning a value, when I may or may not
want to use the returned value. Delphi's Pascal allows me to ignore
the value returned by the function. For example, I may have a class
data member that should be temporarily overridden. If it's an integer,
I could have function tc.SetInt(i:integer):integer; which sets the new
value and returns the previous value. This is then used like so:
var
old:integer;
Begin
old := c.SetInt(x);
...
c.SetInt(old);
End;
But this doesn't seem to work in Dephi 3 when the variable is not
Integer but procedure of object. But it works fine in Delphi 1!!!
The shortest program I could put together that illustrates the problem
is:
Type
tProc = Procedure (x:integer) of object;
tc = Class
pvar : tProc;
Procedure cp(x:integer);
Constructor Create;
Function SetPVar(p:tProc):tProc;
{ Sets pvar & returns previous value
}
End;
tm = Class
Procedure mp(x:integer);
End;
Procedure tc.cp(x:integer);
Begin
Writeln('c.cp(',x,')');
End;
Constructor tc.Create;
Begin
pvar := cp;
End;
Function tc.SetPVar(p:tProc):tProc;
Begin
Result := pvar;
pvar := p;
End;
Procedure tm.mp(x:integer);
Begin
Writeln('m.mp(',x,')');
End;
Var
c : tc;
m : tm;
p : tProc;
Begin
Assign(output, paramstr(1));
Rewrite(output);
c := tc.Create;
m := tm.Create;
c.pvar(1);
p := c.SetPVar(m.mp);
{ install new handler, save old handler }
c.pvar(2);
c.SetPVar(p); { restore old handler }
c.pvar(3);
c.Free;
m.Free;
Close(Output);
end.
This will compile with DCC and returns the following values:
c.cp(1)
m.mp(2)
c.cp(3)
However, I have to assign c.SetPVar's return value, to get it to work
in DCC32, i.e. p := c.SetPVar(p); { restore old handler } - Otherwise
it will not compile. It demands more parameters. If I give it one,
i.e. c.SetPVar(p)(5); { restore old handler } then it compiles but the
output is:
c.cp(1)
m.mp(2)
m.mp(5)
c.cp(3)
In other words, the Delphi 1 compiler is correctly taking the function
result as the value of a variable specifying a procedure, but Delphi 3
considers it an invocation of the procedure.
Why would this behaviour have changed between the versions? And is
there some syntax for ignoring the function's return value?
FP
Well, in D1 an integer was 2 bytes while a procedural variable
was 4 bytes so it was harder for the compiler to get them confused (or
rather to misunderstand which one you wanted). Have you tried changing
Function tc.SetPVar(p:tProc):tProc;
Begin
Result := pvar;
pvar := p;
End;
to
Function tc.SetPVar(p:tProc):tProc;
Begin
Result := @pvar;
@pvar := p;
End;
and/or adding @'s elsewhere? Didn't read the example carefully enough
to be certain exactly where the missing @'s are at, but adding a few
@'s somewhere is going to solve it, and on a rough first pass it looks
like this is where.
(See "procedural values...setting" and "procedural values...comparing"
in Delphi3.hlp.)
--
David Ullrich
sig.txt not found
??? Dunno what you mean by saying it's not a
procedural type. It seems to do the things I'd expect
a procedural type to do, for example this:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
function FortyTwo: integer;
function TwentyFour: integer;
end;
type TP = function: integer of object;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var P: TP;
begin
@P:= @TForm1.FortyTwo;
ShowMessage(inttostr(P));
@P:= @TForm1.TwentyFour;
ShowMessage(inttostr(P));
end;
function TForm1.FortyTwo:integer;
begin
result:= 42;
end;
function TForm1.TwentyFour:integer;
begin
result:= 24;
end;
end.
does what I expected. (Seems like the problem in his code is
a few missing @'s, which were not needed in D1 because in
D1 SizeOf(integer) <> SizeOf(Pointer)...)
> It is the tMethod = record
> code,data: pointer;
> end;
> You have to save the value in the tmethod type.
> --
> Roman
> KRE...@mbox.cesnet.cz
> (please remove STOPSPAM. in header]
--
TProcOfObject = procedure (x : Integer) of object;
procedure(k : TProcOfObject; var returns : TProcOfObject);
There are some nasty cases this avoids. Of course, you can't do nice looking
functional programming.
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
Procedure ShowIt(Sender: tObject);
{ Public declarations }
end;
Procedure tForm1.ShowIt(Sender: tObject);
begin
Showmessage(caption);
end;
procedure TForm1.Button1Click(Sender: TObject);
var aP: tNotifyEvent;
begin
caption := 'Form 1';
FillChar(tMethod(aP),sizeOf(aP),#0);
@aP := @tForm1.ShowIt; // the assignement sets only the first four bytes
of aP variable
aP(Self); // no caption will be displayed
end;
I'm missing your point here. When I read your "no caption will be
displayed" I thought you meant the showmessage would not be executed at
all - in fact it is, but an empty caption is displayed.
Which is what you'd expect - the showmessage is looking for the
caption of the instance of TForm1 that invoked the method, ie the instance
of TForm1 that was passed in the method call, and there is none. If you
change what you wrote to
Procedure tForm1.ShowIt(Sender: tObject);
begin
Showmessage(TForm1(Sender).caption);
end;
then 'Form 1' gets displayed. Because now we're passing an actual
string to showmessage.
I didn't see quite what you meant by "of object is not a
procedural variable" and I still don't, quite. Of course it gives
something that doesn't work the same as if the "of object" is not
there.
Note as well that the original poster specified that his
code worked in D1. Whatever your point is it didn't change between
D1 and D3, did it? Otoh the value of SizeOf(Pointer) = SizeOf(integer)
certainly changed, hence my conjecture on how to fix his problem.
I guess I do begin to see what you're saying here, but
not how it applies to his problem - the methods he was trying to
use do not depend on the status of the instance either.
David Ullrich wrote in message <34C506...@math.okstate.edu>...
>Frank Peelo wrote:
>> [...]
>> Why would this behaviour have changed between the versions? And is
>> there some syntax for ignoring the function's return value?
>
> Well, in D1 an integer was 2 bytes while a procedural variable
>was 4 bytes so it was harder for the compiler to get them confused
(or
>rather to misunderstand which one you wanted). Have you tried
changing
> [...]
>and/or adding @'s elsewhere? Didn't read the example carefully enough
>to be certain exactly where the missing @'s are at, but adding a few
>@'s somewhere is going to solve it, and on a rough first pass it
looks
>like this is where.
Thanks, but it didn't work. It added a layer of indirection, i.e. if
pvar is a procedure @pvar is a pointer. The pointer type was
incompatible with the variable. This probably has to do with the fact
that the variables were "procedure of object" rather than just
procedure. That means a "self" pointer has to be stored somewhere.
IMHO the problem is that procedural variables are trying to look like
procedures, when they are really references, or "pointers" of some
sort. If I had to add ^ after a procedural expression to call it,
there would be no ambiguity. As it is there seems to be no way to
simply ignore the return value of this function.
>(See "procedural values...setting" and "procedural
values...comparing"
>in Delphi3.hlp.)
(oops) Thanks for the reminder! I did look at those pages but they
looked like the same stuff as I remembered from BP7 so I didn't read
them carefully enough. The "Procedural types in expressions" page
suggests that it was actually D1 that was wrong, or maybe they changed
the language definition so both are right. :-(
I got over the problem by declaring SetPVar as a procedure, which
returns the value in a Var parameter. This ensures that I can't omit
the return value and then spend ages trying to figure out why it won't
compile (again) but it doesn't feel right, somehow.
FP
David Ullrich wrote in message <34C51A...@math.okstate.edu>...
>Roman Krejci wrote:
>>
>> Hi Frank,
>> The "procedure(..) of object" is not a procedural type.
>
> ??? Dunno what you mean by saying it's not a
>procedural type. It seems to do the things I'd expect
>a procedural type to do, for example this:
>...
I thought when a message was cross-posted the replies came in all the
relevant newsgroups? AFAIR I only posted to NGs that I have
subscribed, but I missed Roman's reply, which NG was it in?
Well, it's not quite the same as a procedural type. The "of object"
implies a Self pointer, which means variables of this type have to be
larger than "plain" procedural types by at least the size of a
pointer.
>... (Seems like the problem in his code is
>a few missing @'s, which were not needed in D1 because in
>D1 SizeOf(integer) <> SizeOf(Pointer)...)
no, the function _worked_ fine. With the added '@' it doesn't compile.
It's only what happens to the return result that I have a problem
with, and it's more to do with the definition of the language. How do
you ignore the return value of the function?!
btw In D3 the size of an integer is not the same as the size of a
procedure of object, either (although what sizeof(integer) has to do
with the problem I don't understand).
FP
David Ullrich wrote in message <34C649...@math.okstate.edu>...
>
> I didn't see quite what you meant by "of object is not a
>procedural variable" and I still don't, quite.
Explained elsewhere. Might have been clearer if he had said "is not
JUST a procedural variable".
> Note as well that the original poster specified that his
>code worked in D1. Whatever your point is it didn't change between
>D1 and D3, did it? Otoh the value of SizeOf(Pointer) =
SizeOf(integer)
>certainly changed, hence my conjecture on how to fix his problem.
Sorry, I seem to be causing confusion here. My problem with the
function that set a new value of the pointer and returned the old
value was: that the D3 compiler could not call it and ignore the
return value. D1 was able to ignore the return value. I routinely used
this technique when the value being set was type integer, char,
boolean, anything... but now it failed in D3 for this procedural type.
And it took me _ages_ to figure out why, when the code had not
changed, so I was wondering if I had something wrong with the syntax.
Anyway, many thanks for replying to my post, it looks like the only
answer is that Borland have changed the language so that the return
value of such a function cannot be ignored so I use a procedure with a
Var parameter instead. Then the user of this procedure _can't_ call it
without providing a variable to put the result in!
FP
No, no, no. I finally realized what your point was last night -
you're absolutely right, I'm sorry if _I_ confused anyone.
I've been a little fuzzy on exactly what happened when I
_did_ call the method via that procedural (methodological?) variable,
in particular I really had no idea what was getting passed as the
method's Self parameter. In fact, as I eventually figured out from
your example and then verified by loooking for "TMethod" in the docs,
if P is a variable of type "procedure of object" then when I call
P the first four bytes of P are the address of the method, and the
next four bytes are the address passed as Self. (Seems like the
declaration of TMethod in classes.pas or wherever it is could be
TMethod = record
Code, Instance: Pointer;
end;
instead of "Code, Data".)
Which is exactly what you've been trying to say - sorry to be
so slow. Thanks for the persistence; I've got sort of a thick skull
but it's possible to pound stuff in if you work at it. I'm not certain
that I actually said anything false in any of this but there were
certainly things I was seriously misunderstanding.
Anyway: so if I did have a variable of type "procedure of
object" I could make a call correctly by setting the Code and Data
parts. I wonder if this is regarded as legitimate or a hack?
That's a good question. You must know that at first when I saw your
FillChar followed by that assignment I (erroneously) concluded that the
FillChar was filling a variable which was immediately discarded, so I
tried it without the FillChar. You know what happened when I did that<g>.
And thinking about why that happened is how I finally saw the point that
I was missing.
But then after I "understood" it I realized that I still
didn't see where Delphi was getting nil.caption from. Trying to trace
back through the source it seems not quite as mysterious as
it did yesterday: the GetText procedure works by Performing a few
messages, not by accessing any fields of the object directly.
And Perform does not assume that the instance actually exists,
in fact in the source for Perform you see a line "if Self <> nil
then ...". There's no problem with calling methods of nil
objects as long as they don't access any fields of the object,
and evidently Caption doesn't.
David Ullrich wrote in message <34C7A3...@math.okstate.edu>...
> Actually I don't believe that it doesn't work if you do it right.
>A self pointer _is_ stored somewhere in a "procedure of object" type
>variable - Krejki got me straightened out on exactly where.
> Regardless, these aspects of the object model didn't change
>from D1 to D3 - if "it" worked in D1 then "it" "must" work in D3
>if you get the @'s straight.
Assuming we are both talking about the same problem, "it" refers to
getting the compiler to ignore the return value of a function. In D1 I
could call the function to set the procedural variable and ignore the
return value. In D3 the compiler insists on either putting the return
value in a variable or calling it! This has nothing whatsoever to do
with putting '@' in all over the place. In fact using '@' casts a
"procedure of object" into a pointer, i.e. strips the self pointer.
Not helpful.
I redeclared the function as a procedure with a var parameter to
return the old value. This does the same thing but forces the caller
to put the return value in a variable. That will at least ensure that
the D1 code does put the result somewhere and prevent me being bitten
by this problem again.
>
>> IMHO the problem is that procedural variables are trying to look
like
>> procedures, when they are really references, or "pointers" of some
>> sort. If I had to add ^ after a procedural expression to call it,
>> there would be no ambiguity. As it is there seems to be no way to
>> simply ignore the return value of this function.
>
> You do that by adding the @, as suggested in the docs:
You do exactly the opposite by adding the @. Using the @ returns the
address of the procedure (Of course getting the address of a procedure
does not call it!) and implies that the variable _is_ a procedure.
Using ^ on a pointer gives you what the pointer points at (the
procedure) and implies that the variable the address.
My argument was the variable is not a procedure but a pointer or
reference to a procedure, and having them look like procedures is
confusing. Or better still, let's ignore my argument since it would
also apply to pchars and class handles, and wishing these were
otherwise is a waste of effort. (unless I went to C++builder where
they are still pointers... nah, I'm not that desperate.)
> Note the words "situations where the compiler can't determine
>the desired action from the context". In D1 the code you posted was
>not such a "situation", since in D1 Sizeof(integer) <>
SizeOf(pointer).
>But in D3 exactly the same code _is_ such a situation, precisely
>because SizeOf(integer) = SizeOf(Pointer).
not again! The function returns type procedure of method. This is NOT
the same as type procedure, because for procedure of method there has
to be a self pointer as well. So the return value of the function is
not the same as SizeOf(integer) in D3 either. I used the example of a
function using integers to show how I would like to call a function
and ignore the returned value.
And it really shouldn't matter! Pascal is a high-level language with
strict typing, it does NOT use the size of a variable or other object
to determine its type. The return value of the function is known to be
procedure of object not because of its size but because that is how it
was declared!
>> >(See "procedural values...setting" and "procedural
>> values...comparing"
>> >in Delphi3.hlp.)
>>
>> (oops) Thanks for the reminder! I did look at those pages but they
>> looked like the same stuff as I remembered from BP7 so I didn't
read
>> them carefully enough. The "Procedural types in expressions" page
>> suggests that it was actually D1 that was wrong, or maybe they
changed
>> the language definition so both are right. :-(
>
> I don't see anything there that looks like anything changed
>or anything there that contratdicts anything I've said. Could you
>quote exactly the bit you're referring to?
"Usually, using a procedural variable in a statement or an expression
calls the procedure or function stored in the variable."
The function returns a procedural variable (all right, procedure of
object). So in D1, calling the function and not putting the return
value somewhere should have called the procedure or function pointed
to. But it did not! So D1 was wrong. Or maybe D1 was right by its own
lights and D3 just has different ideas. It comes to the same thing -
incompatibility.
Look, I really appreciate your efforts in trying to help but we're
talking at cross purposes all the time: the problem you are addressing
is not the problem I was trying to ask about. As I explained above,
and in the message you were replying to, the function is now a
procedure which ensures that the same source code will compile in both
systems. So if I'm not completely gruntled, at least the code works -
and that will have to do for me.
Bye now
Frank