2004
| Revision History | |
|---|---|
| Revision 0.1 | 03 Jan 2004 |
| First draft for review. | |
Abstract
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
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.
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'> ^selfexternalCallFailed
![]() | 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 |
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
|sizestring|string←'SqueakString'.size←CApistrlen:string. Transcriptshow:sizeprintString;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:
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) | |
|---|---|
| ✓ | rand |
| ✓ | apiRand |
| ✗ | happenstance |
The name should not be changed to make it more "smalltalkish"
| Example int toupper(int c) | |
|---|---|
| ✓ | toupper: c |
| ✗ | toUpper: c |
| ✗ | toUppercase: char |
| ✗ | uppercase: aCharacter |
Underscore[2] characters should be removed and the next letter after an underscore should be in uppercase.
| Example char *get_current_dir_name(void) | |
|---|---|
| ✗ | get_current_dir_name |
| ✓ | getCurrentDirName |
| Example int key_setsecret(const char *key) | |
|---|---|
| ✗ | keySetSecret: key |
| ✓ | keySetsecret: key |
Method names for functions with more than one paramater should be created in one of two ways:
Create a method name with keywords
The first keyword matches the functionname and the first argument matches the first parameter, taking the above guidelines into account
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)
|
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:aHDCwith:leftwith:topwith:rightwith:bottomwith:widthwith:height<apicall: bool 'RoundRect' (Win32HDC long long long long long long) module: 'gdi32.dll'> ^ selfexternalCallFailed
apiRoundRect(aHDC,left,top,right,bottom,width,height) <apicall: bool 'RoundRect' (Win32HDC long long long long long long) module: 'gdi32.dll'> ^ selfexternalCallFailed
Example 4. Usage of the above API methods in another method
roundRectangle:aRectwidth:widthheight:height^selfapiRoundRect:selfwith:aRectleftwith:aRecttopwith:aRectrightwith:aRectbottomwith:widthwith:height
roundRectangle:aRectwidth:widthheight:height^selfapiRoundRect(self,aRectleft,aRecttop,aRectright,aRectbottom,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.
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() {
InitializeAWindowPlease();
glClearColor (0.0, 0.0, 0.0, 0.0);
glClear (GL_COLOR_BUFFER_BIT);
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);
glEnd();
glFlush();
UpdateTheWindowAndCheckForEvents();
}
Example 6. The same example from above in squeak
glRenderOn: aRenderer
aRenderer
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GLColorBufferBit);
glColor3f(1.0, 1.0, 1.0);
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
glBegin(GLPolygon);
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);
glEnd();
glFlush()

If you evaluate OpenGLMorph new
openInWorld you should see a white rectangle on
black background[6].
[ RaabSPD5 ] Experiment with positional arguments in Croquet. 05 Dec 2003.
[OGL] OpenGL programming guide: the official guide to learning OpenGL, version 1.1 . Addison-Wesley. 2nd ed. 1997.
[ 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.