Skip to Main Content Skip to Search
Home |   Select Country  Choose Country  |  Contact Us  |  Cart Store 
Create Account | Log In
Products & Services Industries Academia Support User Community Company

 

Newsletters - MATLAB Digest

Tips For Making a Stand-alone Application From Your MATLAB Graphical Application

by Peter Fry

Introduction

With the recent release of the MATLAB C/C++ Graphics Library, porting your MATLAB code to a completely stand-alone application becomes much easier. The C/C++ Graphics Library, when used in conjunction with the MATLAB Compiler and C/C++ Math Libraries, will take your applications directly from m-file to stand-alone application.

For many programs, the Compiler will create a fully functioning application with a simple call, such as:

mcc -B sgl lorenz.m

The command mcc invokes the Compiler, and the switch -B sgl tells the Compiler to create a stand-alone application that uses the Graphics Library. This application runs exactly as though you were running it from the MATLAB Command Window, except that some MATLAB menus are by default not present in stand-alone applications (note that as part of the demo the path of the Lorenz attractor changes from run to run). You can freely redistribute the generated executable with the C/C++ Math and Graphics Libraries.

lorenz.m running in MATLAB
lorenz running externally

The Graphics Library uses the same libraries as MATLAB, to ensure similar behavior to MATLAB. Almost every MATLAB 2-D, 3-D, and GUI primitive has a counterpart in the Graphics Library and is available for use in external applications. However, since some functionality does not yet exist in the libraries, this article will give tips on how to change small parts of your code to make your functions behave identically inside and outside of MATLAB.

Compiling an Application

For the first tip, we use a sample MATLAB GUI to demonstrate issues you might run into when compiling an application. The demonstration source code can be found in two zip files, pre.zip and post.zip. These files contain two directories containing the sample source code, pre and post. The pre directory contains the unmodified code. The code consists of an M-File, fractal_gen.m, a series of MEX-Files for each platform, mandel_core.mexext, and the C-File used to generate the MEX-File, mandel_core_mex.c. The post directory contains the source code after modifications to enable it to compile. You can run the GUI by typing fractal_gen. The GUI will look like this:

Using Scripts

The MATLAB Compiler currently does not support compiling scripts, but in most cases you can fix this by adding a function declaration to the top of the script. If the script depends on variables existing in the base workspace prior to execution, you will have to pass them in as input arguments and change the calls to your script to pass the arguments. If you need the variables' values to remain between calls, declare them to be "persistent" (See Release 11, Using MATLAB, page 10-17).

Linking MEX-Files

MEX-files are linked against MATLAB to resolve many function symbols. As MATLAB will not be running your application, you will need to recompile and link your MEX-file directly into your application. The MATLAB Compiler has a built-in facility to simplify this. For this example we have a MEX-file called mandel_core that calculates a Mandelbrot set and returns the matrix to be plotted as an image. To begin with we try:

mcc -B sgl fractal_gen.m

We receive the following warnings and the executable fails to link.

Warning: "/home/pfry/digest/post/mandel_core.mexlx" is a Mex or P-file being referenced from "/home/pfry/digest/post/fractal_gen.m".
NOTE: A link error will be produced if a call to this function is made from stand-alone code. Use -v for more information.

If we create an m-file called mandel_core.m and give it the MATLAB style syntax for how many input and output arguments we expect, we merely have to write:

function image = mandel_core(bitDepth,dim)
%#external mandel_core

Again type mcc -B sgl fractal_gen.m, you can now see that the Compiler finds mandel_core.m and recognizes the %#external macro and generates mandel_core.c, mandel_core.h, and mandel_core_external.h. The mandel_core.c and mandel_core.h files give the function bodies that wrap our MEX function for mlf and mlx style calling. These files are regenerated at each call to mcc. The only function that you will have to write, Mmandel_core, has its signature in mandel_core_external.h. You will need to write the following function (for MEX-Files it will just be a wrapper function) to call mexFunction(). You should add this function to the C-file with your mexFunction(), in this case mandel_core_mex.c.

Here is all the code for the Mmandel_core function that we need to add to mandel_core_mex.c:

(1)
(2)


(3)






(4)
#ifndef MATLAB_MEX_FILE
#define mexFunction mandel_core
void mexFunction(int nlhs, mxArray * * plhs, int nrhs, mxArray * * prhs);

mxArray * Mmandel_core(int nargout_, mxArray * bitDepth, mxArray * dim) {

mxArray * mprhs[2];
mxArray * mplhs[1];



mprhs[0] = bitDepth;
mprhs[1] = dim;
mexFunction(nargout_, mplhs, 2, mprhs);
return mplhs[0];
}
#endif

By wrapping the code that we've added in an #ifndef MATLAB_MEX_FILE (1), we ensure that the code will continue to compile as a MEX-file. The next line (2), #define mexFunction (to anything else), makes it possible to add multiple MEX-files to the same executable in this manner.

Now use the function prototype that the Compiler generated in the _external.h file to create Mmandel_core. For this function (3), there are two inputs and one output. Declare a right-hand side array of pointers (inputs) to mxArrays of length 2, and a left-hand side array of pointers (outputs) to mxArrays of length 1. We need only fill in the right-hand side arrays with the arguments and pass everything to mexFunction (4). Multiple outputs and variable argument lists on either side are a bit more involved, but follow the same idea.

Now we can compile and link our GUI with the command:

mcc -B sgl fractal_gen.m mandel_core_mex.c

Some MEX API functions do not exist in the C/C++ Math Libraries. These functions will not be callable; if your MEX-File calls them you will need to work around these on a case-by-case basis.

Using functions that are never mentioned outside of callbacks

At this point our GUI opens and most every callback functions properly. If however you attempt to change the colormap to cool, the following error occurs:

An error occurred in the callback : cool(256)
The error message caught was : Reference to unknown function cool from FEVAL in stand-alone mode.
An error occurred in the callback : colormap(cool(256));fractal_gen('changecolor',2);
The error message caught was : Sub-Expr "cool(256)" has no return value.

If you look at the source code in fractal_gen.m, the function cool is never referenced. Therefore, the Compiler doesn't know to compile it. There are two methods to fix this. The first is to mention cool.m when calling mcc.

mcc -B sgl fractal_gen.m mandel_core_mex.c cool.m

Now the Compiler will include cool.m in your application. The more permanent solution is to add the Compiler pragma %#function into the M-File that calls cool (or any other function that will be compiled). From then on, whenever you compile fractal_gen.m, cool.m will be compiled also. For this example add the line,

%#function cool

to the top of fractal_gen.m, recompile, and the menu item will now work properly.

Avoiding uncompilable callbacks

The Graphics Library does not contain the MATLAB interpreter, and instead uses a small parser that reads the string and calls feval on each token. The parser accepts string literals, scalars, vectors, and 2-D arrays, but not N-D arrays. You can also nest functions arbitrarily deeply. Because all tokens are evaluated through feval and no workspace exists, variable reference, assignments, and binary operators are not allowed in compiled callbacks.

Compilable Callbacks Uncompilable Callbacks

surf(peaks(10));
figure('color',[0 0 0]);axes;
set(findobj('type','line'),'color',rand(1,3));
pcolor(plus(rand(10),1));

a = 12;
h = gcf;
plot(myvector(:,2));
pcolor(rand(10)+1);

Using switchyard programming

With the release of MATLAB 5 a style of implementing GUIs, called switchyard programming, became the recommended design. Implementing your GUI in this fashion makes your application much more robust in accommodating variables being cleared or drastically changed from the Command Window or by other M-Files. It allows callbacks to call into a central function with one or more arguments that then dispatch to other functions based on the arguments. For example, a small switchyard appears below. It implements a GUI that plots a surface, and allows the colormap to be changed:

function mygui(arg1,arg2)
if nargin==0
arg1 = 'initialize'; end
switch arg1

case 'initialize'
local_init; case 'buttonpressed'
local_button_pressed(arg2); end function local_init
figure;
surf(peaks);
shading('interp');lighting('phong');light;colormap(hot(25));
uicontrol('Units','Normalized','Position',[.1 .1 .1 .1],'String','Cool!',...

'Callback','mygui(''buttonpressed'',cool(25))'); uicontrol('Units','Normalized','Position',[.8 .1 .1 .1],'String','Hot!',...
'Callback','mygui(''buttonpressed'',hot(25))'); function local_button_pressed(cmap)
colormap(cmap);

For another example of switchyard programming, look at fractal_gen.m; for a more thorough discussion, refer to Building GUIs with MATLAB.

Conclusions

The recent introduction of the C/C++ Graphics Library complements the existing Math Libraries and Compiler extremely well. The licensing model of the Graphics Library follows the new model for the Math Libraries: you are allowed to redistribute any application and the libraries without cost.

As the examples show, there are a few situations where MATLAB code may not compile and run on the first pass. In most situations it should be relatively easy to work around these limitations to make your MATLAB applications execute properly when either interpreted or compiled.

Contact sales
E-mail this page
Print this page
Subscribe to newsletters