Muthiah Annamalai
11th July 2005
int gcd(int x,int y)
to be available for use in Octave, you will have to write some code to do this
DEFUN_DLD(gcd,args, ,"gcd(x,y) calculates the GCD of two numbers") { int x,y; int rval; if (args.length() < 2) { cout<<"see : help gcd"; return octave_value(); } x=args(0).int_value(); y=args(1).int_value(); rval=gcd(x,y) return octave_value(rval); }
After you compile this code, and make it a shared module, that is dynamically loadable from Octave, you have this function exported into Octave.
You will see that, the process of language bindings works like this
/* function prototype */ int gcd(int x,int y) 1 Take a function definition. 2 Do type checking or sanity checks. 3 Convert args from host language [octave] to the target language [C]. 4 Invoke the function in the target language/execution domain [C]. 5 Convert results from target language/execution domain to 6 host language/execution domain [Octave] 7 Return result/value to host language/execution.
which is exactly performed in each line of code, shown above.
1 DEFUN_DLD(gcd,args, ,"gcd(x,y) calculates the GCD of two numbers") 2 { 3 int x,y; 4 int rval; 5 6 /* sanity checks */ 7 if (args.length() < 2) 8 { 9 cout<<"see : help gcd"; 10 return octave_value(); 11 } 12 /* convert arguments from octave->c */ 13 x=args(0).int_value(); 14 y=args(1).int_value(); 15 /* invoke function */ 16 rval=gcd(x,y) 17 /* convert arguments from c->octave */ 18 /* return arguments to octave */ 19 return octave_value(rval); 20 }
For complex type definitions and function declarations you need to export, you can have a look at a function like this
int* modulate_fm(int *sample, int sample_len,float carrier_freq);
Now what if host language [Octave] cannot handle pointer types? what do you do now?
Well the solution is to export a function of a different interface to the host language [Octave] and add special concern to the step 3,
Convert args from host language [octave] to the target language [C].
Your host language interface might be invokable like this
modulate_fm([1,2,3,4,5] , 107.1);
which is native to this host language, rather than doing something alien like
array_obj=create_array([1,2,3,4,5]) modulate_fm(array_obj,5,107.1)
now modulate_fm will have a native look in the host language only in the former invokable model. So lets start by making the supposed transformation withing the wrapper code.
1 DEFUN_DLD(modulate_fm,args, , "modulate_fm(signal,carrier_freq) calculates the FM of signal") 2 { 3 int *signal; 4 int *output; 5 int length=0; 6 octave_value val; 7 float carrier_freq=0.0; 8 9 /* sanity checks */ 10 if (args.length() < 2) 11 { 12 cout<<"see : help modulate_fm"; 13 return octave_value(); 14 } 15 /* convert arguments from octave->c */ 16 /* convert col matrix into a signal array */ 17 val=args(0).matrix_value(); 18 signal=(int *)malloc(sizeof(int)*val.cols()); 19 for(int i=0;i<val.cols();i++) 20 signal[i]=val.elem(0,i); 21 /* length param */ 22 length=val.cols(); 23 carrier_freq=args(1).double_value(); 24 /* invoke function */ 25 output=modulate_fm(signal,length,carrier_freq); 26 27 /* cleanup memory*/ 28 free(signal); 29 /* convert arguments from c->octave */ 30 /* convert the memory array to a Octave array */ 31 octave_matrix rval(0,length); 32 for(i=0,signal=output;signal!=NULL;signal++,i++) 33 rval(0,i)=*signal; 34 35 /* return arguments to octave */ 36 return rval; 37 }
For example in, making Gd library bindings to GNU Octave, a case was, to pass a struct of points to the C function. Now from Octave, our only and most important native types would be to make them look like lists of points, where each point is a 2 element list of form [x,y].
Ultimately we made the interface from target language which was
void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c);
into this interface, acessible from host language
rotmat=[cos(angle) -sin(angle);... sin(angle) cos(angle)]; offset=[100 100]; rot(1,:)=[rotmat*[sqr(1,1);sqr(1,2)]]'+offset; %rotate X,Y rot(2,:)=[rotmat*[sqr(1,1);sqr(2,2)]]'+offset; %rotate X,Y rot(3,:)=[rotmat*[sqr(2,1);sqr(2,2)]]'+offset; %rotate X,Y rot(4,:)=[rotmat*[sqr(2,1);sqr(1,2)]]'+offset; %rotate X,Y
gdImagePolygon(ip,rot,0,green);
Where you see, a native type, like a matrix was passed to the target language via the wrapper function.
The wrapper function for this transformation from matrix to gdPointPtr is
1 DEFUN_DLD (gdImagePolygon, args,, 2 " void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c); ") 3 { 4 gdImagePtr im; 5 gdPointPtr p; 6 Matrix _p; 7 int n; 8 int c; 9 if (args.length () != 4) 10 { 11 std:: 12 cout << 13 "eg: void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c); " 14 << std::endl; 15 return octave_value (-1); 16 } 17 #ifdef DEBUG 18 std::cout << " gdImagePolygon " << std::endl; 19 #endif /* DEBUG */ 20 /* Generating Code For: ['gdImagePtr', 'im'] */ 21 const octave_value & _im = (const octave_value &) args (0).get_rep (); 22 im = (gdImagePtr) _im.ulong_value (); 23 _p = args (1).matrix_value (); 24 p = new gdPoint[_p.rows ()]; 25 for (int j = 0; j < _p.rows (); j++) 26 { 27 p[j].x = (int) _p.elem (j, 0); 28 p[j].y = (int) _p.elem (j, 1); 29 } 30 n = (int) args (2).int_value (); 31 c = (int) args (3).int_value (); 32 n = _p.rows (); 33 gdImagePolygon (im, p, n, c); 34 delete p; 35 return octave_value (); 36 }
The actual conversion of the rowmatrix into a gdPointPtr is done in the lines 24 to 32.
24 p = new gdPoint[_p.rows ()]; 25 for (int j = 0; j < _p.rows (); j++) 26 { 27 p[j].x = (int) _p.elem (j, 0); 28 p[j].y = (int) _p.elem (j, 1); 29 } 30 n = (int) args (2).int_value (); 31 c = (int) args (3).int_value (); 32 n = _p.rows ();
Well making things work on a UNIX like platform must not be hard. Working of language bindings on the window$ platform must be hard. You need to have the cygwin development environment and friends to build your bindings; development alone needs cygwin. Distribution can be done on supplying DLL's alone.
Well in code where your wrapper will do things like, creating new objects, and converting one object into another etc, you could be forced to create a new object, in the host language that most closely/natively represents the target language feature; now you will have to create a data structure/ object and pass it around to the host environment.
All this is fine, as long as you donot run into memory leaks, or run out of memory. To fix this issue, you must have to use the concept of reference counting, and attach a free() function to the destructor of your object.
In simpler terms you need to create a meta object, that wraps the host language type, and just overrides its destructor; within this destructor, free() the reference to all your target language environment data structures and pointers.
Well you're generally into handling type pointers as unsigned long integers, which means you can only do as much to, check if the pointer is not NULL. see the section 'Hiding pointers ...' for more details.
To make type checking easier, you will have to wrap the type pointers, into objects, so that you can prevents pointers to be accidentally modified. Pass the return values as objects,with the actual pointer data stored in their private member variables.
Now all you have to do on receiving this variable back in your wrapper code, is to check if this variable is alive, and created by you, and dereference the pointer without much hassle.
Depending on the host language you have to write get/set accessors to get/set accessors methods to types.
Pass by value, & pass by reference.
Some host languages might allow you to only pass args by values only, or by reference alone. You can stuff a pointer into a integer, and overcome this type of discrepancy, to make pass by reference possible. Its a lame trick, and works in most places.
You can use a concept of closures, that allow callbacks to be invoked, when initiated. Closures will assign data members, and functions and allow definite instances of callbacks to be invoked according to closure types.
There are lots of language bindings for Octave and related pacakges. see the Debian Octave Group page at http://pkg-octave.alioth.debian.org/ for a comprehensive list of language bindings to GNU Octave.
Though in theory code generation is not advised, practically it can handle most of the cases, with others wrappers copied from 'overrides' files, where generic code generation falls short of custom code's flexibility. Many language bindings packages are generated automatically, like gd-octave.
Many autogeneration tools exist like SWIG, which help the process. If you want to generate the interfaces, you would like to have a look at the PyGTK interface generator 'codegen', which generates GTK+ interface code from Python.
A simple example of providing language bindings exists in the file /LanguageBindings/. You may also download the whole code, and this document from here http://octave-gtk.sf.net/LanguageBindings/LanguageBindings.tgz for the latest version.
Check the site http://octave-gtk.sf.net/LanguageBindings/ for the latest version of this document.
This document was generated using the LaTeX2HTML translator Version 2002-2-1 (1.70)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -split=0 LanguageBindings.tex -noexternal_images
The translation was initiated by muthu on 2005-07-11