Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2008-07

assigning a local function to a var

14 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

David Emerson

I'd like to store an address of a local function in a variable, and call that local function, but I don't know how to define a variable of type "local function". Here's the error I'm stuck on, with sample code:

Error: Incompatible types:
got "<address of local function(AnsiString):Boolean;Register>" expected "<procedure variable type of
function(AnsiString):Boolean;Register>"

procedure test;
   function func_a (pass_str : ansistring) : boolean;
      begin
         writeln (pass_str, 'A');
         func_a := true;
      end;
   function func_b (pass_str : ansistring) : boolean;
      begin
         writeln (pass_str, 'B');
         func_b := false;
      end;
   var
      // what should this be??
      my_func : function (pass_str : ansistring) : boolean;
   begin
      my_func := @func_a;
      my_func('hello, ');
   end;

Thanks!
~David.

Lourival Mendes
to David Emerson

I'm not sure, but you could use TProcedure, some think like this:

procedure test;
   function func_a (pass_str : ansistring) : boolean;
      begin
         writeln (pass_str, 'A');
         func_a := true;
      end;
   function func_b (pass_str : ansistring) : boolean;
      begin
         writeln (pass_str, 'B');
         func_b := false;
      end;
   var
      // TProcedure
      my_func : TProcedure
      result: boolean ;
   begin
     my_func:= nil ;
     if not Assigned(my_func) then begin
        my_func:= TProcedure(@func_a);
        result:= my_func('hello, ');
   end
   else begin .....
    end;
end;

Try it and let me know...

Lourival Mendes

2008/7/9 David Emerson :

Show quoted text 40 lines
I'd like to store an address of a local function in a variable, and call that local function, but I don't know how to define a variable of type "local function". Here's the error I'm stuck on, with sample code: Error: Incompatible types: got "<address of local function(AnsiString):Boolean;Register>" expected "<procedure variable type of function(AnsiString):Boolean;Register>" procedure test; function func_a (pass_str : ansistring) : boolean; begin writeln (pass_str, 'A'); func_a := true; end; function func_b (pass_str : ansistring) : boolean; begin writeln (pass_str, 'B'); func_b := false; end; var // what should this be?? my_func : function (pass_str : ansistring) : boolean; begin my_func := @func_a; my_func('hello, '); end; Thanks! ~David. _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Lourival J. Mendes Neto
Chris Cheney
I'd like to store an address of a local function in a variable, and call that local function, but I don't know how to define a variable of type "local function". Here's the error I'm stuck on, with sample code:

"One can assign the following values to a procedural type variable:
1. Nil, for both normal procedure pointers and method pointers.
2. A variable reference of a procedural type, i.e. another variable of the same type.
3. A global procedure or function address, with matching function or procedure header and calling
convention.
4. A method address."

[reference manual
ftp://ftp.freepascal.org/pub/fpc/docs-pdf/ref.pdf
page 38]

Note "A GLOBAL procedure or function ... "

This is for a reason - think what would happen if you were able to a local procedure to a global variable!

FWIW the same is true of Delphi.

HTH

David Emerson
to David Emerson: I'd like to store an address of a local function in a variable, and call that local…

Thanks Lourival, Joao, Chris, for your responses.

It looks like what I'm attempting is not possible. FWIW, I've included a more illustrative example at the end of this message.

Thus, I would ask of the devs, has this functionality been considered? Is there some particular reason that anyone is opposed to implementing it? Or is it just difficult to implement, and/or not considered useful?

There is a part of me that wants to jump in and try to implement this in the compiler, although I suspect I would be in well over my head. Before I consider making such an attempt (or looking for someone to do it) it'd be nice to get a feeling for whether such an extension would be welcome.

I am hard-pressed to detect any drawback or conflict, although of course that's largely because I have no idea how this stuff is actually implemented.

Lourival wrote some code which included:

Show quoted text 6 lines
var my_func : TProcedure; begin my_func:= TProcedure(@func_a); my_func('hello, '); end

Lourival: while your suggestion to typecast the local function to TProcedure did compile and run, the procedure is no longer "local"! Thus the parent function's variables are no longer in scope, which was the principal reason to use a local function. I neglected to include that in my original example, so I rewrote the example below.

Chris: It seems like you're saying what I want to do is impossible. Thanks for pointing out that citation in the reference manual. It would be nice to hear a more definitive answer, though :) For now, I've made the functions external, which unfortunately requires passing many (local) parameters to them, within a loop that should go fast. :(

Chris wrote, among other things:

This is for a reason - think what would happen if you were able to assign a local procedure to a global variable!

Perhaps my subject line should have read "assigning a local function to a LOCAL var" -- that is, after all, what I'm after. ((Just because it's possible to do something stupid with a feature, doesn't mean the feature is a bad idea: I can think of all sorts of stupid/absurd things to do with pointers, which compile without hint or warning.))

Joao: creating a named type isn't a solution, as it only moves the problem of declaration into the "type" section, where the very same problem persists. Thanks for responding, though.

David Emerson wrote
I'd like to store an address of a local function in a variable, and call that local function, but I don't know how to define a variable of type "local function".

Here's a revised version that better illustrates the need:

test.pas(20,14) Error: Incompatible types:
got "<address of local procedure;Register>"
expected "<procedure variable type of procedure;Register>"

procedure test;
   var
      what_to_say : ansistring;
   procedure proc_a;
      begin
         writeln ('A says, "', what_to_say, '"');
      end;
   procedure proc_b;
      begin
         writeln ('B says, "', what_to_say, '"');
      end;
   var
      my_proc : procedure;   // need to specify "local". impossible?
   begin
      what_to_say := 'hello, world!';
      my_proc := @proc_a;   // won't compile! proc_a is local.
      my_proc();
   end;

Cheers,
~David.

Joao Morais
to David Emerson
David Emerson wrote
Joao: creating a named type isn't a solution, as it only moves the problem of declaration into the "type" section, where the very same problem persists. Thanks for responding, though.

Did you test? I did and it works.

==========
function func_a(pass_str: ansistring): boolean;
begin
   writeln(pass_str, ' A');
   func_a := true;
end;
function func_b(pass_str: ansistring): boolean;
begin
   writeln(pass_str, ' B');
   func_b := false;
end;
type
   tmy_fnc = function(pass_str: ansistring): boolean;
var
   my_fnc: tmy_fnc;
begin
   my_fnc := @func_a;
   my_fnc('hello');
end.
==========

Joao Morais

David Emerson
to Joao Morais

Hi Joao,

You're missing the point -- those functions, func_a and func_b, should be embedded within another function. The behavior I would like to see concerns local functions, not global functions. In your executable, func_a and func_b are global functions (within the unit) not local functions (embedded within another function).

As a sidenote, the type declaration is still superfluous -- when dealing with global functions, the following will work equally well:

var my_fnc: function(pass_str: ansistring): boolean;

(Although I'm not sure if there might be compiler switches that would enable/disable this functionality, possibly requiring the type definition. My fpc.cfg may have something enabled that yours doesn't.)

At any rate, global functions work fine, with or without the type definition. The reason I posted was to ask about local functions.

~David.

Joao Morais wrote 8 lines
David Emerson wrote: > Joao: creating a named type isn't a solution, as it only moves the > problem of declaration into the "type" section, where the very same > problem persists. Thanks for responding, though. Did you test? I did and it works. ==========
procedure parent_procedure;
var parent_var_available_to_local_functions;  // ;)
Show quoted text 17 lines
function func_a(pass_str: ansistring): boolean; begin writeln(pass_str, ' A'); func_a := true; end; function func_b(pass_str: ansistring): boolean; begin writeln(pass_str, ' B'); func_b := false; end; type tmy_fnc = function(pass_str: ansistring): boolean; var my_fnc: tmy_fnc;
// my_fnc : function(str:ansistring):boolean; // works equally well
Show quoted text 5 lines
begin my_fnc := @func_a; my_fnc('hello'); end.
// this is a procedure, so 'end' should be followed by a semicolon,
// not a period.
Show quoted text 7 lines
========== Joao Morais _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Joao Morais
to David Emerson
David Emerson wrote 5 lines
You're missing the point -- those functions, func_a and func_b, should be embedded within another function. The behavior I would like to see concerns local functions, not global functions. In your executable, func_a and func_b are global functions (within the unit) not local functions (embedded within another function).

Aff... indeed. Sorry. Local procedures and functions are really unsupported because of stack and local variable issues afaik. Delphi has this same limitation.

Joao Morais

Lourival Mendes
to David Emerson: Thanks Lourival, Joao, Chris, for your responses. It looks like what I'm attempting is…

Dear David,

Sorry for my question, probably I didn't get the real idea of what you need, but I didn't see why the revised version didn't work.

Actually, I would write it like:

procedure test;
  var
     what_to_say : ansistring;
  procedure proc_a;
     begin
        writeln ('A says, "', what_to_say, '"');
     end;
  procedure proc_b;
     begin
        writeln ('B says, "', what_to_say, '"');
     end;
  var
//     my_proc : procedure;   // need to specify "local". impossible?
     my_proc : TProcedure;   // Lourival
  begin
     what_to_say := 'hello, world!';
     // Lourival Begin
     my_proc:= nil;
     if not Assigned(my_proc) then begin
        my_proc:= TProcedure(@proc_a);
        my_proc ();
     end
     else .....
     // Lourival End
  end;
     Again I didn't test it, but I think that should work, shouldn't??

Lourival

2008/7/10, David Emerson :

Show quoted text 98 lines
Thanks Lourival, Joao, Chris, for your responses. It looks like what I'm attempting is not possible. FWIW, I've included a more illustrative example at the end of this message. Thus, I would ask of the devs, has this functionality been considered? Is there some particular reason that anyone is opposed to implementing it? Or is it just difficult to implement, and/or not considered useful? There is a part of me that wants to jump in and try to implement this in the compiler, although I suspect I would be in well over my head. Before I consider making such an attempt (or looking for someone to do it) it'd be nice to get a feeling for whether such an extension would be welcome. I am hard-pressed to detect any drawback or conflict, although of course that's largely because I have no idea how this stuff is actually implemented. Lourival wrote some code which included: > var > my_func : TProcedure; > begin > my_func:= TProcedure(@func_a); > my_func('hello, '); > end Lourival: while your suggestion to typecast the local function to TProcedure did compile and run, the procedure is no longer "local"! Thus the parent function's variables are no longer in scope, which was the principal reason to use a local function. I neglected to include that in my original example, so I rewrote the example below. Chris: It seems like you're saying what I want to do is impossible. Thanks for pointing out that citation in the reference manual. It would be nice to hear a more definitive answer, though :) For now, I've made the functions external, which unfortunately requires passing many (local) parameters to them, within a loop that should go fast. :( Chris wrote, among other things: > This is for a reason - think what would happen if you were able to > assign a local procedure to a global variable! Perhaps my subject line should have read "assigning a local function to a LOCAL var" -- that is, after all, what I'm after. ((Just because it's possible to do something stupid with a feature, doesn't mean the feature is a bad idea: I can think of all sorts of stupid/absurd things to do with pointers, which compile without hint or warning.)) Joao: creating a named type isn't a solution, as it only moves the problem of declaration into the "type" section, where the very same problem persists. Thanks for responding, though. On Wednesday 09 July 2008 9:31 am, David Emerson wrote: > I'd like to store an address of a local function in a variable, and > call that local function, but I don't know how to define a variable of > type "local function". Here's a revised version that better illustrates the need: test.pas(20,14) Error: Incompatible types: got "<address of local procedure;Register>" expected "<procedure variable type of procedure;Register>" procedure test; var what_to_say : ansistring; procedure proc_a; begin writeln ('A says, "', what_to_say, '"'); end; procedure proc_b; begin writeln ('B says, "', what_to_say, '"'); end; var my_proc : procedure; // need to specify "local". impossible? begin what_to_say := 'hello, world!'; my_proc := @proc_a; // won't compile! proc_a is local. my_proc(); end; Cheers, ~David. _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Lourival J. Mendes Neto
Marco van de Voort
to David Emerson: Thanks Lourival, Joao, Chris, for your responses. It looks like what I'm attempting is…
Thus, I would ask of the devs, has this functionality been considered?

Yes. A solution for this problem is needed for Mac Pascal compatability.

Is there some particular reason that anyone is opposed to implementing it?

Not really I guess.

Or is it just difficult to implement, and/or not considered useful?

Useful, but a lot of work.

There is a part of me that wants to jump in and try to implement this in the compiler, although I suspect I would be in well over my head.

Well, my ideas about it were:

- there must be a local directive that enables a procvar type declaration t to become a "full" procvar. (to switch between compatible and full). Maybe mac mode should turn this on by default.
- The new "full" procvar type consists out of a framepointer and a procedure address. A two pointer record, like tmethod.
- In case of a global procedure being assigned to the full procvar, the framepointer=nil.
- In case of a local procedure being assigned to the full procvar, the framepointer is the parents framepointer. (the implict first argument)
- Calling such localproc is of course only allowed from within the immediate parent of the local procedure, so that the frame exists.
- When a procedure calls such a procedure, on asm level the code is roughly like this:

   if assigned(procvar.frameptr) then
     procvar.procaddress(procvar.frameptr,arg1,arg2,arg3..)
   else
     procvar.procaddress(arg1,arg2,arg3..)

Of course this is just brainstorming. IIRC Jonas punched a few holes in it when I talked to him about it, but I can't remember what they were. Except a warning that the callnode code was complicated, due to the many ways a procedure/method can be called. (think directives, special rules for parameter order, hidden params, float params, small complex types etc)

Before I consider making such an attempt (or looking for someone to do it) it'd be nice to get a feeling for whether such an extension would be welcome.

It's on the roadmap for the mac dialect:
http://wiki.freepascal.org/Mode_MacPas

(Search for "planned", it is the "nested procedure parameters") item)

I am hard-pressed to detect any drawback or conflict, although of course that's largely because I have no idea how this stuff is actually implemented.

See above.

Jonas Maebe
to Marco van de Voort
Marco van de Voort wrote 5 lines
> Thus, I would ask of the devs, has this functionality been > considered? Yes. A solution for this problem is needed for Mac Pascal compatability.

No, it isn't. Mac Pascal only requires that you can declare a procedure like this:

procedure test(function f(para1: longint): longint);
begin
end;

and that you then can pass the address of either local or global functions as a parameter to this procedure. It does not require, or indeed allow, assigning the address of a local function/procedure to a procvar.

This furthermore only works for cases where the procvar type definition appears directly in the function declaration, and not in case you have a separate procvar declaration. I.e., in the following case you can only pass procvars and the addresses of global function to test:

type
   tfun = function(para1: longint): longint;
procedure test(f: tfun);
begin
end;

Afaik it's also not Mac Pascal specific, but regular Standard Pascal functionality.

Jonas

Jonas Maebe
to Lourival Mendes: Sorry for my question, probably I didn't get the real idea of what you need, but I didn't…
Lourival Mendes wrote 14 lines
var // my_proc : procedure; // need to specify "local". impossible? my_proc : TProcedure; // Lourival begin what_to_say := 'hello, world!'; // Lourival Begin my_proc:= nil; if not Assigned(my_proc) then begin my_proc:= TProcedure(@proc_a); my_proc (); end

This will compile (because you are disabling the compiler's type checking), but is guaranteed to
a) not function correctly most of the time
b) crash in various situations

Jonas

David Emerson
to Florian Klaempfl
Florian Klaempfl wrote 5 lines
David Emerson schrieb: > Or is it just difficult to implement, and/or not considered useful? The procvar handling code is one of the ugliest parts of the compiler :)

Well, I would *clearly* be in well over my head, as I expected.

Thanks to everyone for your responses. Looks like I will have to use external procs with lots of params for now.

Cheers,
~David.