With graphics, you will often have to pass collections of floats and
doubles from Smalltalk MT to a C DLL. Here are some valuable pieces of
information when you are doing this.Passing single Floats to and from a DLL
Floats are 32 bit values and so can be passed to a DLL by value. That is, the
32bit value is pushed on the stack and can be access directly by the DLL.
Say we have a C API like this
float WINAPI StAddFloats(float p1, float p2)
{
return p1+p2;
}
We would call this in Smalltalk MT as follows:
NOTE, the return type of the API call must be set to FLOAT
addFloats
|result|
result := WINAPI StAddFloats: 0.0 with: 1.0.
^Float value: result This will return a 32 float value into
result. To bring this back into Smalltalk MT, we use ^Float value: result
Which turns the 32 bit float back into a 64 bit Smalltalk MT float and this is
returned from the method. |
Passing multiple Floats to a DLL
When it comes to passing multiple Floats by value, we can use the convenience
of a WordArray.
First for the C API which adds an array of Floats:
float WINAPI
StAddFloatArray(int
count, float
points[])
{
float
result=0.0f;
for
(int i = 0; i <
count; i=i++)
{
result += points[i];
}
return
result;
}
Here is a sample call
NOTE, the return type of the API call must be set to FLOAT
addFloatArray
|points result|
points := #[0.0 1.0 2.0 3.0 4.0].
result := WINAPI StAddFloatArray: points size with: points.
^Float value: result Passing single Doubles to and from a
DLL
Doubles use a 64 bit representation to store floating point values. because
most DLL calls use a 32 bit stack, it is easier to pass doubles by reference.
That is the address of the double is passed on the stack. Here is an example C
API to add two doublesvoid
WINAPI StAddDoubles(double
*p1, double *p2,
double *result)
{
*result = *p1 + *p2;
}
Here is a sample method call
addDoubles
|a b result|
a := 2.2.
b := 3.2.
result := Float new.
WINAPI StAddDoubles: a basicAddress
with: b basicAddress
with: result basicAddress.
^result Passing multiple Doubles to a DLL
Finally we will look at passing multiple doubles to a DLL. For this we cannot
use a WordArray, but instead we could use a regular Array. Using an Array
In Smalltalk MT,
arrays contain pointers to the elements, so we have to write the C API as
follows:void
WINAPI StAddDoubleArrayRef(int
count, double
*points[], double
*result)
{
for
(int i = 0; i <
count; i=i++)
{
*result += *(points[i]);
}
}
The Smalltalk MT call looks like this addDoubleArray
|points result|
points := #(0.0 1.0 2.0 3.0 4.0).
result := Float new.
WINAPI StAddDoubleArrayRef: points size
with: points basicAddress
with: result basicAddress.
^result Using a Structure
An alternative to using a Smalltalk MT array is to pass a collection of
Doubles by value. To do this, create a structure (say FLOAT64). Use an
initialize method as follows
initialize
"
Private - Initialize the definition dictionaries.
"
super initialize.
self
addAccessor: #double type: FT_DOUBLE
size: QWORD
Change the C API to use Double by Value
void
WINAPI StAddDoubleArrayVal(int
count, double points[], double
*result)
{
for
(int i = 0; i <
count; i=i++)
{
*result += points[i];
}
}
Finally change the Smalltalk MT call
addDoubleArray
|points result|
points := #(0.0 1.0 2.0 3.0 4.0).
pstruct := FLOAT64 new: 16.
1 to: points size do: [:i|
struct := pstruct structAt: i.
struct double: (points at: i).
].
result := Float new.
WINAPI StAddDoubleArrayVal: points size
with: pstruct
with: result basicAddress.
^result Conclusions
Using Floats allows values to be passed directly on the stack. This makes for
a convenient and easy to read API call. Using Doubles requires typically
requires passing by
reference and including the basicAddress message send. The Smalltalk MT array
can be accessed directly from the C code making it very efficient. An
alternative is to use a 64bit structure to allow multiple Doubles to be passed
by value |