如何为输出参数为零的函数重载 subsref / numArgumentsFromSubscript?
How to overload subsref / numArgumentsFromSubscript for functions which have zero output arguments?
我想要一个包含 containers.Map
的 class。此外,我希望能够使用 ()
索引从父 class 访问子地图,例如:
>> map = containers.Map('Foo', 'Bar');
>> ex = Example(map);
>> ex('Foo')
ans =
Bar
此代码如下,运行良好。我遇到的唯一问题是 class 上定义的其他方法。根据文档,我知道我需要覆盖 numArgumentsFromSubscript
(不知何故?)来帮助 nargout
。正如我在网上看到的那样,我粗略地尝试简单地使用 numel(obj)
在大多数情况下都有效,但当您调用的函数没有输出参数时(在这种情况下 numel(obj) == 1 ~= 0
)。
使用下面的示例代码,
>> ex.outGoodbye
ans =
Goodbye
太棒了!然而,
>> ex.sayHello
Error using Example/sayHello
Too many output arguments.
Error in Example/subsref (line 17)
[varargout{1:nargout}] = builtin('subsref', obj, struct);
如何解决这个问题?
classdef Example
% =====================================================================
properties
map
end
% =====================================================================
methods
% -----------------------------------------------------------------
function obj = Example(map)
obj.map = map;
end
% -----------------------------------------------------------------
function varargout = subsref(obj, struct)
if strcmp(struct(1).type, '()')
[varargout{1:nargout}] = builtin('subsref', obj.map, struct);
else
[varargout{1:nargout}] = builtin('subsref', obj, struct);
end
end
% -----------------------------------------------------------------
function n = numArgumentsFromSubscript(obj, struct, indexingContext)
n = numel(obj); % Necessary to overload subsref - for some reason
end
% -----------------------------------------------------------------
function obj = subsasgn(obj, struct, varargin)
if strcmp(struct(1).type, '()')
obj = builtin('subsasgn', obj.map, struct, varargin{:});
obj = Example(obj);
else
obj = builtin('subsasgn', obj, struct, varargin{:});
end
end
% -----------------------------------------------------------------
function sayHello(obj)
disp('Hello'); % nargout == 0. Does NOT work
end
% -----------------------------------------------------------------
function out = outGoodbye(obj)
out = 'Goodbye'; % nargout > 0. Works
end
% -----------------------------------------------------------------
end
% =====================================================================
end
所以进一步深入研究这个问题,您有几个选项可以解决这个问题。
method(obj)
调用约定
您可以简单地更改调用 class 方法的方式。您可以简单地使用标准函数表示法,而不是使用点表示法。
sayHello(ex)
%// vs.
ex.sayHello()
这将避免在调用方法时调用 subsref
。此外,这实际上是 MATLAB OOP 当前迭代中 class 的 fastest way to call a method。此外,这不需要更改您当前的代码。
使用nargout
确定方法输出的数量
另一种选择是在 subsref
或 numArgumentsFromSubscript
中添加一个特殊情况,以查找使用点表示法调用的方法。然后您可以使用 following call 到 nargout
.
显式确定方法的输出参数的数量
nArgs = nargout('classname>classname.methodname');
那么您将使用它而不是 numel(obj)
。这可以在 numArgumentsFromSubscript
或 subsref
.
中实现
numArgumentsFromSubscript
function n = numArgumentsFromSubscript(obj, struct, indexingContext)
%// Check if we are calling obj.method
if strcmp(struct(1).type, '.') && ...
ischar(struct(1).subs) && ismember(struct(1).subs, methods(obj))
%// Determine the package (if any)
cls = class(obj);
parts = regexp(cls, '\.', 'split');
%// Import the package (if any) just into the namespace of this method
if numel(parts) > 1
import(cls);
end
%// Determine number of outputs for this method
n = nargout(sprintf('%s>%s.%s', parts{[end end]}, struct(1).subs));
else
%// Default to numel(obj)
n = numel(obj);
end
end
subsref
function varargout = subsref(obj, struct)
if strcmp(struct(1).type, '()')
[varargout{1:nargout}] = builtin('subsref', obj.map, struct);
%// Check if we are calling obj.method
elseif strcmp(struct(1).type, '.') && ...
ischar(struct(1).subs) && ismember(struct(1).subs, methods(obj))
%// Determine the package (if any)
cls = class(obj);
parts = regexp(cls, '\.', 'split');
%// Import the package (if any) just into the namespace of this method
if numel(parts) > 1
import(cls);
end
%// Determine number of outputs for this method
nout = nargout(sprintf('%s>%s.%s', parts{[end end]}, struct(1).subs));
%// Call builtin subsref with this number of outputs
[varargout{1:nout}] = builtin('subsref', obj, struct);
else
[varargout{1:nargout}] = builtin('subsref', obj, struct);
end
end
总结
subsref
很痛苦,很多时候你最终会用很多逻辑来确定正在调用的是什么,而你只有大约 80% 的时间是正确的。我认为第一种方法是最直接的,可能是最高效的(您跳过 subsref
中的所有检查)并且可以更好地处理对象数组。
如果你真的想保留 obj.method
符号,我可能会建议更改 numArgumentsFromSubscript
(而不是 subsref
)以将输出参数的数量保留在一个地方。
更新
根据您的反馈,nargout
技巧在 class 包含在包中 (foo.bar.Example
) 的情况下不起作用。我在上面的示例中添加了一个解决方法。诀窍是在调用 nargout('Example>Example.sayHello')
.
之前调用 import('foo.bar.Example')
我想要一个包含 containers.Map
的 class。此外,我希望能够使用 ()
索引从父 class 访问子地图,例如:
>> map = containers.Map('Foo', 'Bar');
>> ex = Example(map);
>> ex('Foo')
ans =
Bar
此代码如下,运行良好。我遇到的唯一问题是 class 上定义的其他方法。根据文档,我知道我需要覆盖 numArgumentsFromSubscript
(不知何故?)来帮助 nargout
。正如我在网上看到的那样,我粗略地尝试简单地使用 numel(obj)
在大多数情况下都有效,但当您调用的函数没有输出参数时(在这种情况下 numel(obj) == 1 ~= 0
)。
使用下面的示例代码,
>> ex.outGoodbye
ans =
Goodbye
太棒了!然而,
>> ex.sayHello
Error using Example/sayHello
Too many output arguments.
Error in Example/subsref (line 17)
[varargout{1:nargout}] = builtin('subsref', obj, struct);
如何解决这个问题?
classdef Example
% =====================================================================
properties
map
end
% =====================================================================
methods
% -----------------------------------------------------------------
function obj = Example(map)
obj.map = map;
end
% -----------------------------------------------------------------
function varargout = subsref(obj, struct)
if strcmp(struct(1).type, '()')
[varargout{1:nargout}] = builtin('subsref', obj.map, struct);
else
[varargout{1:nargout}] = builtin('subsref', obj, struct);
end
end
% -----------------------------------------------------------------
function n = numArgumentsFromSubscript(obj, struct, indexingContext)
n = numel(obj); % Necessary to overload subsref - for some reason
end
% -----------------------------------------------------------------
function obj = subsasgn(obj, struct, varargin)
if strcmp(struct(1).type, '()')
obj = builtin('subsasgn', obj.map, struct, varargin{:});
obj = Example(obj);
else
obj = builtin('subsasgn', obj, struct, varargin{:});
end
end
% -----------------------------------------------------------------
function sayHello(obj)
disp('Hello'); % nargout == 0. Does NOT work
end
% -----------------------------------------------------------------
function out = outGoodbye(obj)
out = 'Goodbye'; % nargout > 0. Works
end
% -----------------------------------------------------------------
end
% =====================================================================
end
所以进一步深入研究这个问题,您有几个选项可以解决这个问题。
method(obj)
调用约定
您可以简单地更改调用 class 方法的方式。您可以简单地使用标准函数表示法,而不是使用点表示法。
sayHello(ex)
%// vs.
ex.sayHello()
这将避免在调用方法时调用 subsref
。此外,这实际上是 MATLAB OOP 当前迭代中 class 的 fastest way to call a method。此外,这不需要更改您当前的代码。
使用nargout
确定方法输出的数量
另一种选择是在 subsref
或 numArgumentsFromSubscript
中添加一个特殊情况,以查找使用点表示法调用的方法。然后您可以使用 following call 到 nargout
.
nArgs = nargout('classname>classname.methodname');
那么您将使用它而不是 numel(obj)
。这可以在 numArgumentsFromSubscript
或 subsref
.
numArgumentsFromSubscript
function n = numArgumentsFromSubscript(obj, struct, indexingContext)
%// Check if we are calling obj.method
if strcmp(struct(1).type, '.') && ...
ischar(struct(1).subs) && ismember(struct(1).subs, methods(obj))
%// Determine the package (if any)
cls = class(obj);
parts = regexp(cls, '\.', 'split');
%// Import the package (if any) just into the namespace of this method
if numel(parts) > 1
import(cls);
end
%// Determine number of outputs for this method
n = nargout(sprintf('%s>%s.%s', parts{[end end]}, struct(1).subs));
else
%// Default to numel(obj)
n = numel(obj);
end
end
subsref
function varargout = subsref(obj, struct)
if strcmp(struct(1).type, '()')
[varargout{1:nargout}] = builtin('subsref', obj.map, struct);
%// Check if we are calling obj.method
elseif strcmp(struct(1).type, '.') && ...
ischar(struct(1).subs) && ismember(struct(1).subs, methods(obj))
%// Determine the package (if any)
cls = class(obj);
parts = regexp(cls, '\.', 'split');
%// Import the package (if any) just into the namespace of this method
if numel(parts) > 1
import(cls);
end
%// Determine number of outputs for this method
nout = nargout(sprintf('%s>%s.%s', parts{[end end]}, struct(1).subs));
%// Call builtin subsref with this number of outputs
[varargout{1:nout}] = builtin('subsref', obj, struct);
else
[varargout{1:nargout}] = builtin('subsref', obj, struct);
end
end
总结
subsref
很痛苦,很多时候你最终会用很多逻辑来确定正在调用的是什么,而你只有大约 80% 的时间是正确的。我认为第一种方法是最直接的,可能是最高效的(您跳过 subsref
中的所有检查)并且可以更好地处理对象数组。
如果你真的想保留 obj.method
符号,我可能会建议更改 numArgumentsFromSubscript
(而不是 subsref
)以将输出参数的数量保留在一个地方。
更新
根据您的反馈,nargout
技巧在 class 包含在包中 (foo.bar.Example
) 的情况下不起作用。我在上面的示例中添加了一个解决方法。诀窍是在调用 nargout('Example>Example.sayHello')
.
import('foo.bar.Example')