This FAQ was written by me (Chris Double) to remind me about common problems and idioms while developing with SmallScript. There is a lot of knowledge on the SmallScript newsgroups and I wanted to document them in some way so it was easier for me to look things up.
In case it proves useful to others I've published it here. If you'd like to add, modify or discuss entries please contact Chris Double.
Note that the things mentioned here only represent my understanding of the issues. Don't hold me responsible for any errors or incorrect items here.
V1.0 - 17 Nov 2002 - Initial Version.
V1.1 - 18 Nov 2002 - Added class library questions regarding databases, tcp/ip,
and COM.
Foreign Function Interface
How do I call functions in a DLL?
Calling a function in a DLL that takes float or double arguments
isn't working.
How do I wrap a C struct for passing to an FFI function?
How do I convert an FFI return value to a structure?
How to handle FFI Functions that have pointer arguments.
How do I convert an FFI char* to a SmallScript String?
Class Library
Are there any libraries for accessing Databases?
Are there any facilities for interacting with COM/ActiveX?
Are there any TCP/IP libraries?
A. The simplest method is to create a namespace and use the 'dll' keyword. This will make all functions exported from your DLL available. Taking this approach you do not need to write any foreign function definitions except under special circumstances. For example:
namespace name: OpenGL dll: opengl32
{
}
Calling OpenGL methods now becomes as easy as referring to the functions as if they were native SmallScript functions:
glViewport(0, 0, 400, 400).
A. David Simmons explains the reasoning behind this in message id yvexrXdjCHA.2252@minos.QKS.COM on comp.lang.smallscript. The basic problem is that the automatic marshalling provided by SmallScript can't determine how to pass floats without a hint. So in this situation you need to create a prototype which declares how the arguments are passed. Here's an example:
Function [<$extern>
glColor3d(<~double>a, <~double> b, <~double> c)
]
The '~' tells SmallScript to ignore the type information for the purposes of multi-method dispatch which makes things a little faster. The 'double' says the value is a Float that should be marshalled by value. The '<$extern>' states the function is external and located in a DLL.
A. Create a class where the fields are 'auto struct' fields. Following the 'auto struct' you can define the fields almost exactly as they appear in the original C struct. For example:
class name: WSAData
fields: auto struct
int16 wVersion,
int16 wHighVersion,
char szDescription[257],
char szSystemStatus[129],
ushort iMaxSockets,
ushort iMaxUdpDlg,
char* lpVendorInfo;
{
}
You can nest structures as required. e.g.:
class name: PARAMDESCEX
fields: auto struct
uint32 cBytes,
VARIANT varDefaultValue;
{
}
class name: PARAMDESC
fields: auto struct
PARAMDESCEX* pparamdescex,
uint16 wParamFlags;
{
}
The structure can be created using the default constructor:
|paramdesc| := PARAMDESC(). paramdesc.wParamFlags := 0.
A. Use the 'SetStructToAddress' method. A common idiom is to provide a structure class with a 'fromAddress:' function that does this for you. For example:
class name: ELEMDESC
fields: auto struct
TYPEDESC tdesc,
PARAMDESC paramdesc,
uint16 filler;
{
Function [
fromAddress: anAddress
^__new() SetStructToAddress(anAddress,md_nsbytes)
]
}
An example of using this function would be:
[
|elemDescResult|
someFunction(<&>elemDescResult).
|elemDesc| := ELEMDESC fromAddress: elemDescResult.
stdout << elemDesc.tdesc.vt; cr.
]
A. FFI functions that take pointers for arguments do this for a couple of reasons.
The first is to return more than one value. This is commonly done in COM interfaces where an HRESULT is the actual return value and any futher return values are assigned to arguments passed in as pointers.
The second is to act as an 'in/out' value. That is, the item passed in contains initialization data which is then written to when the functions returns to provide additional return data.
Here are a couple of SmallScript examples:
// Returning a value |myInt| getInt(<&>myInt). stdout << myInt; cr. // Providing a structure with some data set. |elemDesc| := ELEMDESC(). elemDesc.filler := 20. doSomething(<&>ememDesc). stdout << elemDesc.tdesc.vt; cr.
The '<&> ' modifier tells SmallScript that the argument is an 'in/out' parameter and should be marshalled as a pointer.
A. The following code demonstrates how to convert a char* result from an FFI call to a String or WideString:
|ffiString| getName(<&>ffiString). |str| := LPSTR in: ffiString at: 0. |ffiWideString| getNameW(<&>ffiWideString). |wideStr| := (LPWSTR in: ffiString at: 0) asWideString.
A. An example ships with SmallScript that uses SQLite. Apart from this your best best may be to access your database using the FFI or COM.
A. SmallScript has quite a bit of COM interoperatability built in. You can create and call COM components. A program, written in SmallScript, to generate wrapper code from Type Libraries is available at Chris Double's SmallScript site. Looking at the generated code from that utility will give you a good start on how to use COM from SmallScript.
A. A third party library exists that provides TCP, UDP and Raw sockets. It is available from the SmallScript download page but more recent versions are available from the SmallScript news group. The author is Dino Rosati.