How do I pass a variable number of outputs through two functions?

I am trying to return a variable number of outputs, but sometimes these outputs have to be passed through an intermediary function, and sometimes not.
I am using the MATLAB built-in Sensor Data Collection functions and constantly logging accelerometer data and GPS data on a phone (there are actually 7 sensor types, but I simplified it to 2 types for this question). This data is retrieved with unique functions. Accelerometer data (3 output arrays) is output from the "accellog" built-in function, and GPS data (7 output arrays) is output from the "poslog" built-in function, where n is the number of measurements.
I have two functions that I use to pull data from these datasets:
Function 1: "GetData" pulls all the data from a specified dataset. A string "sensorDataString" specifies the type of data that is being requested (this is needed so that functions can be generalized for different sensors).
function varargout = GetData(mobileObj, sensorDataString)
switch sensorDataString
case 'Acceleration'
[varargout{1:2}] = accellog(mobileObj);
case 'GPS'
[varargout{1:7}] = poslog(mobileObj);
end
end
Function 2: "GetSecondsOfData" returns a portion of the data set based on waiting a specified amount of time between , but relies on the "GetData" function to grab data in the first place.
% Return a set of mobile sensor measurements that span the specified number of seconds
function varargout = GetSecondsOfData(mobileObj, sensorDataString, numSeconds)
[tempdata, ~] = GetData(mobileObj, sensorDataString);
startIndx = size(tempdata,1) + 1; % Determine the starting index of measurements
pause(numSeconds) % Collect data for the specified number of seconds
varargout = GetData(mobileObj, sensorDataString);
for i = 1:size(varargout)
varargout{i} = varargout{i}(startIndx:end,:); % Pull out desired section of data
end
end
The errors occur when attempting to assign varargout from "GetData" and "GetSecondsOfData".
Is there a generalized way to pass a variable number of outputs from one function then pass these outputs through the calling function? (Or is there a better approach to this problem?)
I can use a switch-case structure to determine the number of outputs in the "GetSecondsofData" function, but this looks messy with 7 sensors. I could also output data in a different form (cell arrays) then deal them, but outputting multiple variables with varargout is the easiest to handle especially since I will sometimes use "GetData" and sometimes use "GetSecondsOfData", so I would have to assign outputs from cell arrays.
If you know the number of outputs you can use code like:
[varargout{1:7}] = poslog(mobileObj);
I'm looking for something that looks more like this, where the number of cells does not have to be specified:
varargout = GetData(mobileObj, sensorDataString);

 Accepted Answer

Currently your GetData function literally has no purpose other than to alias the function you want with other names...the mechanism below can achieve this for variable number of outputs, but in my view is more useless now that you have to specify the function name exactly anyway:
function varargout = GetData(mobileObj, sensorDataFun)
nargs = nargout(sensorDataFun);
[varargout{1:nargs}] = sensorDataFun(mobileObj);
end
As Stephen suggested, maybe GetData's purpose is to wrangle variable outputs into cell outputs:
function DataCell = GetData(mobileObj, sensorDataFun)
nargs = nargout(sensorDataFun);
[DataCell{1:nargs}] = sensorDataFun(mobileObj);
end
Then in the caller function
function DataCell = GetSecondsOfData(mobileObj, sensorDataFun, numSeconds)
tempdata = GetData(mobileObj, sensorDataString);
startIndx = size(tempdata{1},1) + 1; % Determine the starting index of measurements
pause(numSeconds) % Collect data for the specified number of seconds
DataCell = GetData(mobileObj, sensorDataFun);
for i = 1:numel(DataCell) % size isn't a good function to use here
DataCell{i} = DataCell{i}(startIndx:end,:); % Pull out desired section of data
end
end
If you really want to keep the variable number of outputs for GetSecondsOfData, but don't actually care about having GetData as a separate functionality, then
function varargout = GetSecondsOfData(mobileObj, sensorDataFun, numSeconds)
nargs = nargout(sensorDataFun);
[tempdata{1:nargs}] = sensorDataFun(mobileObj);
startIndx = size(tempdata{1},1) + 1; % Determine the starting index of measurements
pause(numSeconds) % Collect data for the specified number of seconds
[DataCell{1:nargs}] = sensorDataFun(mobileObj);
for i = 1:numel(DataCell) % size isn't a good function to use here
DataCell{i} = DataCell{i}(startIndx:end,:); % Pull out desired section of data
end
varargout = DataCell;
end

13 Comments

Just to be clear, the functions in GetData are built-in functions. The point of adding a string to the list of inputs for these functions was to allow me to specify multiple functions/sensors (via a cell array of strings) in any calling functions.
not relevant...you can still specify them using their function handles (or if you really want, strings of their actual names). And as written, you cannot send multiple sensor names anyway...you'd have to iterate...in which case the advice of not using varargout as output of GetData is more relevant. You can have function handle arrays.
The line in the caller function:
DataCell = GetData(mobileObj, sensorDataFun);
works. I didn't realize that varargout acted differently than a cell array, but I guess that makes sense.
The names/function handles were intended to be used in succession and the answers would be immediately used, so in a function I may want to calibrate one type of data to another's timestamp, so I would include two sensor names to specify the calibrating sensor's dataset and the dataset of the sensor to be calibrated. In these functions only one sensor type would ever be called at one time.
The more I look at this, the more sense it makes. Thanks for the comprehensive answer.
I ended up using this basic structure to always rely on varargout:
nargs = nargout(sensorDataFun);
[varargout{1:nargs}] = ...
Where the ellipses are replaced with any of the discussed functions. They all rely on varargout, which means the initial call has intuitive outputs regardless of which function this call relies on. I avoid that output processing line, and all I have to add in is this simple "nargs" line. I also got rid of the GetData function as suggested.
Yes, that makes sense. Glad it was helpful!
Now I'm getting an error: "Function accellog does not exist." accellog is actually a method in the mobiledev class so maybe I can't create function handle for it?
Hmm never encountered that...quick google search turned up some Answers posts that might be related...
Is the error with the nargout() function? If so, this seems to work:
nargout(@()(classname.methodname))
EDIT: above would only work for static methods with the generic classname. It should work if you can tolerate generating an anonymous handle based on an instance you want to apply the method on
nargout(@()(instance.methodname))
And so if you supply the sensorDataFun as a string again (sorry for the misstep!), you can do
funH = @()(mobileObj.(sensorDataFunStr));
nargs = nargout(funH)
[DataCell{1:nargs}] = funH();
Not sure if there is a more elegant way to achieve...
If the method is private or protected or has controlled read access, then you might not be able to take its handle from outside the class definition.
Also, even if you have a handle to a method, if the method is not a static method, then it will not resolve properly unless it is acting on an instance of the class. The error message would not be that the datatype of the function is wrong: the error message would be that the function is not found (unless there happened to be another function by the same name.)
I think in this case it should work since mobileObj is an instance, and the function handle is generated dynamically using the instance...
Yes, the problem is with nargout. I couldn't find what you posted:
nargout(@()(classname.methodname))
, but I found this code:
nargout('ClassName>ClassName.MethodName')
So I wrote:
nargs = nargout(sprintf('mobiledev>mobiledev.%s',func2str(sensorDataFunc)));
And this works! Thanks again.
To clarify, and to respond to Walter, I should not have posted
nargout(@()(classname.methodname))
which would only work for static methods.
But I think this should work:
nargout(@()(instance.method))
And, nice to know about specifying with ">" for the general method function
nargout('ClassName>ClassName.MethodName')
Hmmm....
>> nargout('string>string.gt')
Error using nargout
Not a valid MATLAB file.

Sign in to comment.

More Answers (0)

Categories

Find more on MATLAB Coder in Help Center and File Exchange

Asked:

on 15 Oct 2020

Commented:

on 15 Oct 2020

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!