Where I talk about my implementation of Squirrel bindings for Swift funcs
I’ve already talked about my quest to learn me some Swift. Having implemented basic Swift wrappers for Squirrel tables and arrays, the time has come to move on to the fun stuff: binding functions.
Squirrel allows binding native functions having the SQFUNCTION
signature which is as following:
Working with C function pointers in Swift? Oh boy, this gonna be good!
The goal is simple. I would like to create an SQClosure
class, which would be initialized with a Swift function like this:
And then it could be used as a valid Squirrel object and called from the Squirrel code.
That’s not that a big thing to dream of. SQRat, a Squirrel binding utility for C++, does that easily with templates (although it had been written a long time before lambdas appeared on the C++ horizon, so these are not supported AFAIK).
How does one achieve this sorcery? It could be done with a certain amount of boilerplate code and some clever templates, at least in C++.
So the in the core of the binding mechanism lies the following API:
Here you pass the Squirrel VM handle, an SQFUNCTION
pointer and number of free variables to bind to the function1.
After that, you have your freshly bound native closure in the Squirrel VM stack and can set it as a value for a table slot, use it as a parameter for another function or whatever.
When this function is called from the Squirrel code, the native C SQFUNCTION
will get called with the Squirrel VM as the only parameter. All parameters passed from Squirrel code will already be pushed to the VM stack waiting to be read and processed.
Pretty simple, right? Apparently not if we are accessing Squirrel from Swift.
Googling “swift c function pointer” yields the following result:
C function pointers are imported into Swift as
CFunctionPointer<Type>
, whereType
is a Swift function type. For example, a function pointer that has the typeint (*)(void)
in C is imported into Swift asCFunctionPointer<() -> Int32>
.
So we delve deeper and look into the mysterious CFunctionPointer
template… to find almost nothing (less relevant code omitted).
Nothing really useful so far. It seems we will not be able to create CFunctionPointers
from Swift code ourselves and surely we will not be able to pass Swift funcs as C functions to the Squirrel API.
Well, it seems we will not be able to avoid Objective-C code in our Swift framework entirely. Since we cannot reasonably provide SQFUNCTIONs
to the Squirrel API from Swift, it seems to be the only possible solution to do it from Objective-C.
Why not ‘Objective’ and not just ‘C’ you ask? Because we’ll be binding Objective-C blocks.
The idea is pretty simple. First, we introduce a ‘Swift binder’ block type, which has a signature similar to SQFUNCTION
:
Then we use a single SQFUNCTION
for all our bindings, which expects the block passed to it as a free variable and just forwards the HSQUIRRELVM
param to the said block. One of the possible implementations looks like this:
Here we are using the SQUserPointer
type (which actually is just a void*
) to pass the block to the bound function. The binding call uses a helper function and looks like this:
The single free variable will be on top of the Squirrel VM stack when calling the native function.
Notice that this code has a flaw. The block is leaked on the native closure creation. It happens because the user pointer itself is a simplified version of a ‘user data’ concept in Squirrel and it is considered a value type and therefore has no destruction facility built in. We obviously have to release the block when the closure is destroyed, so SQUserPointer
is probably not a good way to go.
The comment in the last code snippet hints that using sq_newuserdata
API would probably help. I’ve left this note for myself but have not tried it yet. User data can have release hooks - additional functions, which are called when the corresponding Squirrel object is destroyed2. These should prove helpful for releasing our blocks when needed.
Now we have a binding API with an overly verbose name SwiftSquirrel_Private_createNativeClosureWithBlock
, which no longer accepts a C function pointer, but an Objective-C block instead. This one can be used from Swift rather straightforwardly:
A little bit of this and that is going on here: we are also keeping a strong reference to the Squirrel object representing the newly created closure, adding a readable name to it for debugging purposes, etc. However, the general idea should be more or less clear. We’ve bound an Objective-C block to Squirrel, and it is callable from inside the Squirrel scripts.
We can bind simple functions using the API we’ve created. For example, this is a function binding which adds two integers together:
Still, that is not really what we wanted initially. At least it is not completely what we wanted. It would be more convenient to do the same thing by calling something like that:
Is that possible? Kind of. However, this is a topic of a future post.
I do not really know if the ‘free variable’ term is commonly used in this meaning. Squirrel API documentation defines free variables as following: “A free variable is a variable external from the function scope as is not a local variable or parameter of the function. Free variables reference a local variable from an outer scope”. So basically ‘free variables’ are captured variables from an outer scope, which are the reason why the Squirrel closures are closures. When binding a native function, you can pass external free variables using the provided API. ↩
Squirrel utilizes memory-counted model similar to the Objective-C with ARC enabled: there are strong and weak references between Squirrel objects, and when the reference count of an object becomes zero, it gets immediately destroyed. Although I remember there were some APIs related to garbage collection so there may be other options. ↩