# Using Legacy Code with MATLAB Coder

By Anders Sollander and Sarah Drewes, MathWorks

You plan to generate C code for a MATLAB^{®} application using MATLAB Coder™, but the application contains MATLAB functions not supported by MATLAB Coder, as well as legacy C code from a previous project. Will you still be able to generate code for the application?

Fortunately, the answer is yes. Using `tic`

and `toc`

and a key value store as examples, this article shows you how. It covers the following topics:

- Implementing functions not supported by MATLAB Coder
- Calling legacy C functions from generated code
- Importing legacy C functionality into MATLAB
- Improving the build process by using helper functions to add information such as include directories and source files

The code used in this article is available for download.

## Implementing Functions Not Supported by MATLAB Coder

We can generate code for unsupported functions by providing implementations and wrappers that can be used in MATLAB. We will illustrate this process with a `tic`

and `toc`

function.

`tic`

and `toc`

measure elapsed time by reading the time, in seconds, that elapses between starting the stopwatch timer (`tic`

) and stopping it (`toc`

).

Suppose we have a MATLAB application that involves measuring the elapsed time:

`my_alg.m`

function [B, t1] = my_alg(n) t0 = mlcutils.tic; A = rand(n,n); B = inv(A'*A); t1 = mlcutils.toc(t0); end

Since functions for measuring real time are target-dependent, there is no direct way for MATLAB Coder to generate code for `tic`

and `toc`

that will work everywhere. We therefore have to supply this code ourselves. We will create a package, `mlcutils`

, with wrapper functions, `mlcutils.tic`

and `mlcutils.toc`

, which uses our own C implementations of `tic`

and `toc`

.

`tictoc_impl.c`

#include <time.h> #include "tictoc_impl.h" double MW_tic(void) { return ((double) clock()); } double MW_toc(double start) { return (clock() - start) / CLOCKS_PER_SEC; }

Both the `tic`

and `toc`

C methods can be called from MATLAB using the `coder.ceval`

function.

`tic.m`

1 function startTime = tic 2 % mlcutils.tic This function works like the built-in tic, but 3 % should be called with the corresponding package. 4 5 if coder.target('MATLAB') 6 % This will call the built-in tic function, since we're not 7 % using the package name 8 startTime = tic; 9 else 10 srcDir = mlcutils.getSrcRoot; 11 coder.updateBuildInfo('addSourcePaths', srcDir); 12 coder.updateBuildInfo('addIncludePaths', srcDir); 13 coder.updateBuildInfo('addSourceFiles', 'tictoc_impl.c'); 14 coder.cinclude('tictoc_impl.h'); 15 16 startTime = coder.nullcopy(double(0)); 17 startTime = coder.ceval('MW_tic'); 18 end 19 end

Before generating code, we must do two things: distinguish code running in MATLAB from C code, and include build information.

## Distinguish Code Running in MATLAB from C Code

The `coder.ceval`

function only works when you are generating C code; it cannot be called when you are running the code in the MATLAB interpreter. For this reason, we use the `coder.target`

function to distinguish code running in the MATLAB interpreter from code generated by MATLAB Coder.

If the code runs in MATLAB, the standard `tic`

and `toc`

MATLAB functions are called (lines 5–8 in the above code sample). If code is generated, the C methods `MW_tic`

and `MW_toc`

are called using `coder.ceval`

(lines 9–17 in the above code sample).

Note that to prevent a recursive call in the `if`

statement, we included our own `tic`

function in the package `mlcutils`

, to distinguish it from the MATLAB built-in `tic`

function. Alternatively, we could have renamed our function or ensured that the built-in version of `tic`

is called as follows:

```
startTime = builtin('tic');
```

## Include Build Information

To enable the code generated by `coder.ceval`

to build correctly, we must add information to enable the build process to find the source and include paths of the legacy C code (lines 10–13). We must also add an include statement for the relevant header file (line 14) to ensure that the function called (line 17) is known to the compiler. Since `coder.ceval`

only sees the name of the C function called, we must ensure that the arguments and return values are of the right data type. We will then be able to cast the function’s arguments to the correct type. For the return value, we use `coder.nullcopy`

to clarify which data type is expected.

## Generating the Code

We can now use the adjusted application `my_alg.m`

for code generation with MATLAB Coder. We can do this either using the MATLAB Coder app or via the MATLAB command line.

```
codegen -config:lib my_alg -args {int32(0)}
```

The first argument (-`config:lib`

) specifies that we want to generate C library code, the second argument is the name of the function (`my_alg`

), and the last part `(-args {int32(0)})`

specifies the type of input argument. The generated code will utilize the C methods `MW_tic`

and `MW_toc`

in `tictoc_impl.c`

.

/* mlcutils.tic This function works like the built-in tic, but */ /* should be called with the corresponding package. */ t0 = MW_tic();

## Using a Function with No MATLAB Equivalent

The generated code might need to use a function that is not available in MATLAB. To show how this can be done, we’ll use a simple implementation of a key value store.

A key value store (kvs) is an associative container that stores elements formed by a combination of a key value and a mapped value. The key values are used to sort and identify the elements, while the mapped values store the content associated with this key.

Our sample application includes the following MATLAB and C++ functions:

`set`

: Insert a key value pair, where key is of type integer and value is a string.`has`

: Check if a certain key is present in the container; return 1 if it is present and 0 if not.`get`

: Retrieve the value associated with a specified key.

`kvs_impl.cpp`

#include "kvs_impl.h" #include <map> #include <string> static std::map<int, std::string> gMap; int hasEntry(int key) { return gMap.find(key) != gMap.end(); } void getEntry(int key, char *pEntry) { auto search = gMap.find(key); if (search != gMap.end()) { const char *pStr = search->second.c_str(); size_t N = strlen(pStr); strncpy(pEntry, pStr, N); pEntry[N] = 0; } else { pEntry[0] = 0; } } void setEntry(int key, const char *str) { gMap[key] = std::string(str); }

These functions are called from MATLAB using `coder.ceval`

.

`kvs.m`

function [iRet, sRet] = kvs(action, key, val) iRet = int32(0); coder.varsize('sRet',[1, 256], [0, 1]) sRet = char(zeros(1, 256)); if coder.target('MATLAB') % no implementation in MATLAB iRet = 0; sRet = '0'; else srcDir = mlcutils.getSrcRoot; coder.updateBuildInfo('addSourcePaths', srcDir); coder.updateBuildInfo('addIncludePaths', srcDir); coder.updateBuildInfo('addSourceFiles', 'kvs_impl.cpp'); coder.cinclude('kvs_impl.h'); % call the C function when not called from MATLAB switch action case 'has' iRet = coder.ceval('hasEntry', int32(key)); sRet = ''; case 'get' iRet = int32(0); coder.ceval('getEntry', int32(key), coder.ref(sRet)); sRet(sRet==0) = []; % Shorten by discarding 0 elements case 'set' sval = [val, char(0)]; coder.ceval('setEntry', int32(key), sval); iRet = int32(0); sRet = ''; end end end

It is important to specify the data types of inputs and outputs correctly. In the code segment above, the C++ function `getEntry`

has input value key of type integer and writes the associated output to the second argument `pEntry`

, a character string. Thus, the first argument is declared as an int32 scalar and the second argument is a character of length up to 256 that is declared as a reference.

## Importing Legacy C Functionality into MATLAB

In our first example, both `tic`

and `toc`

have an equivalent function in MATLAB that we access when running the application in MATLAB. As our key value store example showed, this might not always be the case. Further, instead of the simple key value store, we might want to use a sophisticated database function for which we have legacy C code but no MATLAB equivalent.

In this case, we can call the C code from MATLAB via a mex interface. We use MATLAB Coder to generate a mex function for each legacy C code method that should be called from MATLAB.

We begin by generating a mex function from `kvs.m`

.

```
>> codegen -config:mex -I .\+mlcutils -o .\+mlcutils\kvs_mex kvs ...
-args {'abc', int32(0), coder.typeof('x', [1,256], [0, 1])}
```

The argument `–I`

tells the compiler where to find the MATLAB file, and the `–o`

causes the compiler to put the resulting executable back into the package. The generated mex function `kvs_mex.mexw64`

can now be called from MATLAB in `kvs.m`

.

`kvs.m`

if coder.target('MATLAB') % call the C function via a mex interface [iRet, sRet] = mlcutils.kvs_mex(action, int32(key), val); else % Code generation, not shown right now end

The same implementation in `kvs_impl.c`

can now be used by both the generated C code and the MATLAB application.

## Using Helper Files to Manage the Build Information

As we have seen, a considerable amount of information must be added for the build process to complete. Adding this information to every file can be error-prone and cumbersome. Instead, we consolidate all `buildInfo`

updates and incorporate them into one helper file.

`updateBuildInfo.m`

function updateBuildInfo % General information srcDir = mlcutils.getSrcRoot; coder.updateBuildInfo('addSourcePaths', srcDir); coder.updateBuildInfo('addIncludePaths', srcDir); % tic/toc specific coder.updateBuildInfo('addSourceFiles', 'tictoc_imp1.c'); coder.cinclude('tictoc_imp1.h'); % kvs specific coder.updateBuildInfo('addSourceFiles', 'kvs_imp1.cpp'); coder.cinclude('kvs_imp1.h'); end

Note that we only add the source paths and include paths once. We then add each of the source files and include directives as necessary.^{1}

## Location Independence

For code that can be maintained and used in different environments, it is often necessary to dynamically determine file locations. The problem, however, is that functions like `which`

, `fileparts`

, and `fullfile`

are not supported for code generation. The solution is to create a static function `getSrcRoot`

during the installation phase.

`getSrcRoot.m`

function root = getSrcRoot root = 'C:\projects\ACME-Inc\code\src'; end

The following function creates the correct version of this static function automatically.

`genMLCUtilsRoot.m`

function getMLCUtilsRoot here = fileparts (mfilename('fullpath')); fcnName = 'getSrcRoot'; fn = fullfile(here, [fcnName, '.m']); fh = fopen(fn, 'wt'); if fh < 0 error('Couldn't open file for write.\n'); end closeAfter = onCleanup(@() fclose(fh)); fprintf(fh, 'function root = %s\n\n', fcnName); fprintf(fh, 'root = ''%s'';\n\n', fullfile(fileparts(here), 'src')); fprintf(fh, 'end\n\n'); end

If you create an install script for your library, that sets the necessary MATLAB paths, you can also call this function in your setup script to ensure that the right source directory can always be found during code generation.

## Summary

In this article, we showed how you can integrate MATLAB functions not supported by MATLAB Coder and call existing legacy functions from the generated code. We imported C-code functionality into MATLAB with minimal effort, reusing the existing C code and our MATLAB Coder configuration. Finally, we showed how the build process can be maintained in an easy and location-independent fashion when working with MATLAB Coder projects.

^{1} With large libraries, this step might unnecessarily increase the build times if the project uses only a small part of the library. In this case you may prefer to keep the build information in the respective applications.

Published 2015 - 92309v00