Page 1 of 1

Passing arguments by reference - a name dilemma

Posted: 27 May 2011 09:34
by dbenham
This site describes how to create batch functions that isolate their environment from the calling environment. But I see a problem if a variable is passed by reference (ie the name of a variable). More specifically, I see a problem if two or more variables are passed by reference and the function must define local variables.

Example pseudo code 1:

Code: Select all

:foobar varRef1 varRef2
  set "localVar=something"
  set "%~1=something else"
  set "%~2=something else again"
  {do whatever and return your result}
exit /b
This function fails if either varRef1 or varRef2 is named localVar.

Example pseudo code 2:

Code: Select all

:foobar varRef1 varRef2
  setlocal enableDelayedExpansion
  set arg1=!%~1%!
  set arg2=!%~2%!
  set "localVar=something"
  {do whatever and return your result}
exit /b
This function fails if varRef2 is named arg1.

The tricky problem with this is that functions are supposed to be black boxes. The person calling the function may not remember all of the names of the local variables, so could be unaware of potential problems.

I can't think of a perfect solution to this dilemma. The best I can come up with is to have a convention that all internal variables are prefixed with the function name followed by a dot. If all values to all reference variables are stored in properly named local variables initially, then subsequent local variables can be named anything. At least then a user of the function can have confidence any variable reference can be passed as long as the name does not start with the name of the function. If it does then the function definition should be scanned to make sure there is no name collision. But such name collision is highly unlikely, unless the call is recursive, in which case the caller better know the names of the local variables!

Anyone have any other ideas?

Dave Benham

Re: Passing arguments by reference - a name dilemma

Posted: 27 May 2011 12:44
by jeb
dbenham wrote:I can't think of a perfect solution to this dilemma.


Yes we can :wink:

Code: Select all

set "local1=one"
set "local2=two"
call :swap local1 local2
echo local1=%local1% local1=%local2%

set local1=one
set local2=two
call :swap local1 local2
echo local1=%local1% local1=%local2%
goto :eof

:swap
setlocal EnableDelayedExpansion
for /F "delims=" %%a in ("!%~1!") do (
   for /F "delims=" %%b in ("!%~2!") do (
      set "local1=%%~a"
      set "local2=%%~b"
   )
)
(   
   endlocal
   set "%~1=%local2%"
   set "%~2=%local1%"
   goto :eof
)

The trick is to "access" both parameters, but didn't write them directly to local1 and local2, but save them first "virtually" into %%a and %%b.

Ok, this isn't the perfect solution(currently you could break it), but this way could be the right one.

jeb

Re: Passing arguments by reference - a name dilemma

Posted: 27 May 2011 13:18
by dbenham
jeb wrote:The trick is to "access" both parameters, but didn't write them directly to local1 and local2, but save them first "virtually" into %%a and %%b
Oooh,I like your thinking Jeb. 8)


jeb wrote:Ok, this isn't the perfect solution(currently you could break it),
What are the pitfalls you are seeing?

Oh, never mind: Complications if values contain ! (delayed expansion) or special characters with/without quotes.

I've been working hard to encapsulate (via a macro) your solutions for universally passing any value out of a function so that they are easy to implement. I've gotten it to work a number of times, but I keep breaking it when I add new features. Once it is stable I will post it.

I'm not sure I want to worry about the input as well! I'll have to think about this. I may decide to go with my naming convention "solution" unless you have some magic bullet that easily resolves the complications.

Dave

Re: Passing arguments by reference - a name dilemma

Posted: 01 Jun 2011 16:10
by jeb
Hi Dave,

dbenham wrote:What are the pitfalls you are seeing?

Ahh, there are ... :D
- Name of the input variables could contain some special characters, but I will ignore this, as no normal programmer would name the vars like "myVar!%%a".
- The more serious problem is the <LF>, as it will fail with the FOR /F Loop, so it has to be double-replaced like in the "secure return" technic.

So the code could look like

Code: Select all

@echo off
cls
setlocal Enabledelayedexpansion
set LF=^


rem ** Two lines empty

set "local1=one!lf!two"
set "local2=three!lf!four"
call :swap local1 local2
echo local1=!local1!
echo local2=!local2!
echo(
set "local1=one!lf!two"
set "local2=three!lf!four"
call :swap local2 local1
echo local1=!local1!
echo local2=!local2!
goto :eof

:swap
setlocal DisableDelayedExpansion
setlocal EnableDelayedExpansion
set "local1=!%~1!"
for %%a in ("!LF!") do set "local1=!local1:%%~a=%%~L!"
for /F "delims=" %%a in ("!local1!") do (
   endlocal & rem this removes the "local1" variable
   setlocal EnableDelayedExpansion
   set "local1=!%~2!"
   for %%a in ("!LF!") do set "local1=!local1:%%~a=%%~L!"
   
   for /F "delims=" %%b in ("!local1!") do (
      endlocal & rem this removes the "local1" variable
      setlocal EnableDelayedExpansion
      set "local1=%%~a"
      set "local2=%%~b"
   )
)
for %%L in ("!LF!") do (
   endlocal
   endlocal
   set "%~1=%local2%"
   set "%~2=%local1%"
   goto :eof
)


dbenham wrote:I may decide to go with my naming convention "solution" unless you have some magic bullet that easily resolves the complications.


It's batch, there aren't easy solutions :)

jeb

Re: Passing arguments by reference - a name dilemma

Posted: 01 Jun 2011 17:45
by dbenham
@Jeb
Ouch! It's worse than I thought. I hadn't considered <LF> or <CR>.

Shouldn't the full blown solution implement the complete "secure return technic" :?:

That is a lot of code to handle something that a developer should be able to avoid by following simple rules. I think I will stick to my naming convention for the time being.

jeb wrote:It's batch, there aren't easy solutions

Amen to that :!: Not many anyway.

Thanks

Dave