BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
SatyaG
Calcite | Level 5

Hi,

I am trying to create a utility macro (check_params as shown below) which is when invoked from a macro - it should check all the parameters defined in that macro's definition.

%macro abc(aa=, bb=, cc=);

/* Utility macro invoked here */
   %check_params;

/* Actual code starts from here */

%mend abc;

%abc(aa=mm,bb=nn);

When I explore, I see PARMBUFF option with SYSPBUFF system automatic macro variable as a way out. But, this will only give me the parameters which are passed during macro invocation. E.g. In the above call SYSPBUFF = (aa=mm,bb=nn). This is not the full list of parameters defined in the macro definition, I would need all params i.e. (aa=mm, bb=nn, cc=)

Is there any way I get this (aa=mm, bb=nn, cc=)into a macro variable then I can play around and have checks?

Thanks in advance,

Satya

1 ACCEPTED SOLUTION

Accepted Solutions
Quentin
Super User

Thanks Tom.  Agree, that suggestion only works for the limited use case from the OP (get a list of the parameters), and could check they are non-null.  Your suggestions would allow for a more complete parameter validation framework.

The Boston Area SAS Users Group is hosting free webinars!
Next webinar will be in January 2025. Until then, check out our archives: https://www.basug.org/videos. And be sure to subscribe to our our email list.

View solution in original post

10 REPLIES 10
user24feb
Barite | Level 11

I understand you're playing around a little, but I am not sure, if this would be a proper approach. If your macro list is a tupel, this is, if you always have aa, bb, and cc combined, you could simply check, for example, if cc has a value at all with "%If (&cc^=) %Then ..", etc.

%macro abc(aa=, bb=, cc=);

%If (&aa.^=) & (&bb.^=) & (&cc.^=) %Then %Do;

  *execute something;

%End;

%Else %Do;

  %Put **something is missing**;

%End;

%mend abc;

%abc(aa=mm,bb=nn)

However, if your macro parameter list is dynamic, you might want to do the checking before you split your list up into individual elements.

RW9
Diamond | Level 26 RW9
Diamond | Level 26

You could.  There are plently of posts on parameter lists.  In most of them you will find me advising against it.  The code gets large and unreadble very quickly having to maks/unmask, reference other references etc.

IMO if you are writing macros/system for other people you will first want to read up on Software Development Life Cycle.  Sit down and plan what it is you want to achieve, how things fit together, what the standards will be, how things will be tested.  Once you have all the documentation - Functional Design Specifications, Testing Document etc. then start looking at coding.  In the companies I have worked for the first thing I notice when I get there is at least one folder full up with undocumented, untested, vague bits of code put into macros over 10 years or more.

In response to your point, there are various methods, yes parambuff is one of them.  However me personally, I would put paramters into a dataset.  The reason is that datasets are very easy to work with, do checking on etc. Its also easy to pass parameter tables through:

%macro Do_Something (Parameter_Table=work.abc);

     %Check_Parameter_Table (Parameter_Table=&Parameter_Table.);

...

You can set up your dataset any way you like with as much information as you need, and example:

PARAMETER          VALUE                    CHECK

FILE                        c:\temp\temp.xlsx     EXISTENCE

OUT_LOCATION     c:\temp                     EXISTENCE

...

Then your check is simply:

data _null_;

     set parameter_table;

     call execute('%'||strip(check)||'("'||strip(value)||'");');

run;

And this will generate the following two macro calls:

%EXISTENCE("c:\temp\temp.xlsx");

%EXISTENCE("c:\temp");

That said, that is just an example and heavily depends on what you want to do and where.  For simple macros, its quite simple to have your checks up front and then then jump to the end if fails:

%macro Test (file=);

     %if %sysfunc(fileexist(&file.))=0 %then %do;

          %put Macro Test failed to find &file. and has stopped operation;

          %goto exit;

     %end;

     /* Process file */

     ...

     %put Macro Test passed execution;

%exit:

%mend Test;

Quentin
Super User

This looks like a repeat of your earlier post:

https://communities.sas.com/thread/75519

I posted one approach there.  I think what you're looking for is reasonable.  In my check macro I usually pass the list of required parameters explicitly, because I sometimes have optional parameters with default null value.  So my macros end up looking like:

%macro abc(p1=, p2=, p3=);

  %if %MissingRequiredParameter(p1 p2) %then %goto mexit;

  %mexit:

%mend abc;

And %MissingRequiredParameter is the helper macro that checks them all for null, and %PUTs and ERROR: message to the log etc.

The Boston Area SAS Users Group is hosting free webinars!
Next webinar will be in January 2025. Until then, check out our archives: https://www.basug.org/videos. And be sure to subscribe to our our email list.
Tom
Super User Tom
Super User

You cannot query anything at runtime to find the list of parameters defined for the macro you are currently running.

I would suggest one of two approaches.

1) Have a general utility for checking parameters and enforce coding standards that macros call this utility.  If you modify the macro to have more parameters you modify the code of the macro to test them.

  %macro mymac(parm1,parm2);

     %checkparm(parm1,....)

      %checparm(parm2,.....)

     ....

   %mend mymac;

2) Maintain a database of parameter checks using the macro name as a key.  Again you will need to enforce by your coding standards that the data in the database matches the macro definition.

   %macro mymac(parm1,parm2);

      %let _macro = &sysmacroname ;

      %checkparms(&_macro);

      ...

   %mend ;

   data parameters ;

        length macroname $32 parameter $32 test $2000 ;

        infile cards dsd truncover ;

        input macroname parameter test ;

   cards;

   mymac parm1 REQUIRED

   mymac parm2 OPTIONAL

   ;;;;

Quentin
Super User

did you see my suggestion in the other thread?  Would be interested in your thoughts.

While you can't query at run time to get the parameters in the current macro, you could query to get the list of local macro variables in the current macro.  And if you do that early enough (before creating any local macro variables other than the parameters), in effect you get the parameters.

Pasting that response below, since this thread is now more-alive:

****

%macro GetOuterScopeVars(debug=0);
 
%local MyVarList;
  proc sql noprint;
    select
     distinct(name) into :MyVarList separated by
" "
     from dictionary.macros
     where scope eq
"%sysmexecname(%eval(%sysmexecdepth - 1))";
    ;
  quit;
 
%if &debug=1 %then %put Macro vars in %str( ) %sysmexecname(%eval(%sysmexecdepth - 1)) are: &MyVarList;
%mend GetOuterScopeVars;


%macro abc (aa= , bb=, cc=);
  %
GetOuterScopeVars(debug=1)

  %
*main stuff;

%mend abc;

%abc(aa=mm, bb=nn)
The Boston Area SAS Users Group is hosting free webinars!
Next webinar will be in January 2025. Until then, check out our archives: https://www.basug.org/videos. And be sure to subscribe to our our email list.
Tom
Super User Tom
Super User

That helps a little, but to properly check you would still need to have other metadata.  Is the parameter required?  What are the valid list of values?

Plus many macros will also need to validate options that are passed in via macro variables instead of parameters.

Quentin
Super User

Thanks Tom.  Agree, that suggestion only works for the limited use case from the OP (get a list of the parameters), and could check they are non-null.  Your suggestions would allow for a more complete parameter validation framework.

The Boston Area SAS Users Group is hosting free webinars!
Next webinar will be in January 2025. Until then, check out our archives: https://www.basug.org/videos. And be sure to subscribe to our our email list.
SatyaG
Calcite | Level 5

Dear All,

Thank you very much.. I am overwhelmed with the responses.. this is the first time I posted something on board here.. Great to have such supportive programmers around. Thanks a lot!

Yes, with %sysmexecname and %sysmexecdepth we can only get the list of parameters and check for non-null. However, to have further checks like if it is required/ optional and is restricted to values. So, I would have an excel sheet located centrally which has all these details for each of the macros and macro parameters and I use this as a kind of metadata. Thanks a lot.. I appreciate everyones participation in the discussion and look forward to contribute more to the forum.

Nice day all,

Satya

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Seriously, for yours and everyones sanity, don't use Excel!  Worse case scenario, save as CSV.  You can still use Excel as input, but the output is plain text file you can write a datastep read.  Even then, get a database, or metadata management software.

Ron_MacroMaven
Lapis Lazuli | Level 10

I am reminded of the famous quote from an engineer.

"Sure I can idiot-proof this design.

How big of an idiot do you want me to proof it against?"

My first response is to suggest that all your macro parameters have default values.

%macro Demo(data=sashelp.class,var=age);

The macro runs with no parameters

and the user is already aware of the expected values,

e.g. that data is a two-level name.

My next suggestion is to examine the assumption(s) that an unassigned mvar is indeed worthy of concern.

Here is a sas community org wiki page on assertions.

Assertions - sasCommunity

This page has a number of reality checks for existence of various objects, data, var, catalog, etc..

http://www.sascommunity.org/wiki/Conditionally_Executing_Global_Statements

My solution is to write my macros to do nothing gracefully.

Ron Fehd  testing-aware programs maven

Writing Testing Aware Programs - sasCommunity

... and, of course, being an introvert, I thought of a couple more comments to add * Here is a macro with the code for checking existence of various objects. http://www.sascommunity.org/wiki/Macro_Exist * from a design perspective, what you are doing by adding the parm-check macro to any other macro is changing from a subroutine which calls no other macros to complete its task to a routine which raises the level of complexity of your test suites. Message was edited by: Ronald Fehd, Friday morning May 1.

SAS Innovate 2025: Register Now

Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 10 replies
  • 5993 views
  • 8 likes
  • 6 in conversation