Tl; Dr: Is there a way to have unmatched optional name-value arguments in Matlab's new (2019b) function argument validation?
I have existing Matlab code that uses an input parser to dispatch optional arguments to my sim.
The sim is set up such that you choose a few functions that dictate how your object behaves in its environment, and you call runSim
. In this imaginary example, my sim is being defined with two functions that take both unique and common optional inputs.
runSim('sharedArg', sa, 'func1Arg1', f1a1, 'func1Arg1', f1a2, 'func2Arg1', f2a1)
runSim
will call the appropriate functions that you chose to define your sim, and pass those optional arguments along to each function. In this case, the input parser in func1
will ignore func2arg1
and the input parser in func2
will ignore func1Arg1
and func1Arg2
.
Everything works great, but my sim spends a third of its time doing input parsing, as these functions are called thousands of times in a loop. And Matlab's input parser has a documented history of being slow as (insert profanity here).
Having recently updated to 2020a, I have discovered function argument validation, which is a lot less terrible than input parsers. Having tested it out on a few of much functions, not only is the code much more readable, but I'm seeing a huge boost in performance.
function output = func1FirstTry(options)
% This function does some things
arguments
options.sharedArg
options.func1Arg1 double = 1
options.func1Arg2 double = 2
end
output = 2;
end
Love it. Great. Awesome. BUT...
... the new function validation does not allow for unmatched arguments (or at least the linked page doesn't explain it, and I haven't found any more thorough documentation). Previously, func1
only had to know about its optional arguments. If I specified func2arg1
as an input to func1
, it would just ignore it. With the new function validation, this will throw an error, as I have not defined func2Arg1
as an allowed input in the arguments block. So when I did this test, I had to instead do the following:
function output = func1SecondTry(options)
% This function does some things
arguments
options.sharedArg
options.func1Arg1 double = 1
options.func1Arg2 double = 2
options.func2Arg1 double = Nan
end
% Rest of the code goes here
end
Now this works, but I also have to change func2
to accept func1
's optional arguments. And I have like 20+ more functions with optional arguments to consider, so obviously this strategy is not going to work out. Is there a way I can specify that I want to accept and ignore any unnamed optional inputs in the same manner that input parser would? Specifically, I would like func1FirstTry('func2Arg1', 3)
to not error, without adding func2Arg1
to the arguments block.
This ended up not being the solution that I took, but I think a possible answer is to define a dummy class with all the possible inputs to the parser as public properties, then use the values from class properties syntax. So all of the properties of that class are legal inputs to the functions, but then you would only access the parameters specific to that function. This syntax also allows you to redefine any specific parameters if you want to change the default values.
% Define this in its own file
classdef OptionalArgumentClass
properties
sharedArg
func1Arg1
func1Arg2
func2Arg1
argumentUndefinedByOtherFunctions
end
end
% In a separate file from class
function output = func1(options)
% This function does some things
arguments
options.?Path.To.OptionalArgumentClass
options.func1Arg1 = 1 % Can choose specific default values here if needed
end
% options.func1Arg2 has no default value, so this will error if unspecified
output = options.func1Arg1 + options.func1Arg2;
end
The inclusion of options.?Path.To.OptionalArgumentClass
means that I can specify func2Arg1
or argumentUndefinedByOtherFunctions
and have them be gracefully ignored, which was all that I was looking for.