Swift - A Silver Bullet? Falls short with C-API

If you have worked with scripting languages, then you know how easy it is in comparison to using Objective-C or C++. When Apple announced Swift, it was literally the next best thing after sliced bread and Caffeinated drinks. This did prove to be popular and it also gave Apple and the developers a way to think about developing for the future and avoid errors as much as possible.
After so many swift articles, books and videos, it does surface that Swift is futuristic compatible but not very backwards compatible. At first it seemed that developers could not do everything that they could using Objective-C. While there are so many aspects to the language, developers that get stuck with one or work with write articles on that particular aspect. So there have been articles on how Swift compares to C and Objective-C in terms of speed and so on.

UPDATE: With Swift 2.0, there is greater support for c functions, callbacks and selectors.

I was trying to look at integrating Lua library into swift. I can share the results with you first, After spending a considerable amount of time on this activity, I can say that running Lua code is not such a big deal. However, exposing Swift to Lua is still a bit of challenge.

One reason for that is the lack of documentation from Apple, while Apple has an entry for all functions, there is not much that one can really take from that documentation as there are no code samples to illustrate that function or API. Apple has code samples that work on a bigger larger application which is not very useful when you want to really know how a particular API functions.

The second reason is that since Swift does not have pointers and C does and relies on them heavily, there is a lot of confusion caused because of this. The structures for the pointers are named quite well like UnsafePointer, UnsafeMutablePointer, CFunctionPointer, COpaquePointer, etc, but when it comes to using them, they are not as simple and straight forward.

The third reason is that a lot of the C/Objective-C code enums and structs are converted auto-magically for use in Swift and this changes the types significantly and unless you are an advanced user you would be wondering what happened and what to do next.

The last reason is that despite the presence of pointers, it is not easy to pass the address of a function or a closure to a C function pointer. Many of the C-API work with function pointers for callbacks etc.

Now, does that mean that Swift is unusable? Absolutely not! It is usable and like two wrongs make a right, you can create an Objective-C wrapper to deal with portions of code that you cannot manage in swift and use that. But that would defeat the purpose because then why not just use Objective-C and have import swift classes or functions for use with the Objective-C code. The idea of a pure swift solution is currently not easily achievable.

I am not expert with Swift and have not used features of swift that others might have with regards to the pointers. However some findings that I can share with all about integrating Lua and Swift is as follows.

Step 1

Create a bridging header, add a new file of type Objective-C and when asked if you want to create a bridging header, select yes.
Though you can manually set and create a bridging header, this is the easier way. Once the header is created, you can even delete the .m file.

Step 2

Add the lua source folder to a new Xcode project, select the copy files checkbox.
Make sure that you do not include the luac.c and lua.c if you have these you will get compile errors and it won't e pretty. This is simply due to the fact that both have a main function that tells the linker of an entry point which only confuses the linker and causes errors.

Step 3

Now you need to add the includes in the bridging header file to expose the Lua functions in your swift code. SO add the three imports
#import "lua.h"
#import "lualib.h"
#import "lauxlib.h"

if you look at the lua C-API, the luaL_newstate() function looks like
LUALIB_API lua_State *(luaL_newstate) (void);

However in swift the autocomplete shows you the auto-magical transformation and it is


It is converted to a COpaquePointer where as in the C-API it is defined as

typedef struct lua_State lua_State;

and further as

struct lua_State {
CommonHeader;
lu_byte status;
StkId top; /* first free slot in the stack */
StkId base; /* base of current function */
global_State *l_G;
CallInfo *ci; /* call info for current function */
const Instruction *savedpc; /* `savedpc' of current function */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
CallInfo *end_ci; /* points after end of ci array*/
CallInfo *base_ci; /* array of CallInfo's */
int stacksize;
int size_ci; /* size of array `base_ci' */
unsigned short nCcalls; /* number of nested C calls */
unsigned short baseCcalls; /* nested C calls when resuming coroutine */
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
TValue l_gt; /* table of globals */
TValue env; /* temporary place for environments */
GCObject *openupval; /* list of open upvalues in this stack */
GCObject *gclist;
struct lua_longjmp *errorJmp; /* current error recover point */
ptrdiff_t errfunc; /* current error handling function (stack index) */
};

logically lua_State * should have been something like UnsafePointer or UnsafeMutablePointer.

You can still use this so you can create a new state as

let lPtr:COpaquePointer = luaL_newstate()
luaL_openlibs(lPtr)
lua_settop(lPtr, 0)

Next you can load some lua code using the lua_loadstring method


var theScript:String = "a = 5; a = a*7; print('a = ' .. a); return a,'OK'"

var error = luaL_loadstring(lPtr, to_cString(theScript)!)
if error == 0 {
error = lua_pcall(lPtr, 0, LUA_MULTRET, 0)
}

var nResults = lua_gettop(lPtr)

If you have worked with Lua, you would know that you can return multiple return values without having to use tuples. The values are all on the stack and as you read the stack they are popped off the stack. We use the nResults to get the number of return values. In the example above, we get two return values, one is the number 35 and the other is the string OK.

The values can be read using a loop as,

for var i = nResults; i>0; i-- {
var msg = String(UTF8String: lua_tolstring(lPtr, -1 * i, nil))
println("return \(i) = \(msg)")
}

That's all there is to running some lua code from swift. You can also set the deinit function to release the memory as

lua_close(lPtr)

What is the KillJoy?

If you want to expose your swift class or its members that is when everything starts to fall apart. You can expose a C function to Lua however it ahs to be static and the signature of the function is

int function_name(lua_State* L)

the swift equivalent that swift has translated would be

func function_name(L: COpaquePointer) -> Int32

and... that's all folks!! The compiler starts to complain It cannot convert between the types, read the four points above as to why Swift is impeded. Again I do not profuse to be a Swift expert and if anyone knows how to get this bit working without having to resort to using an Objective-C wrapper, please let me know.

I could think of alternative ways, but since you cannot get the Address of the function, most of the alternative methods would not work.

So there you have it. Swift works for 80% of whatever you might need and believe me it works much better and is a joy to work with. It is only when you want to look at backwards compatibility, there are issues. I say backwards compatibility because the structures and the code would all look a lot different if they were rewritten using swift.

There is a structure called CFunctionPointer but it is almost useless in Swift since it is used for passing a C function pointer received from C or Objective-C to another C or Objective-C API, not for use in Swift.


Comments

Popular Posts