Richard A. DeVenezia
2003-11-24 14:30:46 UTC
Sometimes a macro has to return a status value to the caller so the caller
can make a decision. Typically the status value is some sort of error code,
other times, the value is one of several that get set by the macro.
Consider this macro
%macro attrc (data=, lib=, mem=, rc=);
%local dsid;
%let dsid = %sysfunc (open (&data));
%if &dsid %then %do;
%let &lib = %sysfunc (attrc(&dsid,LIB));
%let &mem = %sysfunc (attrc(&dsid,MEM));
%let &rc = 0;
%end;
%else %do;
%let &rc = -1;
%end;
%mend;
options nosource;
%let libname=;
%let memname=;
%let rc=;
%let xrc=;
%attrc (data=sashelp.class, lib=libname, mem=memname, rc=rc);
%put libname=&libname;
%put memname=&memname;
%put rc=&rc;
%attrc (data=foo.bar, lib=libname, mem=memname, rc=rc);
%put libname=&libname;
%put memname=&memname;
%put rc=&rc;
%attrc (data=sashelp.class, lib=libname, mem=memname, rc=xrc);
%put libname=&libname;
%put memname=&memname;
%put xrc=&xrc;
%attrc (data=foo.bar, lib=libname, mem=memname, rc=xrc);
%put libname=&libname;
%put memname=&memname;
%put xrc=&xrc;
The invocations have the log showing
libname=SASHELP
memname=CLASS
rc=
libname=SASHELP
memname=CLASS
rc=
libname=SASHELP
memname=CLASS
xrc=0
libname=SASHELP
memname=CLASS
xrc=-1
One first blush I would want to say
%if &rc eq 0 %then <ok to proceed with libname and memname>
So, why did the first two invocations show rc = blank ?
Because the macro variable specified to contain the return value is also the
name of a macro variable local in scope to the macro invoked.
Thus I posit:
What is a good strategy to prevent variable name collision in macros that
are supposed to set macro variables in scope above themselves ?
Worst case I would want the macro called to generate an ERROR message to the
log if it is request to place a value in a macro variable that will be local
when the macro is running.
Is there a programmatic way to determine which macro variables are currently
defined at local scope (without going to SASHELP.VMACRO)?
Perhaps something like
%if %isLocal ( &rc ) %then %do;
%put ERROR: &rc can not be used to return a value;
%return;
%end;
...
%let &rc = < some return code value >;
or
%if &rc %in ( &_LOCALS_ ) %then %do;
%put ERROR: &rc can not be used to return a value;
%return;
%end;
where macro variable _LOCALS_ is a reserved automatic macro variable listing
all the macro variables having local scope. (Probably not feasible;
consider a macro utilizing a macro 'array' having thousands of elements)
can make a decision. Typically the status value is some sort of error code,
other times, the value is one of several that get set by the macro.
Consider this macro
%macro attrc (data=, lib=, mem=, rc=);
%local dsid;
%let dsid = %sysfunc (open (&data));
%if &dsid %then %do;
%let &lib = %sysfunc (attrc(&dsid,LIB));
%let &mem = %sysfunc (attrc(&dsid,MEM));
%let &rc = 0;
%end;
%else %do;
%let &rc = -1;
%end;
%mend;
options nosource;
%let libname=;
%let memname=;
%let rc=;
%let xrc=;
%attrc (data=sashelp.class, lib=libname, mem=memname, rc=rc);
%put libname=&libname;
%put memname=&memname;
%put rc=&rc;
%attrc (data=foo.bar, lib=libname, mem=memname, rc=rc);
%put libname=&libname;
%put memname=&memname;
%put rc=&rc;
%attrc (data=sashelp.class, lib=libname, mem=memname, rc=xrc);
%put libname=&libname;
%put memname=&memname;
%put xrc=&xrc;
%attrc (data=foo.bar, lib=libname, mem=memname, rc=xrc);
%put libname=&libname;
%put memname=&memname;
%put xrc=&xrc;
The invocations have the log showing
libname=SASHELP
memname=CLASS
rc=
libname=SASHELP
memname=CLASS
rc=
libname=SASHELP
memname=CLASS
xrc=0
libname=SASHELP
memname=CLASS
xrc=-1
One first blush I would want to say
%if &rc eq 0 %then <ok to proceed with libname and memname>
So, why did the first two invocations show rc = blank ?
Because the macro variable specified to contain the return value is also the
name of a macro variable local in scope to the macro invoked.
Thus I posit:
What is a good strategy to prevent variable name collision in macros that
are supposed to set macro variables in scope above themselves ?
Worst case I would want the macro called to generate an ERROR message to the
log if it is request to place a value in a macro variable that will be local
when the macro is running.
Is there a programmatic way to determine which macro variables are currently
defined at local scope (without going to SASHELP.VMACRO)?
Perhaps something like
%if %isLocal ( &rc ) %then %do;
%put ERROR: &rc can not be used to return a value;
%return;
%end;
...
%let &rc = < some return code value >;
or
%if &rc %in ( &_LOCALS_ ) %then %do;
%put ERROR: &rc can not be used to return a value;
%return;
%end;
where macro variable _LOCALS_ is a reserved automatic macro variable listing
all the macro variables having local scope. (Probably not feasible;
consider a macro utilizing a macro 'array' having thousands of elements)
--
Richard A. DeVenezia
Richard A. DeVenezia