luapower / objc

Objective-C & Cocoa Bridge
http://luapower.com/objc
25 stars 7 forks source link

How to pass objects back and forth through the bridge? #1

Closed rraallvv closed 9 years ago

rraallvv commented 9 years ago

Is it posible to do something like this:

MyClass *myObj = [[MyClass alloc] init];
lua_pushlightuserdata (L, myObj);
lua_setglobal(L, "myObj");
lua_dostring(L,
  "local objc = require'objc' \n"
  "local ffi = require'ffi' \n"
  "local myLuaObj = ffi.cast(ffi.typeof'id', myObj) \n"
  "myLuaObj:doSomething() \n"
);

With the snippet above I get the error 'struct objc_object' is not callable

I would like to be able to pass objects back and forth between ObjC and the Lua virtual machine.

capr commented 9 years ago

Never used objc from C but I can't think of a reason why your snippet wouldn't work. Can you look if objc.lua reaches 1940 where the metatype for objc_object is hooked? If it is, then look if get_instance_field gets called when you call doSomething.

rraallvv commented 9 years ago

Thanks for the quick reply,

I changed @property and @synthesize to the respective ivar, setter, and getter, and now the the error is an assertion at line 133, commenting that line seems to work.

@interface MockObject : NSObject {
    NSString *_name;                //<-- workaround
}
//@property (strong) NSString *name;
- (NSString *)name;                 //<-- workaround
- (void)setName:(NSString *)name;   //<-- workaround
@end

@implementation MockObject
//@synthesize name;
- (NSString *)name {                //<-- workaround
    return _name;
}
- (void)setName:(NSString *)name {  //<-- workaround
    _name = name;
}
@end

From the Lua side

 local objc = require('objc')
 oldName = mockObj:name()
 mockObj:setName(objc.toobj('new name'))
 newName = mockObj:name()
rraallvv commented 9 years ago

To get objc to work with properties I need to change line 1866

from this

return caller(obj)

to this

return caller
capr commented 9 years ago

Ah, crud. I knew that was going to happen one day :) As the comment says, your pointers are too large to hold in the integer part of a Lua number (courtesy of ASLR I suppose?). We need that to make pointers hashable (and we can't use all the bits of the double because of LuaJIT's NaN tagging). I guess you'll have to change line 132 from tonumber() to tostring() for now, until I find a better solution. Interesting that I didn't stumbled upon this limitation when loading the standard frameworks -- it seems that they never map to such high memory addresses for some reason.

On the @property issue, I have to look at it more closely. My initial gut reaction was to say that you don't have enough RTTI on your class. In any case, your solutions breaks my test (below) so I'm not sure what's going on.

local pr = NSProgress:progressWithTotalUnitCount(123)
assert(pr.totalUnitCount == 123) --as initialized
pr.totalUnitCount = 321 --read/write property
assert(pr.totalUnitCount == 321)
assert(not pcall(function() pr.indeterminate = true end)) --attempt to set read-only property
assert(pr.indeterminate == false)
rraallvv commented 9 years ago

I see, the problem is that I was calling the property getter with a colon, calling property getters with dot notation seems more advantageous, so I put back the parameter obj in the line 1866.

Regarding the assertion issue, I'm still getting error at line 133 when using tostring in line 132.

capr commented 9 years ago

Oh yeah sure, I forgot - you have to remove the assertion if you do tostring().

So did you figure out what the problem was with the property? Is that a class property or an instance property you're defining? I'm not too familiar with Obj-C's notation. In fact I only use objc from Lua[1].

[1] https://github.com/luapower/nw/blob/master/nw_cocoa.lua

capr commented 9 years ago

Regarding the nptr() issue, are you doing this on iOS by any chance? Because AFAIK iOS uses tagged pointers for class pointers, so tonumber() wouldn't work there anyway.

rraallvv commented 9 years ago

I'm trying to develop a plugin system for a SpriteKit level editor on OS X, for iOS maybe I'll do the bindings by hand or using SWIG. By the way using objc for creating an entire application is pretty cool, I'd bet its even better than JXA. Regarding the issue, I think I'm closing this, thanks for helping out.

capr commented 9 years ago

I see. Sorry if I couldn't be of more help. I'm not clear on whether you solved the problem or you gave up :)

Regarding JXA, I never used it but AFAIK needs 10.10? But yeah, looking at the jxa-cookbook the syntax looks very similar (not clear if you can make subclasses that implement interfaces though).

objc works down to 10.7 btw, that's when they switched to generating .bridgesupport files with clang so they got much more accurate and complete.

rraallvv commented 9 years ago

I'was concerned about passing objects back and forth from/to ObjC, but now that I see it's possible I'm definitely using objc for the plugin manager. The problem was in the way I was calling the property getter after passing the object to Lua, and using this trick I can get that or any object back.

For JXA I found this tutorial, it seems it allows subclassing.