next_inactive up previous


Writing Language Bindings, especially for GNU Octave

Muthiah Annamalai

11th July 2005

Introduction

Language Bindings, means providing access to functions & libraries written in the target language from the host language. Language bindings have been the snake-oil commodity of Linux platform, as its not widely documented, and left to a select few to learn. Creating language bindings helps to provide off the shelf functionality to new language/platforms without toiling to write/copy library functionality provided in the different target platform, to developers in the host language.

Motivation

Generating language bindings allows you to access functions from libraries in scripting languages or other platforms. If you want to access some functions from the LibGimp library primarily coded in 'C' language, in your host language say, Python, then you will have to write Language Bindings to this LibGimp. Same case for every other library [not coded in your host language], you want to access from the host language, will need its own Language Binding. Thats why language bindings are so important; they provide functionality at a cheaper cost, and easier method. To make your host language richer, by providing various functions available from libraries in other target languages, you need to write just language bindings. presto!

Writing Language Bindings

Now that you're convinced, "why language bindings", we must see how to actually write language bindings to target language from your host language to export the functionality. Lets start with an example. Suppose you want to make a function with a prototype

	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		}

Language Bindings Issues

The issues in making language bindings are

Providing a native interface

C and C++ allow passing of arrays and string lists as pointer types, with a length argument. Now you may convert that interface to host language native types like string lists, hash tables, tuples etc, which will blend into the host language types.

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 ();

Cross platform integrability

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.

Memory management, reference counting

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.

Type checking, and correct usage of functions

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.

Hiding pointers, and making an opaque interface

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.

Access to structure members

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.

Callback handling

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.

Conclusion

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.

About Document

You may also download the whole code, and this document from here http://octave-gtk.sf.net/LanguageBindings/LanguageBindings.tgz .

Check the site http://octave-gtk.sf.net/LanguageBindings/ for the latest version of this document.

References

License

This article may be copied, modified and distributed freely under the GNU FDL. See http://www.gnu.org/copy-left/

Author

Muthiah Annamalai (gnumuthu -dontspam- at- users -dontspam- sf -dontspam- net)
Last revised on 11th July 2005.

Glossary

About this document ...

Writing Language Bindings, especially for GNU Octave

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


next_inactive up previous
muthu 2005-07-11