Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a native syntax to access the outer function Result variable from inner function in Delphi?

Consider:

function OuterFunc: integer;
  function InnerFunc: integer;
  begin
    // Here I'd like to access the OuterFunc.Result variable
    // for both reading and writing its value
    OuterFunc.Result := OuterFunc.Result + 12;
  end;
begin
end;

Is there a native syntax to access the OuterFunc Result variable inside InnerFunc? Or is the only way to do this to pass it like a parameter, as in the following?

function OuterFunc: integer;
  function InnerFunc(var outerResult: integer): integer;
  begin
  end;
var
  i: integer;
begin
  i := InnerFunc(Result);
end;
like image 537
Vadim Skormakhovich Avatar asked Apr 07 '17 09:04

Vadim Skormakhovich


People also ask

How do you access the outer function of variables in the inner function?

By simply changing the names of "inner" variable to y and the "_inner" variable to z you will get your expected results.

Can inner function access outer variable JavaScript?

A closure is a feature in JavaScript where an inner function has access to the outer (enclosing) function's variables — a scope chain. The closure has three scope chains: it has access to its own scope — variables defined between its curly brackets. it has access to the outer function's variables.

How do you call a function in Delphi?

You can make the call using the declared name of the routine (with or without qualifiers) or using a procedural variable that points to the routine. In either case, if the routine is declared with parameters, your call to it must pass parameters that correspond in order and type to the parameter list of the routine.


2 Answers

You can assign result to functions by assigning to the function name, which actually was the original way in Pascal:

function MyFunc: integer;
begin
  MyFunc := 2;
  // is equal to the following
  Result := 2;
end;

So in your case you can write

function OuterFunc: integer;
  function InnerFunc: integer; 
  begin
    OuterFunc := 12;
  end;
begin
end;

Beware however, that using the function name in a statement block anyware else than on the left side of the assignment operator results in a recursive call, and is therefore different from how the predefined Result works.

In other words, you can not access a previously set value of OuterFunc from within InnerFunc. You would need to use e.g. a local variable in the outer scope defined before InnerFunc to be accessible also from InnerFunc:

function OuterFunc: integer;
var
  OuterResult: integer;

  function InnerFunc: integer; 
  begin
    OuterResult := 0;
    OuterResult := OuterResult + 12;
  end;
begin
  Result := OuterResult;
end;

For more details refer to Function Declarations in the documentation.

like image 82
Tom Brunberg Avatar answered Oct 18 '22 12:10

Tom Brunberg


Another option, except for using the native Pascal syntax (as displayed by Tom Brunberg's answer), is converting the local function into a procedure.

function OuterFunc: integer;
  procedure InnerFunc(out innerResult: integer); 
  begin
    {OuterFunc's} Result := 0;
    innerReuslt := -1;
  end;
var
  i: integer;
begin
  InnerFunc( i );
end;

Since this is your INNER local function you would not break some external API/contract by this simple change.

Twice so since your original code has InnerFunc being the de facto procedure, making no use of its own Result neither by caller, nor by callee.

function OuterFunc: integer;
//  function InnerFunc: integer;
  procedure InnerFunc; 
  begin
    // here i'd like to access OuterFunc.Result variable
    // for both reading and writing its value
//    OuterFunc.Result := OuterFunc.Result + 12;
    Result := Result + 12;
  end;
begin
  InnerFunc();
end; 

But okay, let's assume you just forgot using BOTH results of BOTH functions, but you did originally intend to. Still there are few ways at your disposal to cut corners and to hack over the Delphi language intentions-limitations.

Starting with that procedure approach, you may add a function shorthand, if you want to use such a function in expressions. Though it looks a bit ugly and adds a redirection call for the CPU (you can not inline local functions and if you could Delphi inline implementation is bogged with "register dances"), so slows things down somewhat (but depending on how much you call it w.r.t. other work - that extra work might be non-noticeable).

function OuterFunc: integer;
  procedure InnerFunc(out innerResult: integer); overload;
  begin
    innerResult := +2;
 //   {OuterFunc's} Result := Result + innerResult;
    Inc( Result, innerResult );
  end;
  function InnerFunc: integer; overload;
  begin
    InnerFunc( Result ); 
  end;
var
  i: integer;
begin
//  InnerFunc( i );
  i := InnerFunc();
end;

And yet another hack is declaring the variables overlapping.

function OuterFunc: integer;
var Outer_Result: integer absolute Result;
    i: integer;
  function InnerFunc: integer; 
  begin
    Result := +2;
    Inc( Outer_Result, Result );
  end;
begin
  i := InnerFunc();
end;

Now, this approach might kill the optimizations, like placing the "result" in the CPU registers, forcing using the RAM for it, which is slower. Additionally, once you might wish to change the type of the OuterFunc and if you forget to change the type of the Outer_Result var accordingly - you screwed yourself.

function OuterFunc: double; // was - integer; Proved to be not enough since 2020
var Outer_Result: integer absolute Result;  // and here we forgot to sync type changing.... ooooops!
    i: integer;
  function InnerFunc: integer; 
....

So less hackish way to express that intention (at the price of allocating and accessing yet one more in-RAM variable) would be this:

function OuterFunc: integer;
{$T+} // we need to enable type checking: predictability is safety
var Outer_Result: ^integer;
    i: integer;
  function InnerFunc: integer; 
  begin
    Result := +2;
    Inc( Outer_Result^, Result );
  end;
begin
  Outer_Result := @Result;
  i := InnerFunc();
end;

But all these options are hack-arounds, breaking conceptual clarity, thus hampering ability for people to read/understand the program in the future. If you need the variable - then do declare the variable. That would be the most clear option here. Afterall programs are more written for the future programmers to read them than for the computers to compile them. :-)

function OuterFunc: integer;
var the_Outer_Result: integer;
  function InnerFunc; 
  begin
    Result := +2;
    Inc( the_Outer_Result, Result );
  end;
var
  i: integer;
begin
  the_Outer_Result := 0;

  .....
    I := InnerFunc();
  .....

  Result := the_Outer_Result;
end;

That way you would not fight with the language, but give up and use it as it was intended to use. Fighting and outsmarting the language is always fun, but in the long term, when you have to maintain the code any human being last read 5 years ago and port it to newer versions of Delphi/libraries/Windows - then such the non-natural smart tricks tend to become quite annoying.

like image 25
Arioch 'The Avatar answered Oct 18 '22 13:10

Arioch 'The