lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


See this question on StackOverflow: http://stackoverflow.com/q/39211143/3211851.
stackoverflow.com
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

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