如何为输出参数为零的函数重载subsref / numArgumentsFromSubscript?

时间:2016-04-19 09:26:38

标签: matlab oop indexing overloading

我想要一个包含containers.Map的课程。另外,我希望能够使用()索引来访问父类的子映射,例如:

>> map = containers.Map('Foo', 'Bar');
>> ex = Example(map);
>> ex('Foo')

ans =

Bar

此代码如下所示,效果很好。我遇到的唯一问题是在类上定义的其他方法。根据文档,我知道我需要覆盖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

1 个答案:

答案 0 :(得分:3)

进一步深入研究这个问题,你可以选择一些方法来解决这个问题。

method(obj)致电会议

您可以简单地更改调用类方法的方式。您可以简单地使用标准函数表示法,而不是使用点表示法。

sayHello(ex)

%// vs.

ex.sayHello()

这将避免在调用方法时调用subsref。而且,这实际上是当前MATLAB的OOP迭代中的类的fastest way to call a method。此外,这不需要更改当前代码。

使用nargout确定方法输出数

另一种选择是在subsrefnumArgumentsFromSubscript中添加一个特殊情况,用于查找使用点表示法调用的方法。然后,您可以使用following callnargout显式确定方法的输出参数数。

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技巧在包含在包(foo.bar.Example)中的情况下不起作用。我在上面的例子中添加了一个解决方法。诀窍是在致电import('foo.bar.Example')之前致电nargout('Example>Example.sayHello')