Positional Arguments

A quick review

Alexander Lazarević


Revision History
Revision 0.103 Jan 2004
First draft for review.


Informal review of methods with positional arguments in squeak. Might be interesting for squeak programmers using FFI and/or people starting to learn OpenGL.

Table of Contents

The foreign function interface
Positional arguments
OpenGL programming


After I read [ RaabSPD5 ] I thought to have a look at methods with positional arguments sooner or later. Now I found some time during the holidays to have a closer look at the subject and you might enjoy reading about it. On the one hand it might be helpful for squeak programmers who are maintaining an API for an external library in squeak or planing to write one. On the other hand it describes a possible way to learn OpenGL within squeak using examples presented in standard text books.

The foreign function interface

One bridge for programmers to the "outer world" in squeak is the foreign function interface FFI. It can be used to call functions in external shared libraries from squeak methods. The FFI takes care of parameter type conversion, the right order and size of parameters on the stack and converting results back into squeak objects. Before one can call a specific external function, a corresponding method has to be written in squeak.

Example 1.  a callout to the C function strlen

	strlen: aString
		<apicall: long 'strlen' (char *) module: 'c'>
		^self externalCallFailed

[Caution]module names

At least on Unix the FFI system tries to be clever in guessing where to find shared libraries. In the above example squeak looks for a library named c, c.so, c.dylib, libc, libc.so or libc.dylib in the current directory. If this fails it starts the same search in other "well known" directories like /lib or /usr/lib using compiled in defaults and the environment variables SQUEAK_PLUGIN_PATH and LD_LIBRARY_PATH. If the library you want to use can't be found by that procedure or follows an unusual naming scheme, you have to tell FFI the exact name and/or location of the library. For the above to work on my system I had to specify the exact name libc.so.6 instead of just c.

In Example 1 you can see a method that defines a callout to the function strlen in the standard C library. The function strlen expects a pointer to a null terminated C string and returns the length of the string as an integer. The FFI takes care of converting the string object aString into a C string before the function strlen gets called. The result of the function will be converted back into an integer object, which will be the return value of the method strlen:. The code below the <apicall:module:> declaration only gets executed in case of an error (e.g library not found) and serves as a fallback. In the example an appropriate error will be raised.

Now the selector strlen: could be used in a message to the class CApi in the usual way. Example 2 shows some code fragment assuming the method strlen: had been implemented as a class side method of the class CApi.

Example 2.  Using strlen: in a message to CApi

	| size string |
	sizeCApi strlen: string.
	Transcript show: size printString; cr.

Now it seems easy enough to write the corresponding API methods for several library functions and just use them in squeak[1]. But there is the question how the API method names should be chosen. The main aim should be that the names should match the ones of the C API as far as possible. So programmer who are familiar with a C API should be able to use the same API from within squeak without too many surprises. This also makes it more likely, that a squeak programmer could benefit from consulting the documentation, tutorials, HOWTOs and examples that exists for the C API.

Some good guidelines for naming API methods could be:

  1. The method name begins with the functionname in lowercase. If necessary the name begins with a common lowercase prefix followed by the capital functionname

    Example int rand(void)
  2. The name should not be changed to make it more "smalltalkish"

    Example int toupper(int c)
    toupper: c
    toUpper: c
    toUppercase: char
    uppercase: aCharacter
  3. Underscore[2] characters should be removed and the next letter after an underscore should be in uppercase.

    Example char *get_current_dir_name(void)
    Example int key_setsecret(const char *key)
    keySetSecret: key
    keySetsecret: key
  4. Method names for functions with more than one paramater should be created in one of two ways:

    • Create a method name with keywords

      1. The first keyword matches the functionname and the first argument matches the first parameter, taking the above guidelines into account

      2. For every additonal parameter the keyword with: and an argument named after the parameter will be added

    • Use positonal arguments

    Example size_t fread( void *ptr, size_t size, size_t nmemb, FILE *stream)
    fread: ptr  with: size  with: nmemb  with: stream 
    freadPtr: ptr  size: size  nmemb: nmemb  stream: stream 
    fread (ptr, size, nmemb, stream)

Positional arguments

Methods with positonal arguments are currently only available in the latest version of croquet, but can be also used in the current development branch[3] of squeak after filein of [ PosArgsCS ]. In Example 3 there is the same method once defined with the common keyword method pattern and once with positionl arguments. Below in Example 4 is some sample code to illustrate the difference in message sending.

Example 3.  the same API method with keywords and with positional arguments

	  apiRoundRect: aHDC with: left with: top with: right with: bottom with: width with: height
	  	<apicall: bool 'RoundRect' (Win32HDC long long long long long long) module: 'gdi32.dll'>
	  	^ self externalCallFailed
	  apiRoundRect(aHDC, left, top, right, bottom, width, height)
	  	<apicall: bool 'RoundRect' (Win32HDC long long long long long long) module: 'gdi32.dll'>
	  	^ self externalCallFailed

Example 4.  Usage of the above API methods in another method

	  roundRectangle: aRect width: width height: height 
	  		apiRoundRect: self
			with: aRect left
			with: aRect top
			with: aRect right
			with: aRect bottom
			with: width
			with: height
	  roundRectangle: aRect width: width height: height 
	  		apiRoundRect(self, aRect left, aRect top, aRect right, aRect bottom, width, height)

So what are the benefits of using positional arguments? In the use case of API methods I think they are the best way to achieve the aim from above. That was the easy usage of an API in squeak that conforms to what is described in existing documentation.

OpenGL programming

Now let's see how it feels to use one particular API from within squeak. To be able to use OpenGL we need the corresponding API methods. One can find the C prototypes of the OpenGL API functions in [OGL] for example and it is easy enough to write the API methods in squeak for a few functions. But there are about 150 distinct functions in OpenGL not counting their variations. Luckily somebody already has done the work for us[4]. The OpenGL API is part of the latest version of croquet, but can be used with the current development branch[3] of squeak too, after the filein of [ GLXCS ][5]. Now we just need a little playground where we can experiment with OpenGL. A modified version of the OpenGLMorph from croquet available at [ OGLMorphCS ] is all that we need.

Now let's have a look at the first OpenGL example in [OGL], which can be seen in Example 5. After we filed in all the changesets mentioned above, we have all "whateverWeNeed". The method were we place our OpenGL code resides in the OpenGLMorph and is surely not named main but glRenderOn:. Instead of a window we have an OpenGLMorph that also takes care of all initialization and updating. Now we can write the actual OpenGL code into the method. Example 6 shows how the method looks like.

Example 5.  Example of OpenGL code in [OGL] on page 6

	#include <whateverYouNeed.h>
	main() {

		glClearColor (0.0, 0.0, 0.0, 0.0);
		glColor3f (1.0, 1.0, 1.0);
		glOrtho (0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
		glBegin (GL_POLYGON);
			glVertex3f (0.25, 0.25, 0.0);
			glVertex3f (0.75, 0.25, 0.0);
			glVertex3f (0.75, 0.75, 0.0);
			glVertex3f (0.25, 0.75, 0.0);


Example 6.  The same example from above in squeak

	glRenderOn: aRenderer
			glClearColor(0.0, 0.0, 0.0, 0.0);
			glColor3f(1.0, 1.0, 1.0);
			glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
				glVertex3f(0.25, 0.25, 0.0);
				glVertex3f(0.75, 0.25, 0.0);
				glVertex3f(0.75, 0.75, 0.0);
				glVertex3f(0.25, 0.75, 0.0);

If you evaluate OpenGLMorph new openInWorld you should see a white rectangle on black background[6].



[ RaabSPD5 ] Andreas Raab. Experiment with positional arguments in Croquet. 05 Dec 2003.

[OGL] Mason Woo, Jackie Neider, and Tom Davis. OpenGL programming guide: the official guide to learning OpenGL, version 1.1 . Addison-Wesley. 2nd ed. 1997.

[ PosArgsCS ] A small changeset to enable methods with positonal arguments in squeak[3] .

[ GLXCS ] GLX API in a changeset for squeak[3] .

[ OGLMorphCS ] An OpenGLMorph as a playground for squeak[3] .

[1] There is some more work involved when some of the functions do return things like pointers to C structures or expect them as parameters. Then you have to give FFI more hints how conversions should be done. Have a look at the class ExternalStructure and friends for more information on this.

[2] Or any other character that is used as a separator

[3] Version 3.7alpha update 5566 as of this writing

[4] Or more precisely Andreas has written a tool that does all the work for him, if I read the comments in the API methods correctly. :)

[5] You need to have Balloon3D installed from SqueakMap before filing this in

[6] If you get an error this could be because FFI can't find the OpenGL library. Find the subclass of GLX for your platform and make the entry in the method openGLLibraryName: more explicit like I described above.