Passing Floats and Doubles

 

 

Search
 
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 doubles

void 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