4

Programming

In the last two chapters, we learnt the specification of numEclipse’s scripting language, m-script. In this chapter, we will learn how to put the script in action to write a program, function and procedure. Most of the programming goals could be achieved using m-script. Sometimes we might want to develop a function in higher level programming language to achieve better performance or to integrate with an existing set of APIs. numEclipse is developed using java so it provides seamless integration with java functions. A java program can access C functions using Java Native Interface JNI. numEclipse takes advantage of this mechanism and provides the ability to integrate C functions. In this chapter, we will also learn how to develop and deploy Java and C functions.

4.1 Program

It was previously mentioned that numEclipse could be used in both interactive and programming mode. We saw a number of examples of how the interpreter could be used interactively. We also looked at an example of m-script program in chapter 1. Unlike other programming languages, an m-script program does not have strict structure. It does not have “begin” or “end” statements or curly brackets to show the start and end of the program. The program is stored in a text file with “.m” extension, i.e., m-file. It contains a sequence of statements which you could also execute individually from the command prompt. In that sense, it is very much like a batch file. It also makes the program much simpler. A programmer will normally try different commands interactively. Once, he is sure about the sequence of commands to achieve the objectives, he could copy the commands from the “history view” to an m-file within the Source folder of the project. The program could be executed from the command prompt using the “run” command. A script program runs in the same workspace as the interpreter. It means that all the existing variables in the memory will be available to the program. Similarly, all the variables created during the execution of the program will be available in workspace memory even after the completion of program execution. This could also cause potential conflicts among variables with the same name. To avoid such a situation, we could use the “clear” command in the beginning of the program. This command removes all variables from the workspace memory. In the following, we present a simple program which generates Fibonacci numbers based on user input. Fibonacci numbers are generated based on a recurrence relation as defined in the following.

image (4.1)

Listing 4.1 (fibonacci.m)

1. n = input(‘Number of Fibonacci numbers’);

2. N = str2num(n);

3. fib = zeros(N, 1);

4. fib(1) = 1;

5. fib(2) = 1;

6. for i = 3:N

7.  fib(i) = fib(i−2) + fib(i−1);

8. end;

9. ?fib

In the above program, line 1 asks the user to provide the number of Fibonacci numbers to generate. Line 2, converts the user input from string to a number. Lines 3 and 4, initialize the first two values of Fibonacci numbers as defined in the recursive formula given above. Lines 6–8, use a “for” loop to generate the numbers. The last line, 9, displays the values of vector “fib”. Note the question mark in the last line; it is used to display the value of the variable. In the interactive mode, you do not need to use a question mark to display the value but in a program it is required.

Listing 4.2

run Fibonacci.m

The program becomes immediately available for use in any interpreter in the project. The above code shows how to invoke the program. All the programs must be saved in the “Source” folder within the project. Unlike MATLAB, these programs cannot be compiled into MEX files. Although the compiled program could speed up the execution of a program, it does not make sense to compile code for an interpreter. If you really need performance then you should consider writing the program in Java or C as described later in the chapter.

4.2 Function

The ability to create user-defined functions is available in all programming languages including m-script. Most of the programming in numEclipse involves writing functions. A “toolbox” is a collection of functions in a particular subject area. For example, an image processing toolbox will contain functions related with image processing. A lot of built-in toolbox functions within numEclipse are also implemented using m-script.

In general, a function takes zero or more arguments as input and returns one output value. Here you can go beyond this and define functions which could take a variable number of arguments and return a variable number of output values. In the following, we will show the specification and example of each possible case. Here is a simple specification of a function.

image

In a simple case, a function takes a fixed number of input arguments and returns a single output value. The following example presents a simple function.

Listing 4.2

function y = sqr(x)

y = x.^2;

You would notice that unlike other programming languages there is no need to use a “return” statement. The output variable is directly assigned the value in the function body. The “return” statement is available but it is used differently from other programming languages. It is used to stop the execution of the function or procedure. The program execution is returned to the calling program, when a “return” statement is encountered in a program. Each function call creates a separate workspace memory for the function. The actual input arguments are copied to this workspace. It means that a pass-by-value approach is used instead of pass-by- reference. The advantage is that the function could alter the input parameters within the function but it will not affect the parameter values in the calling program or function. Say, for some reason you want to make a variable available to the interpreter’s memory space, it is possible by declaring the variable as global variable within the function. For example,

global planck_constant;

planck_constant = 6.626e-34;

Like a program, a function is also created in the m-file. The file name must be same as the function name for the sake of traceability. The interpreter uses the file name as the function name. In MATLAB or Octave, you could define multiple functions or procedures in the same file; numEclipse does not allow this for the sake of simplicity. Like a program, the function file must reside under the “Source” folder, within any nested folder if required. It also becomes immediately available for use in any interpreter within the same project. Here is an example to test the function.

Listing 4.3

>> sqr(2)

ans =

4.0000

>> z = 1 + 2i;

>> sqr(z)

ans =

− 3.0000 + 4.0000i

In the above test example, note that the same function works for both a real number and complex number argument. It will even work for a matrix, as mentioned before, every number in numEclipse is treated as a matrix.

The second syntax definition in the following shows that a function can return more than one value in an array.

image

The following code shows the example of a function with multiple returned values. Note that there is no “end” or “endfunction” to close the function. Unlike statements, use of “end” is optional in functions and procedures.

Listing 4.4

function [K, T, B, C] = KTBC(n)

% Create the four special matrices assuming n > 1

K = toeplitz([2 − 1 zeros(1, n-2)]);

T = K; T(1, 1) 1;

B = K; B(1, 1) = 1; B(n, n) = 1;

C = K; C(1, n) = − 1; C(n, 1) = − 1;

The comment on the second line of the function definition is used as a help document in MATLAB and Octave. numEclipse has not implemented a help system yet. But for the sake of documentation, it is a good idea to put some comments describing the function.

The following listing shows how to call these functions.

Listing 4.5

>> x = 1:10;

>> var = variance(x)

var =

2.8723

>> [k t b c] = KTBC(2)

k =

2.0000 − 1.0000

− 1.0000 2.0000

t =

1.0000 − 1.0000

− 1.0000 2.0000

b =

1.0000 − 1.0000

− 1.0000 1.0000

c =

2.0000 − 1.0000

− 1.0000 2.0000

Most mathematical functions have zero or more input arguments and a single output argument. A user of these functions will always pass the fixed number of arguments and expect single output. Here is another example,

Listing 4.6

% min.m

function z = min(x, y)

% This function returns the minimum of x or y.

if (x < = y)

 z = x;

else

 z = y;

end;

This function accepts two input arguments, x and y, and it returns the minimum of these two arguments. The “end” keyword in above function is the end of the “if' statement rather than the function. In fact, the “end” keyword for the function is optional. You can also use “endfunction” like in octave script. One most important point, there is no mention of input arguments’ data type. This is really wonderful, this function will work for real numbers, complex numbers, row/column vectors and matrices. The relational operator “<=” automatically overload based on the type of operands.

In the following we show some examples of the function call.

Listing 4.7

>> min(2, 3)

ans =

2.0000

>> min([1 2], [3 4])

ans =

1.000 2.0000

“min” function as described above does not put any constraints on the type of the input arguments. This might be a problem in certain situations for example when somebody tries to find a minimum of a string and a matrix. In that case, you will use the “error” function as shown below.

Listing 4.8

% min.m

function z = min(x, y)

% This function returns the minimum of x or y.

if type(x) ~ = type(y)

 error(‘usage min(x, y): both x and y must be same type.’);

end;

if (x < = y)

 z = x;

else

 z = y;

end;

In this modified definition, we call an error function when the type of input arguments does not match. The “error” function returns the control back to the caller program along with its string argument. Another function, “warning”, could also be used in the situation where you want to send a string message back to the caller program without returning the control, so the function will keep executing.

The above function restricts the number of input arguments to two. Say, we want to modify it in such a way that it could accept any number of inputs and return the minimum of those input arguments. This is how we will do it.

Listing 4.9

% min.m

function z = min(varargin)

% This function returns the minimum of input arguments.

z = varargin{1};

for i = 2 : nargin

 if (varargin{i} < = z)

  z = varargin{i};

 end;

end;

Listing 4.10

>> m = min(1, 2, 3, 4, 5)

m =

1.0000

When the above function is called as shown above, all the input arguments are packed into a cell array “varargin” and an automatic variable “nargin” is created with value 5 which reflects the number of inputs. The ith argument could be accessed using the curly bracket, vararginf{i}.

Now, we want to modify this function to return more than one value. Say, we want it to return both the minimum and maximum values.

Listing 4.11

function [min max] = extreme(varargin)

% This function returns the minimum and maximum of input arguments.

min = varargin{1};

max = varargin{1};

for i = 2 : nargin

 if(varargin{i} < min)

  min = varargin{i};

 elseif (varargin{i} > max)

 max = varargin{i};

 end;

end;

This function could be called as shown below.

Listing 4.12

>> [m n] = extreme(1, 2, 3, 4, 5)

m =

1.0000

n =

5.0000

If we call the same function slightly differently, as follows.

Listing 4.13

>> [m n p] = extreme(1, 2, 3, 4, 5)

We will get an array index out of range error. This situation could be avoided by using the “nargout” as shown below.

Listing 4.14

function [min max] = extreme(varargin)

% This function returns the minimum and maximum of input arguments.

if nargout > 2

 error(‘usage [min, max] = extreme(varargin): wrong number of output arguments’)

end;

min = varargin{1};

max = varargin{1};

for i = 2 : nargin

 if (varargin{i} < min)

 min = varargin{i};

 elseif (varargin{i} > max)

 max = varargin{i};

 end;

end;

The “nargout” contains the number of output variables requested by the caller program. Checking this variable enables the function to printout a meaningful error message. Let’s give another twist to this function.

Listing 4.15

function m = extreme(s, varargin)

% This function returns the minimum and maximum of input

% arguments.

min = varargin{1};

max = varargin{1};

n = length(varargin);

for i = 2 : n

 if (varargin{i} < min)

 min = varargin{i};

 elseif (varargin{i} > max)

 max = varargin{i};

 end;

end;

if s = = ‘min’

 m = min;

elseif s = = ‘min’

 m = max;

else

 error(‘usage extreme(s, varargin): s should be either max or min.’):

end;

And here is the call to this function.

Listing 4.16

>> extreme(‘min’, 1, 2, 3, 4, 5)

ans =

1.0000

>> extreme(‘max’, 1, 2, 3, 4, 5)

ans =

5.0000

In this function, the first input argument is a string which determines if the function should return the minimum or maximum of the rest of the input arguments. This example is presented to show that “varargin” could be used with other input arguments. Similarly, “varargout” could be used with other output arguments. But, we should always use these at the end of the list of the arguments, for obvious reason. Another important point to remember is that in this situation “nargin” and “nargout” will return the total number of input and output arguments and it will not be the same as the length of these cell arrays. So if you want to loop around these arrays then it is better to use the “length” function on these cell arrays rather than the automatic variables, “nargin” and “nargout”.

A function could also be referenced using a function handle. This allows us to call the original function indirectly through its handle. The function handle is created using the “@” symbol in front of the function name.

 < function handle > = @<function name >

e.g. f = @sin

The advantage of this feature is that we can create a function of function by passing a function handle to another function. In the following function, we calculate the first difference values for the given input function.

Listing 4.17

function y = difference(f)

% This function returns the first difference vector of the input vector.

len = length(f);

y(1:len) = 0;

for i=1:len-1

 y(i) = f(i+1) – f(i);

end;

In the following, we demonstrate how two different functions are passed as input arguments to the above function. This feature enables us to write generalized mathematical functions which will work with any input function, for example, functions to integrate, differentiate, or function to solve differential equations.

Listing 4.18

>> f = @sin;

>> difference(f)

ans =

0.0678−0.7682 − 0.8979 − 0.2021 0.6795 0.9364 0.3324 − 0.5772 − 0.9561 − 0.4560

>> g = @sqrt;

>> difference(g)

ans =

0.4142 0.3178 0.2679 0.2361 0.2134

0.1963 0.1827 0.1716 0.1623 0.1543

Once all the user defined functions are developed and tested, you might want to deploy them as a toolbox for other users. As mentioned earlier, numEclipse does not support compilation of m-script functions to MEX files. But, it does have a neat mechanism of deployment. The deployment package is just a zip file containing the user-defined functions. So you compress the m-files containing the functions in a zip file and deliver to the end-users.

The end-user should take the following steps to include these functions into the interpreter.

1. Copy the zip file locally to the hard-disk.

2. Open the eclipse preferences using menu Windows → Preferences.

3. Select the numEclipse preference category and then select the item Library.

4. Click on button “New” and select the zip file.

5. You will be prompted to re-start the application. You do not have to re-start if you are adding completely new functions. If you were modifying an existing function then you must re-start.

To be on the safe side it is always recommended to re-start, if you are not sure. This completes the deployment. You are ready to try it now. Now we deleted the “extreme.m” file from the workspace and deployed the zip file. Then we tried the function again and the result is same as before.

image

Figure 4.1 Library Preference.

Listing 4.17

>> extreme(‘max’ 1, 2, 3, 4, 5)

ans =

5.0000

Say, you had two definitions of the same function. One in the workspace and other one deployed as a library. The interpreter will pick the definition from the workspace since it has higher precedence.

4.3 Procedure

A procedure is same as a function except it does not return any value. The syntax definition for a procedure is shown below.

image

Here is an example of a procedure.

Listing 4.18

procedure proc(x)

 plot(1:length(x), x);

end;

In the following, we show how to call a procedure.

Listing 4.19

>> proc((1:10).^2)

And it results in the following plot.

Similar to a function, we could also create a procedure with variable number of input arguments. We also have the automatic “nargin” and “varargin” available within a procedure. The only difference from a function is that we cannot return a variable. Although it is not good programming practice, we can still make a procedure communicate with another procedure using the global variables. A procedure also starts with the keyword “function” and it is also written in an m-file. The packaging and deployment process also remains the same.

image

Figure 4.2 Procedure output.

4.4 Java Programming

It is assumed that the reader is familiar with the java development using eclipse. To develop java functions in numEclipse, you would also need to understand some internals of this application. Before we go into those details, let us start with a simple example to demonstrate the development process.

Select eclipse menu File → New Project, it will bring up “New Project” wizard. Select “Java Project” and click on “Next” button. Type the name of project on the “New Java Project” dialog box and click on “Finish” button to create the new java project. You would need to follow similar steps to create the package “test” and a java class “MyJavaClass” within the package. In the following figure, we show a java project “MyJavaProject” containing a class “MyJavaClass” and a static method “addOne”. Note that, in the “Referenced Libraries” of project “MyJavaProject” you need to add the “library.jar” file. It is available within the numEclipse plug-in. You will not be able to compile the class without this library in the classpath. Once the class is successfully compiled, you can immediately start to use it in the interpreter. Right-click in the numEclipse project “MyFirstProject” and select “Properties” from the pop-up menu. Select “Project References” on the “Properties” dialog box and select “MyJavaProject”. This establishes the link between the two projects. Now, switch back to any interpreter and try out the function “addOne” as shown in the following.

image

Figure 4.3 MyJavaClass.

Listing 4.20

>> addOne(1);

>> ans =

2.0000

>> addOne(1+j)

>> ans =

2.0000 + i

>> addOne([1 2; 3 4])

>> ans =

2.0000 3.0000

4.0000 5.0000

Let us look at the function “addOne”, it is basically a static function. It takes an argument of type “IMatrix”. The function body contains only one line, which calls a method “plus” on the input object with the value “1.0” and returns the result of type “IMatrix”. “IMatrix” is an interface for the “Matrix” data type discussed in chapter 2. Each data type in numEclipse has a corresponding java interface, e.g., IComplex, IStruct, ICell. These interfaces are defined on the numEclipse project website (http://www.numeclipse.org/interface.html). It is important to know the methods available in each interface but there is no need to understand the actual implementation. numEclipse provides a default implementation of these data types and the pluggable architecture allows replacement of this implementation. It will be discussed later in detail in another chapter. numEclipse also provides the interface class “LinearAlgebra”. The implementation of this class provides methods to create variables of different data types. The numEclipse interpreter keeps all the variables in a look up table called “Symbol Table”. Each variable in the table is wrapped in another object called “Symbol”. This “Symbol” class provides methods to set and get the variables. In the case of an exception, a java function should throw “SemanticException”. Since it is a “RuntimeException” there is no need to use “try … catch” block. The interpreter will catch the exception and it will show the error message on the command line.

In the last example, we defined the function to accept a matrix argument and return a matrix. But when we called the function, we passed double, complex and matrix numbers and it worked for all of these data types. It is important to remember that a double or a complex variable is treated as matrix of dimension 1 × 1. So you only need to define one function for all three types. In case, you want your function to work with any other types then you should provide the overloaded definition of the function for each of those types.

The “MyJavaClass” needs some changes before it could be deployed. The modified class is shown in the following.

Listing 4.21

1. package test;

2. import org.numEclipse.toolbox.Toolbox;

3. import org.numEclipse.linalg.IMatrix;

4. public class MyJavaClass implements Toolbox {

5. private static Linear Algebra alg;

6.  public static void setAlgebra (Linear Algebra a) {

7.   alg = a;

8.  }

9.  public static IMatrix addOne (IMatrix x) {

10.   return x,plus(1.0);

11.  }

12. }

Note that, we added a static variable “alg” of type “Linear Algebra”. We also added a static method to set the value of this variable. These additions facilitate the pluggable implementation of linear algebra functions. The interpreter could change the implementation on the fly. The class now implements an interface “Toolbox”. Any java class that we want to deploy must implement this interface. In fact, there is not much to implement because the “Toolbox” interface is empty. The “function library manager” within the interpreter uses a common mechanism of java reflection to load all the functions whether they are built-in or deployed. When a class is implementing the “Toolbox” interface, the interpreter could identify the deployed functions.

Deployment of java functions is very similar to the deployment of m-script. Here, we export the java functions to ajar file using the eclipse File → Export menu. Then add the jar file to the numEclipse library using the preferences as we did in the previous section. Make sure that the numEclipse project does not depend on the java project in the workspace. Let the workbench restart and test the functions from the interpreter. In case of duplication, function definition in the workspace takes precedence over the deployed function.

Let us look at some more examples.

Listing 4.22

1. public static IMatrix min(IMatrix m1, IMatrix m2) {

2.  IMatrix m = m1.lessThan(m2);

3.  if (m.isAllPositive()) {

4.   return alg.createMatrix(m1);

5.  } else {

6.   return alg.createMatrix(m2);

7.  }

8. }

This function returns the minimum of two input arguments. In lines 2, we compare the input matrix arguments. The result of the comparison is a matrix of zeros and ones. “One” when the corresponding element is less than the element in m2 and “zero” otherwise. In line 3, we check if all elements of the resultant matrix are greater than zero and return the result accordingly. Note that we return a new instance of the input variable rather than the actual variable. This ensures that the original variables are not modified in the symbol table. This function is very similar to the function developed using m-script in the previous chapter. Let’s see how the function call works here.

Listing 4.23

>> min(2,3)

ans =

2.0000

>> min([1 2], [3 4])

ans =

1.0000 2.0000

Interestingly, the function works for both double and matrix input arguments. We can use any combination of double, complex and matrix input pair and it will still work. So there is no difference in the way it works regardless of the implementation language. In the following we show how to raise an error or exception.

Listing 4.24

1. public static IMatix min(IMatrix m1, IMatrix m2) {

2. int rows1 = m1.getRows();

3. int cols1 = m1.getCols();

4. int rows2 = m2.getRows();

5. int Cols2 = m2.getCols();

6. if (rows1 ! = rows2 || cols1 ! = cols2)

7.  throw new SemanticException(“input argument size must be the same.”);

8.   IMatrix m = m1.lessThan(m2);

9.   if (m.isAllPositive()) {

10.    return alg.createMatrix(m1);

11.  } else {

12.    return alg.createMatrix(m2);

13.  }

14. }

In this modified function, we compare the size of input matrices and throw “SemanticException” in case of a mismatch. Here is an example to call this function,

Listing 4.25

>> min ([1 2], 3)

input argument size must be the same.

Note that the error message shown on the command line is same as the string argument passed to the “SemanticException” in the function. It is always a good idea to use a meaningful message. Now, we will modify this function in such a way that it will accept a variable number of input arguments. Here is a code listing,

Listing 4.26

1. public static IMatrix min (List list) {

2.  IMatrix result = ((Symbol)list.get(0)).getMatrix();

3.  for (int i=1; i<list.size(); i++) {

4.  IMatrix m2 = ((Symbol) list.get(i)).getMatrix();

5.  IMatrix m = m2.lessThan(result);

6.  If (m.isAllPositive()) {

7.   result = m2;

8. }

9. }

10. return alg.createMatrix(result);

11. }

In this example, we used a List instead of IMatrix. Basically, when we call this function from the interpreter or any other function or program, the interpreter packs all the input arguments in a list. We extract the contents of the list to compute the result. Note that, we do not return an object passed to the function. We always create a new object out of the resultant matrix object. This ensures the integrity of the variables in the workspace.

We will change this function further to return multiple output parameters. Here is the code listing,

Listing 4.27

1. public static List extreme(List list) {

2. List result = new ArrayList();

3. IMatrix min = ((Symbol) list.get(0)).getMatrix();

4. IMatrix max = ((Symbol) list.get(0)).getMatrix();

5. for (int i = 1; i<list.size(); i++) {

6.  IMatrix m2 = ((Symbol)list.get(i)).getMatrix();

7.  IMatrix m = m2.less Than(min);

8.  IMatrix n = m2.greater Than(max);

9.  if (m.isAllPositive()) {

10.   min = m2

11.  } else if (n.isAllPositive()) {

12.   max = m2;

13.  }

14. }

15. result.add (alg.createMatrix(min));

16. result.add (alg.createMatrix(max));

17. return result;

18. }

This method could take variable number of arguments and it could return more than one output parameters. The input and output parameters are packed into lists by the interpreter as mentioned in the previous example. Here is an example to call this method.

Listing 4.28

>> extreme(1, 2, 3, 4, 5)

ans =

1.0000

>> [m n] = extreme(1, 2, 3)

m =

1.0000

n =

3.0000

The result is same as we saw in the last chapter. Essentially, you could achieve the same functionality either you write the function in m-script or java. Of course, the java functions will be faster than the m-script.

4.5 C Programming

This section is intended to show you how to write C functions for numEclipse. It is more difficult of the last two approaches but luckily eclipse comes with some tools which make the development a rather smooth process. You would still need a good understanding of C and Java Native Interface (JNI). The intent here is not to teach you C or JNI but rather the development and deployment process. It is assumed that the user is familiar with C and JNI development under eclipse.

The very first step is to write a java class similar to the one developed in the previous section. Except the fact, that we are using native method over here. Here is an example,

Listing 4.29

1. import org.numEclipse.toolbox.Toolbox;

2. import org.numEclipse.linalg. *;

3. public class NativeToolbox implements Toolbox {

4.  private static Linear Algerbra alg;

5.  public native double sqr(double x);

6.  public native String msg(String s);

7.  public static void setAlgebra(Linear Algebra a) {

8.   alg = a;

9.  }

10.  public static double nativeSquare(double x) {

11.   System.loadLibrary(“NativeToolbox”)

12.   NativeToolbox nat = new NativeToolbox();

13.   return nat.sqr(x)

14.  }

15.  public static String nativeMesg(String s) {

16.   System.loadLibrary(“NativeToolbox”);

17.   NativeToolbox nat = new NativeToolbox();

18.   return nat.msg(s);

19.   }

20.   }

Similar to the previous section, this class implements the interface “Toolbox”, declares an attribute “alg” of type “LinearAlgebra” and defines a setter method for this variable. Here we declare two native functions on lines 5 and 6. numEclipse cannot make a direct call to native functions so we define wrapper functions in the class with the names starting with keyword “native”. This naming convention is used by the interpreter to determine the nature of the function. The notion of wrapper function is developed with the view that some conversion will be required for the arguments before and after calling a native method. We previously mentioned that a double or complex argument in a function call is converted to a matrix. In this case, when you name the function starting with native, it will not perform this conversion to matrix. This design decision is made with the view that a lot of time a developer will be more interested in integrating existing C libraries rather than writing new ones. In that case, it makes more sense to keep the function arguments as it is, because existing libraries are unlikely to be using the matrix object defined in numEclipse.

As a next step, we generate the C header file “NativeToolbox.h” using “javah” utility on the last java class. Here is the generated code.

Listing 4.30

/* DO NOT EDIT THIS FILE – it is machine generated */

#include < jni.h >

/* Header for class NativeToolbox*/

#ifndef_Included_NativeToolbox

#define_Included_NativeToolbox

#ifdef_cplusplus

extern “C” {

#endif

/*

* Class: NativeToolbox

* Method: sqr

* Signature: (D)D*/

JNIEXPORT jdouble JNICALL Java_NativeToolbox_sqr(JNIEnv *, jobject,jdouble);

/*

* Class: NativeToolbox

* Method: msg

* Signature: (Ljava/lang/String;)Ljava/lang/String;

*/

JNIEXPORT jstring JNICALL Java NativeToolbox msg(JNIEnv *, jobject, jstring);

#ifdef_cplusplus

}

#endif

#endif

The C header file is not supposed to be modified as it says in the comments within the file. Next, we write a “.def” file as follows.

Listing 4.31

EXPORTS

Java_NativeToolbox_sqr

Java_NativeToolbox_msg

It basically lists the name of the functions exposed for export. Then, we write the C program “NativeToolbox.c” implementing the C functions in the header file.

Listing 4.32

//File: NativeToolbox.c

#include < jni.h >

#include “NativeToolbox.h”

JNIEXPORT jdouble JNICALL Java_NativeToolbox_sqr

 (JNIEnv *env, jobject obj, jdouble d) {

  return d*d;

}

JNIEXPORT jstring JNICALL Java_NativeToolbox_msg

 (JNIEnv *env, jobject obj, jstring s) {

  return s;

}

Finally, we write the following makefile to generate the “dll” file.

Listing 4.33

all : NativeToolbox.dll

NativeToolbox.all : NativeToolbox.o NativeToolbox.def

gcc -shared -o NativeToolbox.dll NativeToolbox.o NativeToolbox.def

NativeToolbox.o : NativeToolbox.c NativeToolbox.h

gcc –I“C:\Program Files\Java\jdk1.5.0_09\include” –I“C:\Program

Files\Java\jdk1.5.0_09\include\win32” -c NativeToolbox.c -o NativeToolbox.o

NativeToolbox.h : NativeToolbox.class

C:Program FilesJavajdk1.5.0_09injavah -jni NativeToolbox

clean:

-del NativeToolbox.h

-del NativeToolbox.o

Now, the code is ready to be deployed. We will follow similar steps to those used in the previous section. We will export the java class to a “jar” file and add the “jar” and “dll” files both to the library preference of numEclipse. You are now ready to test the native functions, as follows.

Listing 4.34

>> nativeSquare(10)

ans =

100.0000

>> nativeMesg(‘Hello’)

ans =

Hello

So this completes the chapter. Here we learnt how to develop and deploy a library of functions in java and C.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset