|
See this question on StackOverflow: http://stackoverflow.com/q/39211143/3211851.
I have the following (simple) Lua logger for `GetInfo' (native C) function, the function is bound to the `player' table in Lua (by 3rd party module):
-- Win32 | Lua 5.1 / LuaJIT 2.0.3
local m_bActiveLogging = true -- A boolean, determines whether logging is enabled or not (by default)
local player = player -- Get a reference to the `player' table
local player_GetInfo = player.GetInfo -- Get a reference to the (original/native C) function called `GetInfo', within the `player' table
-- Replace the original/native C `player.GetInfo' function with a new function (which add logging)
player.GetInfo = function( ... ) -- The function may take ANY number of arguments (and of ANY type), varargs!!!
-- TODO: better logging
if m_bActiveLogging then
print( "Called 'player.GetInfo'" ) -- Just simple
end
-- Return whatever the original function returns. NOTE: The original function may return DYNAMIC range of ANY values!!!
return player_GetInfo( ... )
end
-- Function for toggling `m_bActiveLogging'
ToggleLogging_player_GetInfo = function()
m_bActiveLogging = not m_bActiveLogging
--return nil
end
I think the above (simple) Lua logger should work just fine; Whenever a user would call the new `player.GetInfo' function, it should print out "Called 'player.GetInfo'" as expected (of course, only when logging is enabled).
The question is:
How would I do the same thing (as above) in C (with Lua C API)?
-or-
What is the C code equivalent for the above Lua code?
(I have heard of "lua2c" (https://github.com/davidm/lua2c), but unfortunately I don't know how
to use it.)
The following C code show what I have so far:
// Win32 | Lua 5.1 / LuaJIT 2.0.3
bool m_bActiveLogging = true; // A boolean, determines whether logging is enabled or not (by default)
lua_CFunction original_player_GetInfo; // <== This is where I want to store the original function (`player.GetInfo' before replacement)
>?????????????????< // Question 1. How to store a reference to the original/native C (`player.GetInfo') function?
// ....
// Replace the original/native C `player.GetInfo' function with a new (C) function (new_player_GetInfo)
lua_getglobal(L, "player"); // ==> stack: ..., player
lua_pushstring(L, "GetInfo"); // ==> stack: ..., player, "GetInfo"
lua_pushcfunction(L, new_player_GetInfo); // ==> stack: ..., player, "GetInfo", new_player_GetInfo
lua_settable(L, -3); // ==> stack: ..., player
lua_pop(L, 1); // ==> stack: ...
// Register `ToggleLogging_player_GetInfo' function in global table
lua_pushvalue(L, LUA_GLOBALSINDEX); // ==> stack: ..., _G
lua_pushstring(L, "ToggleLogging_player_GetInfo"); // ==> stack: ..., _G, "ToggleLogging_player_GetInfo"
lua_pushcfunction(L, ToggleLogging_player_GetInfo); // ==> stack: ..., _G, "ToggleLogging_player_GetInfo", ToggleLogging_player_GetInfo
lua_settable(L, -3); // ==> stack: ..., _G
lua_pop(L, 1); // ==> stack: ...
// P.S. Is above code correct (i.e. is stack left balanced)?
// Alternative for registering `ToggleLogging_player_GetInfo' function in global table
//lua_pushcclosure(L, ToggleLogging_player_GetInfo, 0);
//lua_setglobal(L, "ToggleLogging_player_GetInfo");
// ....
// New (replacement for) `player.GetInfo' function with (simple) logging
static int new_player_GetInfo(lua_State *L)
{
//int top = lua_gettop(L); // Question 2. How to get the number of arguments the user have supplied upon the call of this (new) function?
//int n = lua_tointeger(L, lua_upvalueindex(1)); // Get the number of packed values
// TODO: better logging
if (m_bActiveLogging) {
// Just simple
lua_pushvalue(L, LUA_GLOBALSINDEX); // ==> stack: ..., _G
lua_getfield(L, -1, "print"); // ==> stack: ..., _G, _G.print
//lua_getglobal(L, "print"); // Shorter way for the (above) first two API calls
lua_pushstring(L, "Called 'player.GetInfo'"); // ==> stack: ..., _G, _G.print, "Called 'player.GetInfo'"
lua_call(L, 1, 0); // ==> stack: ..., _G
lua_pop(L, 1); // ==> stack: ...
}
// Return whatever the original function returns. NOTE: The original function may return DYNAMIC range of ANY values!!!
>?????????????????< // Question 3. How to call the original `player.GetInfo' function here (with varargs that are supplied by user to this function)?
return <number of results returned by original>; // Question 4. How to get the number of results after above (original) function is called?
}
// Function for toggling `m_bActiveLogging'
static int ToggleLogging_player_GetInfo(lua_State *L)
{
m_bActiveLogging = !m_bActiveLogging;
return 0; // nil (no value)
}
See where the question-marks are (in above C code), they are parts of where I am stuck.
So, just to be more specific and clear here: How do I...
1. Store a reference to the original/native C (in this case `player.GetInfo') function (I guess I would have to execute that first, before replacement)?
2. Get the number of arguments the user have supplied upon the call of new/replaced `player.GetInfo' function?
3. Call the original function with variable number of arguments (varargs) that are being passed to the new/replaced `player.GetInfo' function?
4. Finally, get the number of results after the original function is called (in the new/replaced `player.GetInfo' function)?
Day 2.
I have been around, searching... Turns out I don't know how could I store a reference to the native C function as `lua_CFunction' thus I got rid of "lua_CFunction original_player_GetInfo;" line...
So, here comes the handy Lua registry, this is what I have come up with regarding the first (and partially third) question:
// Create references on "open", before replacing `player.GetInfo' function
lua_newtable(L); // Creates a "new" table on top of the stack
int t_ref = luaL_ref(L, LUA_REGISTRYINDEX); // Creates and returns a reference, in the registry table, for the object (a "new" table) at the top of the stack (and pops the object)
lua_rawgeti(L, LUA_REGISTRYINDEX, t_ref); // Retrieve an object referred by reference t_ref
lua_getglobal(L, "player"); // Get `_G.player' table, it is about to be indexed
lua_getfield(L, -1, "GetInfo"); // Pushes onto the stack the value _G.player["GetInfo"]
lua_remove(L, -2); // Remove `_G.player' table from the stack
int player_GetInfo_ref = luaL_ref(L, -2); // Store `_G.player.GetInfo' (function) in the "new" table (and pops the object on top of the stack automatically)
lua_pop(L, 1); // Pop an object referred by reference t_ref
// ...
// Replace the original/native C `player.GetInfo' function with a new (C) function (new_player_GetInfo)
// ...
// Register `ToggleLogging_player_GetInfo' function in global table
// ...
// Get/Call the original/native C function (a reference from t_ref table with player_GetInfo_ref key)
lua_rawgeti(L, LUA_REGISTRYINDEX, t_ref); // Retrieve an object referred by reference t_ref (a "new" table)
lua_rawgeti(L, -1, player_GetInfo_ref); // Retrieve an object referred by reference t_ref[player_GetInfo_ref] (`_G.player.GetInfo' function)
// Call & Return whatever the original function returns. NOTE: The original function may return DYNAMIC range of ANY values!!!
// "Question 3. How to call the original `player.GetInfo' function here (with varargs that are supplied by user to this function)?"
>?????????????????< // Something like: lua_call(L, <answer from the second question>, LUA_MULTRET); ?
// Question 5. Pop the two references (only) from the stack, and preserve stack for the original function call
>?????????????????< // Argghhh, there are two references before the function call... I am kind of lost here...
// "Question 4. How to get the number of results after above (original) function is called?"
return <number of results returned by original>; // Just a thought, but can't I just simply return LUA_MULTRET; ?
// ...
// Free/Release references on "close"
luaL_unref(L, t_ref, player_GetInfo_ref); // Releases reference player_GetInfo_ref from the table at index t_ref
luaL_unref(L, LUA_REGISTRYINDEX, t_ref); // Releases reference t_ref from the table at index LUA_REGISTRYINDEX
Am I doing it correctly so far? It looks good to me, but ALL of this code is NOT tested (including the syntax).
I must admit I have got lost near the bottom lines (in `new_player_GetInfo' C function) right now, I have a feeling it is almost done. Please help me finish it.
Also, I have got something new, that is yet another (5th) question has been raised:
5. How to pop references before lua_call, while at the same time preserving varargs that are supplied by user (in new `player.GetInfo' function)?
Thanks for your help in advance. <3
|