EDIT: Turns out this is not something possible with Lua, having the __index method AND methods like class instance methods. It's either or.
Trying to get my Lua interface to work where both fields and instance methods are supported. It seems that by manipulating the initialization, I can only get the functions (_f), or the methods (_m) to work, not both.
I feel like it's something really simple I'm just missing.
How I initialize the library:
void PushUserdata(const void *data, const char *metatable)
{
const void **wrapped_ptr = (const void**)lua_newuserdata(l, sizeof(const void*));
*wrapped_ptr = data;
luaL_getmetatable(l, metatable);
lua_setmetatable(l, -2);
}
static int player_head(lua_State *L)
{
if (!Player::playerHead)
lua_pushnil(L);
else
PushUserdata(Player::playerHead, "player");
return 1;
}
static int playerget(lua_State *L)
{
Player *player = *CHECKPLAYER(L, 1); // Get double pointer and dereference to get real pointer
const char *field = luaL_checkstring(L, 2);
if (!strcmp(field, "next"))
{
if (!player->next)
lua_pushnil(L);
else
PushUserdata(player->next, "player");
}
else if (!strcmp(field, "prev"))
{
if (!player->prev)
lua_pushnil(L);
else
PushUserdata(player->prev, "player");
}
else if (!strcmp(field, "obj"))
{
if (!player->obj)
lua_pushnil(L);
else
PushUserdata(player->obj, "wobj");
}
else if (!strcmp(field, "AddCollisionObjHook")) // This ends up here if __index is in the table below...
{
}
else
return 0;
return 1;
}
static const struct luaL_Reg playerlib_f[] = {
{"head", player_head},
{"AddPreThinker", AddPreThinker},
{"AddPostThinker", AddPostThinker},
{NULL, NULL}
};
static const struct luaL_Reg playerlib_m[] = {
{"__tostring", player2string},
{"__index", playerget},
{"__newindex", playerset},
{"AddCollisionObjHook", AddCollisionObjHook},
{NULL, NULL}
};
int Lua_PlayerLib(lua_State *L)
{
luaL_newmetatable(L, "player");
lua_pushvalue(L, -1); // duplicates the metatable...but why?
luaL_setfuncs(L, playerlib_m, 0);
luaL_newlib(L, playerlib_f, 0);
lua_setglobal(L, "player");
return 1;
}
Lua script:
me = playerlib.head()
me:AddCollisionObjHook(playerHitObj)
Error message:
Warning: [string "postload.lua"]: attempt to call method 'AddCollisionObjHook' (a nil value)
'me' is absolutely a valid non-nil value.
What you're trying to do is possible, but not in the way you're trying to do it.
I think it's worth reviewing how method calls and metatables/metamethods work, and what your code, as written, is actually doing. The tl;dr is:
method calls are just normal field lookups
metatables, and the metamethods they contain, are operator overloads, not method definitions
if you're implementing this for userdata you need an __index metamethod that can handle both field and method lookups
First of all, Lua has no inbuilt distinction between "methods" and "fields". You may find it convenient to differentiate the two when organizing your code, but as far as the Lua language is concerned methods and fields are the same thing. A method is just a field where the key is a valid lua identifier and the value is a function.
So, when you write something like me:AddCollisionObjHook(playerHitObj), what actually happens is something like:
local self = me
local method = self["AddCollisionObjHook"]
method(self, playerHitObj)
(Two notes on this:
no actual new locals are created; this all happens in the internals of the Lua interpreter.
self["AddCollisionObjHook"] and self.AddCollisionObjHook are two ways of writing the same thing; the latter is just a convenient shortcut for the former.)
So, how does that self["AddCollisionObjHook"] lookup work? The same way any other field lookup works. The Lua Manual goes into detail on this, including pseudocode, but the part relevant to your code is:
-- We're looking up self[key] but self is userdata, not table
local mt = getmetatable(self)
if mt and type(mt.__index) == 'function' then
-- user provided an __index function
return mt.__index(self, key)
elseif mt and mt.__index ~= nil then
-- user provided an __index table (or table-like object)
-- retry the lookup using it
return mt.__index[key]
else
-- no metatable, or metatable lacks __index metamethod
error(...) -- don't know how to do field lookup on this type!
end
Note that at no point in this process are fields other than __index looked up in the metatable. The metatable exists only to tell Lua how to implement operators for types that don't normally have them; in this case the field lookup ("index") operator ([], and its aliases . and :) for a specific kind of userdata. It's entirely down to __index itself to handle the actual process of turning field names into values, either by being a table that the lookup can be retried with or by being a function that can return the associated value.
So, this brings us to the answer of how to support both (settable) fields and (callable) methods:
__newindex needs to understand how to set fields
__index needs to understand how to return both field values and method implementations
Since, from Lua's perspective, both field lookups and method lookups are the same operation, and thus __index gets used for both.
In light of that, how should we organize the code to support both, and how can we restructure your code to work? There's lots of ways to do this, although for the purposes of this answer I'm going to make a few assumptions:
fields are stored entirely C-side, with no corresponding data to manage in Lua
methods cannot be overwritten by Lua code
metamethods are stored separately from instance methods
The last one is not strictly necessary; in fact it's very common to store both metamethods and instance methods in the same table (I usually do this myself). However, I think this also tends to engender confusion among Lua newbies about the distinction between them, so the interest of making the code as clear as possible, I'm separating them out in this answer.
With that in mind, let's rework the setup code. I've looked at your edits to try to reconstruct what you originally had in mind.
static int playerget(lua_State *L)
{
Player *player = *CHECKPLAYER(L, 1);
const char *field = luaL_checkstring(L, 2);
// Check if it's a method, by getting the method table
// and then seeing if the key exists in it.
// This code can be re-used (or factored out into its own function)
// at the start of playerset() to raise an error if the lua code tries
// to overwrite a method.
lua_getfield(L, LUA_REGISTRYINDEX, "player-methods");
lua_getfield(L, -1, field);
if (!lua_isnil(L, -1)) {
// Lookup in methods table successful, so return the method impl, which
// is now on top of the stack
return 1;
} else {
// No method, so clean up the stack of both the nil value and the
// table of methods we got it from.
lua_pop(L, 2);
}
if (!strcmp(field, "next"))
// ... code for reading fields rather than methods goes here ... //
}
// Functions that are part of the player library rather than tied to any
// one player instance.
static const struct luaL_Reg playerlib_api[] = {
// player.head() -> returns the first player
{"head", player_head},
{NULL, NULL}
};
// Metamethods defining legal operators on player-type objects.
static const struct luaL_Reg playerlib_metamethods[] = {
// Overrides the tostring() library function
{"__tostring", player2string},
// Adds support for the table read operators:
// t[k], t.k, and t:k(...)
{"__index", playerget},
// Adds support for the table write operators:
// t[k]=v and t.k=v
{"__newindex", playerset},
{NULL, NULL}
};
// Instance methods for player-type objects.
static const struct luaL_Reg playerlib_methods[] = {
// player_obj:AddCollisionObjHook(hook)
{"AddCollisionObjHook", AddCollisionObjHook},
// player_obj:AddPreThinker(thinker)
{"AddPreThinker", AddPreThinker},
// player_obj:AddPostThinker(thinker)
{"AddPostThinker", AddPostThinker},
{NULL, NULL}
};
int Lua_PlayerLib(lua_State *L)
{
// Create the metatable and fill it with the stuff from playerlib_metamethods.
// Every time a player object is pushed into Lua (via player_head() or similar)
// this metatable will get attached to it, allowing lua to see the __index,
// __newindex, and __tostring metamethods for it.
luaL_newmetatable(L, "player");
luaL_setfuncs(L, playerlib_metamethods, 0);
lua_pop(L, 1);
// Create the method table and fill it.
// We push the key we're going to be storing it in the registry under,
// then the table itself, then store it into the registry.
lua_pushliteral(L, "player-methods");
luaL_newlib(L, playerlib_methods, 0);
lua_settable(L, LUA_REGISTRYINDEX);
// Initialize the `player` library with the API functions.
luaL_newlib(L, playerlib_api, 0);
// Set that table as the value of the global "player".
// This also pops it, so we duplicate it first...
lua_pushvalue(L, -1);
lua_setglobal(L, "player");
// ...so that we can also return it, so that constructs like
// local player = require 'player'; work properly.
return 1;
}
Breaking it down, this gives us three tables:
player, which holds the actual library API like player.head()
REGISTRY["player"], which holds the metatable shared by all player objects
__tostring which is invoked for prettyprinting
__newindex which is invoked for field writes
__index which is invoked for field reads (including method lookup!)
REGISTRY["player-methods"], which holds all the instance methods
__index looks here for method implementations
As noted above, I've kept the table of metamethods and the table of methods separate in the hopes of minimizing conceptual confusion; idiomatic code would probably store all the methods and metamethods together, and use luaL_getmetafield() at the start of playerset() and playerget() to do method lookup.
In F# i'm using an external DLL (in this case SDL Graphics library) I'm importing the method I require as follows...
[<DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int SDL_QueryTexture(nativeint texture, uint32& format, int& access, int& w, int& h)
This works fine and I can successfully call the method using the following...
let result = SDLDefs.SDL_QueryTexture(textTexture, &format, &access, &w, &h)
The problem is that the native SDL methods accept null values for many pointer arguments. This is required in some scenarios (which function like overloaded methods). I can't find any way to call these methods from F# passing nulls.
For example, this fails with "does not have null as proper value"
let result = SDLDefs.SDL_QueryTexture(textTexture, &format, null, &w, &h)
I read about the attribute [AllowNullLiteral] but it seems like I can only apply it to types I define, and not pre-defined types which are used in my imported DLL.
Is there any way I can do this?
If you want to specify nulls, you need to use "raw pointers", which are represented by types nativeint and nativeptr<T>.
[<DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl)>]
extern int SDL_QueryTexture(nativeint texture, uint32& format, nativeint access, int& w, int& h)
// Call without null
let access = 42
let pAccess = NativePtr.stackalloc<int> 1
NativePtr.write pAccess access
SQL_QueryTexture( textTexture, &format, NativePtr.toNativeInt pAccess, &w, &h )
let returnedAccess = NativePtr.read pAccess
// Call with null
SQL_QueryTexture( textTexture, &format, null, &w, &h )
NOTE: be careful with stackalloc. Allocating memory on the stack is quite handy, because you don't need to explicitly release it, but pointers to it will become invalid once you exit the current function. So you can only pass such pointers to an external function if you're sure that the function won't store the pointer and try to use it later.
If you need to pass a pointer to real heap memory that's not going anywhere, you'll need Marshal.AllocHGlobal. But don't forget to release! (or else :-)
let access = 42
let pAccess = Marshal.AllocHGlobal( sizeof<int> )
NativePtr.write (NativePtr.ofNativeInt pAccess) access
SQL_QueryTexture( textTexture, &format, pAccess, &w, &h )
Marshal.FreeHGlobal( pAccess )
In the Lua C API I can store a number or a string from the stack with lua_tostring().
How can a “reference” (if that is the correct term) to a Lua function be passed to C through the Lua API? So it can be called later from C, with lua_call(), without having to reference it by its name.
(It really needs to be like that, the C program will call the function somewhere in the future and the program doesn't know anything about the function because the functions to be passed are defined in the Lua program)
In C you can't refer to Lua functions directly but you can represent numbers and strings. So, for a function to "be called later", you can store this function in some table and refer to it by a numeric or string key of the table.
Here's a simpleminded mechanism to start with:
On the Lua side:
funcs = {}
local function register_hanlder(key, fn)
funcs[key] = fn
end
register_handler("on_mouse_click", function()
print "You clicked me!"
end)
On the C side:
/* code not tested */
lua_getglobal(L, "funcs");
lua_getfield(L, -1, "on_mouse_click");
if (!lua_isnil(L, -1)) {
lua_call(L, 0, 0);
else {
// nothing registered
}
Instead of registering the functions in a global table you can register them in the registry table (see luaL_ref). You'll get some integer (that's the key in the registry table where the function value is) that you can pass around in you C code.
Note that if you don't need to store a Lua function "for use later" you don't need any of this: if your C function has some Lua function passed to it via argument you can call it outright.
== Edit:
As I mentioned, instead of using a global variable (the funcs above) you can store the reference to the function in the "registry". Conceptually there's no difference between this method and the previous one.
Let's re-use the previous example: you want the Lua programmer to be able to register a function that would be fired whenever a mouse is clicked in your application.
The Lua side would look like this:
register_mouse_click_handler(function()
print "the mouse was clicked!"
end)
On the C side you define register_mouse_click_handler:
static int the_mouse_click_handler = 0;
static int register_mouse_click_handler(lua_State* L) {
the_mouse_click_handler = luaL_ref(L, LUA_REGISTRYINDEX);
return 0;
}
(...and expose it to Lua.)
Then, in your application, when the mouse is clicked and you want to call the Lua function, you do:
...
if (the_mouse_click_handler != 0) {
lua_rawgeti(L, LUA_REGISTRYINDEX, the_mouse_click_handler);
lua_call(L, 0, 0);
} else {
// No mouse handler was registered.
}
...
(I may have typos in the code.)
I currently stuck in a problem.
Below is the original code
sem_t s;
sem_init(&s, 0, 1);
And I need to replace sem_init with sem_open because it will be used on iOS
sem_t s;
sem_open("/s", O_CREAT, 0644, 1); //which will return sem_t*
How should I assign the return address to s?
Thanks
p.s. i do not declare sem_t* s, because this is a huge library which I won't change it too much
Create a new semaphore pointer,
sem_t *sptr;
Invoke sem_open as sptr holds the address,
sptr = sem_open("/s", O_CREAT, 0644, 1);
And below preprocessor macro should do the trick,
#define s *sptr
With the above method, when ever s is passed as argument, for example sem_wait(&s) boils to sem_wait(&*sptr) => sem_wait(sptr) which is desired without changing sem_t s.
I want to know how print function works. But I can't find print function in python3.2's source code!
In python2.5 print an integer(print 100, for example), will call int_print(). Because when create an int object, initialize PyIntObject's function pointer - tp_print that point to int_print():
printfunc tp_print;
/* We can't provide a full compile-time check that limited-API
users won't implement tp_print. However, not defining printfunc
and making tp_print of a different function pointer type
should at least cause a warning in most cases. */
typedef int (*printfunc)(PyObject *, FILE *, int);
In python3.x, all integer is an object of PyLongObject, but it initialize tp_print=NULL. And print is a function not like python2.x.
Please tell me when call print, how it works! Thanks a lot!
I have already known! When print a builtin object, print() will call builtin_print() function.
builtin_print(PyObject *self, PyObject *args, PyObject *kwds)