search:   
Adventures in Text-modeBy Twisol      
In order of no progress to done: Red, Orange, Yellow, Blue, Light blue, Green, Light green.

Last updated 2/12/09 at 12:01 am PST

Cripes! 2.0 -- See the Class Layout

Arenamatic Code w/ cConsole


Thursday, August 13, 2009
Ah, me. How easily I get distracted, enticed by the thought of "Oh, that would be cool", or "I wonder if I can do this". The first thing I need to make sure I understand, right now, is that for pity's sake, I'm not trying to write a full-featured C++/Lua binding!

With that out of the way, I may as well explain what I've done so far (and ultimately scrapped because I can do without it)... You've seen my methods-only object binding from my last post. I may keep that at least. However, you may also remember how I wanted to be able to bind variables, too. I did some experimenting with it, and in the end it did work, but it kills one particular assumption of Lua: variables are dynamically typed. Assigning a string to what once held an integer is acceptable. Not so with my variable binding, and there's no good way around it. Shall I explain? Why not.


All of Lua's lua_push* functions push the value of the variable you pass, not a reference to one. This is a good thing for plenty of reasons, but it also means you need to do some extra work to bind a variable. The only option you have is to store a pointer to the variable as a userdatum. Userdata in Lua have no inherent properties, though; metatables are applied to make them more interesting.

Unfortunately, when you access a Lua variable, the variable itself isn't told about this. It's the metamethods of its [i]parent[/it] container that are notified instead. Specifically, __newindex (for setting) and __index (for getting). So the parent container needs to have these metamethods. It also needs some way to tell if the resource you're asking for is a bound variable and not some other kind of userdatum (or not even userdata at all), namely by checking the userdatum's metatable.

And even after all that, there needs to be some way to actually get/set that variable. These functions are implemented in the C side of things, and called with __index/__setindex are sure that it's a bound C variable that you're trying to get at. The final problem here is that C variables are strongly typed. Passing a string in where the bound variable expects an int, no, you'll get an error. And that's not expected behavior for Lua by any means. At this point I threw in the towel. If I'm implementing get/set methods for these variables, why do I bother at all? I can just bind methods using the approach from my last post and get the same effect, with less hair loss!


I probably rambled a bit there, but it's 2am, so I can't really expect much better of myself. Suffice to say, I can implement the snippet of goal code I gave in my last post in pure C binding rather than doing any C++ object stuff. Maybe I should if it's such a hassle. But what can I say? I like my OO.

Comments: 0 - Leave a Comment

Link



Tuesday, August 11, 2009
I turned 17 today! I won't technically be 17 for maybe three more hours, but for all intents and purposes...

I've been working a lot on that Nucleus engine I mentioned previously. Right now I'm getting to know the Lua/C API so I can write a C module that Nucleus can load as a native extension to Java, as a Lua interop module. At the moment it's standalone for testing purposes, while I struggle to learn how to bind C++ classes/objects into Lua to my tastes. I'm happy with what I have so far, but right now it can only bind functions, not other kinds of values. It's probably worth writing down what I've learned so far though.


The goal: Access instance methods of a C++ object from Lua.
The problem: Lua can only easily bind static methods. There needs to be some way to tell a function what instance it should act on.
The solution (simple in hindsight): store the object instance as a full userdatum, and call its methods in Lua using the colon operator (object:method(params)). This is really just syntactic sugar for object.method(object, params). Given that object is the instance, object is really just 'this'.

The above is the keystone to the solution. Obviously it won't do much without some support. For example, we still have to bind a static function for Lua to call. Furthermore, userdata does nothing useful without a metatable. So we have to register a metatable to our new userdatum, with a (static) method registered as its __index at the very least. This function will handle lookups into the userdatum; if I do "object.foo", __index will recieve obj and "foo", and return an appropriate value, even if it's just nil. This is done with a static array of name/pointer pairs to allow lookups based on name.

We expect "object.method" to call __index and return that particular method. But remember, these methods are instance methods, and Lua can't call those directly. So instead of returning the method directly, we'll create a static 'thunk' function to resolve the access and call our instance method. We'll register this thunking function multiple times, once for each instance method, and registering a pointer to a data structure as an upvalue each time. The data structure just holds a pointer-to-member; even though pointers-to-member can't be casted to void*, a pointer to a vanilla struct instance sure can. This is basically just a Lua version of std::bind1st, creating a new object (closure) each time.

Now, "object.method" will call into __index and get back our simple thunking method, with a unique upvalue representing the method to call. So, calling this method with parameters (and using the colon syntax to pass a hidden 'this') should successfully resolve to a call to an object's instance method: "object:method(1,2,3)".


The code is... a bit ugly right now. I want to try to clean it up a bit before I make it public, and hopefully add support for __index returning things other than a function. This is a hobby project, so while I know I could just go the C route and go with static functions in separate .c files, I like working with objects, and it's fun to figure out how this is all supposed to work.

Thanks for reading!
~Jonathan


EDIT: May as well post what my goal code is on the Lua end. This is what I want to be able to do when the interop module is finished:

local server = atom.request("server/telnet")

server.handlers["connect"] = function(self, client)
  local db = atom.request("database/mysql")
  db:connect("localhost", "foologin", "barpasS")
  db:use("blacklist")

  local result = db:query("SELECT * FROM tbl WHERE IP='" .. client.IP .. "'")
  if #result > 0 then
    self:disconnect(client, result[1].reason)
  end
end


Ideally, the server.handlers table/udata would use a __newindex metamethod to tell when something's being assigned, and register the hooks appropriately. I think it's pretty intuitive, and I'm almost positive I can do everything in this snippet. Give me some time, people...

Comments: 5 - Leave a Comment

Link


All times are ET (US)

 
S
M
T
W
T
F
S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

OPTIONS
Track this Journal

 RSS 

ARCHIVES
August, 2009
July, 2009
March, 2009
February, 2009
December, 2008
November, 2008
October, 2008
August, 2008
July, 2008
June, 2008
May, 2008
April, 2008
November, 2007
July, 2007
June, 2007
May, 2007
April, 2007
March, 2007
February, 2007
December, 2006
October, 2006
September, 2006
July, 2006
May, 2006
April, 2006