Io Programming/Binding Io to C++

The following code example shows a simple class bound to Io. The binding was tested to work, but it may not be fully correct.

The IoBindingTest class only stores a number, with the usual setter and getter accessors. Also, two instances of the class can be compared to each other. The class has the regular constructor, as well as a copy constructor, and a constructor that takes a number. The copy constructor is used in the binding when creating a clone.

// begin test class

class IoBindingTest;

class IoBindingTest { public: IoBindingTest : num(0) {}; IoBindingTest(int n) : num(n) {}; IoBindingTest(IoBindingTest* ptr) : num(ptr->num) {}; ~IoBindingTest {}; int GetNum(void) { return num; }; void SetNum(int n) { num = n; }; bool CompareWith(IoBindingTest* obj) { return num == obj->num; };

private: int num; };

// end test class

The binding class creates a bunch of static class functions, which can be accessed from C code, such as Io's VM, for using the test class' similarly named instance methods. Also, the binding class has methods specific to the Io VM (proto, tag, rawClone, mark, free), and a final function to add the binding to the VM at runtime.


 * 1) include "IoVM.h"

class IoBindingTest_io { public: static IoObject* GetNum(IoObject *self, IoObject *locals, IoMessage *m); static IoObject* SetNum(IoObject *self, IoObject *locals, IoMessage *m);

static IoObject* CompareWith(IoObject *self, IoObject *locals, IoMessage *m);

static IoObject* proto(IoState* state); static IoTag* tag(IoState* state, const char* name);

static IoObject* rawClone(IoObject* self); static IoObject* mark(IoObject* self); static IoObject* free(IoObject* self); static void addBinding(IoState* state);

};

IoTag *IoBindingTest_io::tag(IoState* state, const char* name) { IoTag* tag = IoTag_newWithName_(name); tag->state = state; tag->cloneFunc = (TagCloneFunc*) rawClone; tag->markFunc = (TagMarkFunc*) mark; tag->freeFunc = (TagFreeFunc*) free;

return tag; }

The  function is used for creating the initial prototype in the Io VM.

IoObject *IoBindingTest_io::proto(IoState* state) { IoMethodTable methods[] = { {"GetNum", GetNum}, {"SetNum", SetNum}, {"CompareWith", CompareWith}, {NULL, NULL} }; IoObject* self = IoObject_new(state); self->tag = tag(state, "IoBindingTest"); self->data = 0; IoObject_addMethodTable_(self, methods); return self; }

The  function is used to put the prototype of our object into the Io VM at runtime.

void IoBindingTest_io::addBinding(IoState* state) { IoObject* self = proto(state); IoState_registerProtoWithFunc_(state, self, (IoStateProtoFunc*)proto); IoObject_setSlot_to_(state->lobby, IOSYMBOL("IoBindingTest"), self); }

IoObject *IoBindingTest_io::rawClone(IoObject *self) { IoObject *clone = IoObject_rawClonePrimitive(self); if (self->data) clone->data = new IoBindingTest(reinterpret_cast(self->data)); else clone->data = new IoBindingTest; return clone; }

The  function is used by the garbage collector. If the C++ object has references to other objects in the Io VM, they must also be marked.

IoObject *IoBindingTest_io::mark(IoObject *self) { return self; }

is pretty self-explanatory, this is where the C++ object should be freed/deleted, if appropriate.

IoObject *IoBindingTest_io::free(IoObject *self) { if (self->data) { IoBindingTest* obj = reinterpret_cast(self->data); delete obj; self->data = NULL; } return self; }

The following three functions are the actual bindings of the instance methods, so that they can be called from a script.

IoObject* IoBindingTest_io::GetNum(IoObject *self, IoObject *locals, IoMessage *m) { IOASSERT(self->data, "No C++ object"); IoBindingTest* obj = reinterpret_cast(self->data);

return IoNumber_newWithDouble_(self->state, obj->GetNum);

};

IoObject* IoBindingTest_io::SetNum(IoObject *self, IoObject *locals, IoMessage *m) { IOASSERT(self->data, "No C++ object"); IoBindingTest* obj = reinterpret_cast(self->data);

IOASSERT(IoMessage_argCount(m) == 1, "Wrong number of arguments"); IoObject *arg1 = IoMessage_locals_numberArgAt_(m, locals, 0);

obj->SetNum(IoNumber_asInt(arg1));

return self;

};

IoObject* IoBindingTest_io::CompareWith(IoObject *self, IoObject *locals, IoMessage *m) { IOASSERT(self->data, "No C++ object"); IoBindingTest* obj = reinterpret_cast(self->data);

IOASSERT(IoMessage_argCount(m) == 1, "Wrong number of arguments"); IoObject *arg1 = IoMessage_locals_valueArgAt_(m, locals, 0);

// make sure the object is tagged IOASSERT(arg1->tag, "No tag in arg");

// check the tag to make sure it is the right object class IOASSERT(strcmp(arg1->tag->name, "IoBindingTest") == 0, "arg not IoBindingTest object");

// check for the actual existence of the C++ object IOASSERT(arg1->data, "No C++ object in arg");

IoBindingTest* arg1obj = reinterpret_cast(arg1->data);

// bool is simulated by returning self or nil if (obj->CompareWith(arg1obj)) return self; else return IONIL(self); };