BigAgg
BigAgg

Reputation: 43

C++ accessing global variables in a lua_State

I am currently building a program that lets me implement Lua scripts.

I want to get into serialization of those scripts, by accessing only the global variable of a lua_State by name and by value.

My Lua script looks like this:

TESTVALUE1 = 10
--TESTVALUE2 = 5.5
--TESTVALUE3 = "TEST"
--TESTVALUE4 = {hallo=5, x=5.4, y="y", t={5, 8}}

function Setup(object)  -- Setup is executed once the GameObject is completely loaded the associated object is given to store in a global if needed

end

function Update(deltatime)  -- deltatime is the frametime

end

function Render() -- Called every frame, put your render functions inside!

end

And my serialization in C++ looks like this:

void serialize(std::ofstream &file) {
  if (file.is_open() && file.good()) {
    // Writing script name and path to the file
    file.write((char *)&path, sizeof(path));
    file.write((char *)&name, sizeof(name));
    lua_pushglobaltable(L);
    lua_pushnil(L);
    while (lua_next(L, -2) != 0) {
      char value_type = 0;
      std::string name = lua_tostring(L, -2);
      lua_getglobal(L, name.c_str());
      if (lua_isfunction(L, -1)) {
        lua_pop(L, 2);
        continue;
      }
      if (lua_isinteger(L, -1)) {
        file.write((char *)&name, sizeof(name));
        int val = lua_tointeger(L, -1);
        value_type = 1;
        file.write((char *)&value_type, sizeof(value_type));
        file.write((char *)&val, sizeof(val));
      } else if (lua_isnumber(L, -1)) {
        file.write((char *)&name, sizeof(name));
        float val = lua_tonumber(L, -1);
        value_type = 2;
        file.write((char *)&value_type, sizeof(value_type));
        file.write((char *)&val, sizeof(val));
      } else if (lua_isstring(L, -1)) {
        file.write((char *)&name, sizeof(name));
        std::string val = lua_tostring(L, -1);
        value_type = 3;
        file.write((char *)&value_type, sizeof(value_type));
        file.write((char *)&val, sizeof(val));
      } else if (lua_istable(L, -1)) {
        file.write((char *)&name, sizeof(name));
        value_type = 4;
        file.write((char *)&value_type, sizeof(value_type));
        int top = lua_gettop(L);
        serializetable(file, top);
      } else {
        file.write((char *)&name, sizeof(name));
        file.write((char *)&value_type, sizeof(value_type));
      }
      lua_pop(L, 2);
    }
    lua_pop(L, 1);
  }
}

If I just print out all the names of my global variables, I get this:

setmetatable
dofile
os
rawget
require
package
utf8
_G
_VERSION
load
pairs
warn
tostring
type
pcall
collectgarbage
loadfile
rawset
rawequal
assert
print
next
xpcall
string
coroutine
Render
ipairs
Update
Setup
error
select
debug
math
tonumber
rawlen
TESTVALUE1
getmetatable
io
table

So, there are much more than just my TESTVALUE1, Setup, Update, and Render globals. Is there a way to get rid of the others?

I can skip functions by checking with lua_isfunction(L, -1), but some of these are not functions and crash my code.

Upvotes: 0

Views: 46

Answers (2)

BigAgg
BigAgg

Reputation: 43

I fixed it and found the problem. By getting the global table with lua_pushglobaltable(L); i cannot access the global variables with lua_getglobal(L, key.c_str()) anymore i now did it like this:

  lua_pushglobaltable(L);
  int idx = lua_gettop(L);
  lua_pushnil(L);
  while (lua_next(L, -2) != 0) {
    char value_type = 0;
    std::string key = lua_tostring(L, -2);
    size = key.size();
    lua_pushstring(L, key.c_str());
    lua_gettable(L, idx);

this this code the value of the given key is now on top of the stack. Before it was always nil ... therefore my if-else statements were totally ignored and i got errors working with that nil value. now i can just skip every function I encounter.

Upvotes: 0

shingo
shingo

Reputation: 27359

A simple approach is to remap the global environment to an empty table before running your file.

_ENV = setmetatable({}, {__index = _G})

An equivalent C code is:

lua_newtable(L);                                     // new global environment
lua_createtable(L, 0, 1);                            // new metatable
lua_getglobal(L, LUA_GNAME);                         // _G
lua_setfield(L, -2, "__index");                      // { __index = _G }
lua_setmetatable(L, -2);                             // setmetatable(...)
lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); // _ENV = ...

Upvotes: 1

Related Questions