Solutions for Matlab Simulink and RTW
Wednesday, 20 August 2008
 
  Home arrow Tutorials arrow Objects & Nested Functions  
Home Consulting Tutorials Downloads Matlab Snippets Code Candy Contact Links
Objects & Nested Functions Print E-mail
Written by Administrator   
Thursday, 15 December 2005

For my unit test tool I wrote a bit of magic that allows you to do object oriented like things with nested functions. Fairly soon after I discovered nested functions I realized the dual between objects and NF's. An object is really just a set of nested functions over a common scope.

In matlab we can do this manually by assigning function handles of nested functions to struct fields and returning those from a function. However it would be nice to do this automatically. When I wrote MUnit I wanted to use the OO nested function technique and I wanted the structure populated automatically. For a full idea of the code download and refer to the original code in MUnit at

http://xtargets.com/cms/Tutorials/Matlab-Programming/MUnit-Matlab-Unit-Testing.html

munit_testcase is the constructor for tests and it is always called in the following way.

function test = test_template

    % Subclass the testcase object
    test = munit_testcase;

    % Create a structure of constraint objects for assertions
    c = munit_constraint;

    function setup
        % This function will be called at the
        % start of *every* test_ function
    end

    function teardown
        % This function will be called at the
        % end of *every* test_ function
    end

    % These are examples tests. Create new tests by creating
    % new functions whose name starts with test_
    %
    % The three tests below show the three different styles you
    % can use to create assertions. Refer to the documentation
    % for more details.

    function test_0
        x = 10;
        y = 20;
        % Simple assert using logical expression
        test.assert( x == y );
    end

    function test_1
        x = 10;
        y = 20;
        % Assert using anonymous functions ( recommended )
        test.assert( @() x == y );
    end

    function test_2
        x = 10;
        y = 20;
        % Assert using constraint object
        test.assert( c.eq( x , y ) );
    end
end

 

The interesting code in munit_testcase that automatically populates the return struct with function handles is

<snip>

    % -----------------------------------------------
    % Get named actions from parent
    % -----------------------------------------------
    stk = dbstack('-completenames'
);
    mname = stk(2).file;
    fcn_names = scan(mname, {'setup' ,'teardown', test_[A-Za-z0-9_]*'});
   
    fcns = evalin('caller',['@(){' sprintf('@%s ', fcn_names{:}) '}']);
    fcns = fcns();
    for i = 1:length(fcns);fcn = fcns{i};
        test_case.(fcn_names{i}) = fcn;
    end

</snip>

The function scan returns all functions names in the calling file matching the regular expressions in the list. The evalin line is a real mess but essentially tries to create function handles in the calling workspace. If you wonder why I have wrapped the function handles in an anonyomous function evaluation then so do I. It just works. Without that extra layer it all fails miserably. Ask Mathworks!!

The scan function is below. It is a bit fancy cause it tries to intelligently cache the information so as not to rescan the files every time. xtargets_hash is a hash_table I wrote for matlab. The source is in the unit test code and I won't post it here. You can download it.  It has some mex C code.

% SCAN
%
% Scans and extracts function names from an m file
% using regular expression patterns. The function
% maintains a cache of these scans to speed up
% operations the second time around.
%
% Arguments
%   file        -   full path to an m file to scan
%   patterns    -   A cell array of regular expressions for function names
%                   to extract
%
function fcns = scan(file, patterns)
    persistent class_cache;
    if isempty(class_cache)
        % Create a new cache
        class_cache = xtargets_hash;;
    else
        % Check the cache to see if the
        % file has allready been parsed.
        entry = class_cache.get(file);
        if ~isempty(entry) && iscurrent(entry)
            fcns = entry.fcns;
            return;
        end
    end

    % ISCURRENT
    %
    % Checks the entry from the cache table to see if it is
    % current or not. Checks the datestamps on the files
    function out = iscurrent(entry)
       jfh = java.io.File(entry.file);
       out = jfh.lastModified == entry.date;
    end

    % Scan the file for all functions matching the pattern
    fid = fopen(file);
    fcns = {};
    while 1
        tline = fgetl(fid);
        if ~ischar(tline),
            break
        end
        for i = 1:length(patterns); pat = patterns{i};
            if regexp(tline, sprintf('^\\s*function\\s*%s', pat), 'once')
                tok = regexp(tline, sprintf('^\\s*function\\s*(%s)', pat), 'tokens');
                fcns{end+1}=tok{1}{1};
            end
        end
    end
    fclose(fid);


    % Store the new cache entry
    jfh = java.io.File(file);
    entry.date = jfh.lastModified;
    entry.fcns = fcns;
    entry.file = file;
    class_cache.put(file, entry );
end

As I said the above code is specific to MUnit but I have successfully used it in a modified form in other projects where I wanted to grab function handles all matching a certain pattern from the current workspace.

Good luck Brad.

Last Updated ( Thursday, 15 December 2005 )
Next >
Sponsored by

Sole Central

Your one stop shop for Birkenstock shoes and sandles.