Reading Lua nested tables in C++ - c++

I'm creating a C/C++ function which will be called from Lua. My function must call a library function who's signature is like this:
void libFunction( int val1, int val2, tSETTINGS * pSettings );
I'm given these C/C++ structs:
typedef struct
{
int cmd;
int arg;
} tCOMMAND;
typedef struct
{
int numberCommands;
int id;
tCOMMAND commands[1];
} tSETTINGS;
Maybe my thinking is all wrong on this, but from Lua I'm calling like this:
id = 42
val1 = 1
val2 = 2
cmd1 = { 3, 4 }
cmd2 = { 5, 6 }
commands = { cmd1, cmd2 }
settings = { #commands, id, commands }
mycfunction( val1, val2, settings )
I'm sure that I'm still not understanding the Lua stack as referenced from C++, since what I'm trying just doesn't work. My solution is able to retrieve val1, val2, #commands and id, but when I try to retrieve commands[0] and commands[1] I get {1, 2} and {2, 42} respectively.
My C++ is essentially like this (for this sample I'm discarding the values). I've already retrieved val1 and val2:
int stkNdx = 1;
lua_rawgeti(L, 3, stkNdx++ );
int numcmds = lua_tointeger(L, -1); // this successfully retrieves numberCommands 2
lua_pop(L, 1);
lua_rawgeti(L, 3, stkNdx++ );
int id = lua_tointeger(L, -1); // this successfully retrieves id 42
lua_pop(L, 1);
lua_pushvalue(L, -1 );
lua_pushnil(L);
int cmdNbr = 0;
for( lua_next(L, -2); cmdNbr < numcmds; cmdNbr++ )
{
lua_pushvalue(L, -2);
int cmd = lua_tointeger(L, -1);
int arg = lua_tointeger(L, -1);
lua_pop(L, 2);
lua_next(L, -2);
}
lua_pop(L, 1);
I've tried various permutations of lua_rawgeti() followed by lua_tonumber() and lua_pop(), with basically the same result.
This seems similar to this question, and my solution is modeled after that with no success.
Experimenting more I inserted this:
lua_pushnil(L);
while( lua_next(L, -2) )
{
if( ! lua_istable(L, -1) )
{
int v = lua_tointeger(L, -1);
}
lua_pop(L, 1);
}
This loop executes 4 times. The first 2 times the values 2 and 42 are assigned to v. The next 2 iterations skip the assignment (lua_istable returned true). So it seems that although I've already retrieved numcmds and id, they're still there on the stack. I also clearly don't understand how to iterate over the subtables when they're encountered.

Lua table indices range from [1 .. N] instead of [0 .. N-1].
Your loop should be:
int cmdNbr = 1;
for( lua_next(L, -2); cmdNbr <= numcmds; cmdNbr++ )
{
...
}
or as I prefer it:
lua_rawgeti(L, 3, 2 );
int id = lua_tointeger(L, -1); // this successfully retrieves id 42
lua_pop(L, 1);
lua_rawgeti(L, 3, 3);
{
// commands table at stack top
size_t N = lua_objlen(L,-1); // size of the table
for (int i = 1; i <= N; ++i)
{
lua_rawgeti(L,-1, i); // cmd# at stack top
{
lua_rawgeti(L,-1,1); // first entry
int cmd = lua_tointeger(L,-1);
lua_pop(L,1);
lua_rawgeti(L,-1,2); // second entry
int arg = lua_tointeger(L,-1);
lua_pop(L,1);
}
lua_pop(L, 1); // pop cmd#
}
}
lua_pop(L, 1); // pop commands table
Note that, with the function lua_objlen(L,idx), it's not necessary to pass numcmds.

Related

C++ Lua embedding, pushing table to a function that on arguments

so i was trying to push a table on a function inside arguments
*lua
function test1(varlist)
print(varlist[1])
print(varlist[2])
print(varlist[3])
end
addHook("string", "string2", test1)
*cpp
static int lua_addHook(lua_State* L) {
if (lua_isstring(L, 1) && lua_isstring(L, 2) && lua_isfunction(L, 3)) {
lua_newtable(L);
lua_newtable(L);
for (size_t i = 0; i < 3; ++i) {
lua_pushinteger(L, i + 1);
lua_pushstring(L, string("string varlist: " + to_string(i)).c_str());
lua_settable(L, -3);
}
if (lua_pcall(L, 1, 0, 0) != 0) {
printf("error: %s\n", lua_tostring(L, -1));
lua_pop(L, 1);
}
}
return 1;
}
so it should printing
string varlist: 0
string varlist: 1
string varlist: 2
but i keep getting error "attempt to call a table value"
u know what the problem is?
The stack looks like this at the point of lua_pcall:
table (constructed by the loop above) # STACK TOP and arg1 to function call
table (empty) # interpreted as the function to call
function test1
string "string1"
string "string"
Getting rid of one of the lua_newtable calls should fix it.

How can I read and write to nested lua tables from C++?

I have a nested table in my lua code that I want to pass to C++ so the native code can manipulate it:
-- Some persistent data in my game
local data = {
{ 44, 34, 0, 7, },
{ 4, 4, 1, 3, },
}
-- Pass it into a C++ function that can modify the input data.
TimelineEditor(data)
How do I write my C++ code to read the nested table and modify its values?
Reading Lua nested tables in C++ and lua c read nested tables both describe how I can read from nested tables, but not how to write to them.
Short answer
Lua uses a stack to get values in and out of tables. To modify table values you'll need to push the table you want to modify with lua_rawgeti, push a value you want to insert with lua_pushinteger, and then set the value in the table with lua_rawseti.
When writing this, it's important to visualize the stack to ensure you use the right indexes:
lua_rawgeti()
stack:
table
lua_rawgeti()
stack:
number <-- top of the stack
table
lua_tonumber()
stack:
number
table
lua_pop()
stack:
table
lua_pushinteger()
stack:
number
table
lua_rawseti()
stack:
table
Negative indexes are stack positions and positive indexes are argument positions. So we'll often pass -1 to access the table at the stack. When calling lua_rawseti to write to the table, we'll pass -2 since the table is under the value we're writing.
Example
I'll add inspect.lua to the lua code to print out the table values so we can see that the values are modified.
local inspect = require "inspect"
local data = {
{ 44, 34, 0, 7, },
{ 4, 4, 1, 3, },
}
print("BEFORE =", inspect(data, { depth = 5, }))
TimelineEditor(data)
print("AFTER =", inspect(data, { depth = 5, }))
Assuming you've figured out BindingCodeToLua, you can implement the function like so:
// Replace LOG with whatever you use for logging or use this:
#define LOG(...) printf(__VA_ARGS__); printf("\n")
// I bound with Lunar. I don't think it makes a difference for this example.
int TimelineEditor(lua_State* L)
{
LOG("Read the values and print them out to show that it's working.");
{
int entries_table_idx = 1;
luaL_checktype(L, entries_table_idx, LUA_TTABLE);
int n_entries = static_cast<int>(lua_rawlen(L, entries_table_idx));
LOG("%d entries", n_entries);
for (int i = 1; i <= n_entries; ++i)
{
// Push inner table onto stack.
lua_rawgeti(L, entries_table_idx, i);
int item_table_idx = 1;
luaL_checktype(L, -1, LUA_TTABLE);
int n_items = static_cast<int>(lua_rawlen(L, -1));
LOG("%d items", n_items);
for (int i = 1; i <= n_items; ++i)
{
// Push value from table onto stack.
lua_rawgeti(L, -1, i);
int is_number = 0;
// Read value
int x = static_cast<int>(lua_tonumberx(L, -1, &is_number));
if (!is_number)
{
// fire an error
luaL_checktype(L, -1, LUA_TNUMBER);
}
LOG("Got: %d", x);
// pop value off stack
lua_pop(L, 1);
}
// pop table off stack
lua_pop(L, 1);
}
}
LOG("Overwrite the values");
{
int entries_table_idx = 1;
luaL_checktype(L, entries_table_idx, LUA_TTABLE);
int n_entries = static_cast<int>(lua_rawlen(L, entries_table_idx));
LOG("%d entries", n_entries);
for (int i = 1; i <= n_entries; ++i)
{
// Push inner table onto stack.
lua_rawgeti(L, entries_table_idx, i);
int item_table_idx = 1;
luaL_checktype(L, -1, LUA_TTABLE);
int n_items = static_cast<int>(lua_rawlen(L, -1));
LOG("%d items", n_items);
for (int j = 1; j <= n_items; ++j)
{
int x = j + 10;
// Push new value onto stack.
lua_pushinteger(L, x);
// rawseti pops the value off. Need to go -2 to get to the
// table because the value is on top.
lua_rawseti(L, -2, j);
LOG("Wrote: %d", x);
}
// pop table off stack
lua_pop(L, 1);
}
}
// No return values
return 0;
}
Output:
BEFORE = { { 44, 34, 0, 7 }, { 4, 4, 1, 3 } }
Read the values and print them out to show that it's working.
2 entries
4 items
Got: 44
Got: 34
Got: 0
Got: 7
4 items
Got: 4
Got: 4
Got: 1
Got: 3
Overwrite the values
2 entries
4 items
Wrote: 11
Wrote: 12
Wrote: 13
Wrote: 14
4 items
Wrote: 11
Wrote: 12
Wrote: 13
Wrote: 14
AFTER = { { 11, 12, 13, 14 }, { 11, 12, 13, 14 } }

Sharing global variables between different Lua states through require

I'm trying to find a way to share global variables of a specific Lua script(test.lua in the example) between different Lua states.
Here's my simple example code:
In test.lua
num = 2
In main.cpp
#include <iostream>
#include <lua.hpp>
int main()
{
lua_State *L1 = luaL_newstate(); //script A
luaL_openlibs(L1);
lua_settop(L1, 0);
luaL_dostring(L1, "require('test') num = 5");
lua_State *L2 = luaL_newstate(); //script B
luaL_openlibs(L2);
lua_settop(L2, 0);
luaL_dostring(L2, "require('test') print(num)");
lua_close(L1);
lua_close(L2);
}
I expect to get 5 but I get 2.
Is not possible to share global variables between different lua_State* through require?
ADDED :
If it's not possible, would it be a good idea to open test.lua using luaL_loadfile and then create getter/setter methods in C++ to share variable num between script A and B?
For example like this,
Script A:
script = my.Script("test")
script:setVar("num", 5)
Script B:
script = my.Script("test")
print(script:getVar("num"))
I wonder what you think about this design as an alternative to require.
Two distinct lua_States are completely and totally independent. One cannot directly affect anything that happens in another. You can expose some C code to one that allows it to modify the other, or they could both access some external resource (a file, for example) that allows them to share data.
But outside of things like this, no, they cannot interact.
The preferred method for this is to not make them separate lua_States.
Rather than having the global value in a Lua module, you could push a pointer to a C++ value as an upvalue for a metatable to a table which contains those globals. Then you push the globals table with the same metatable to both VMs. When you now access globals.num the getglobal and setglobal metamethods are triggered (depending on whether you read or write). These will update the value on the C++ side, such that it is shared between the two VMs.
N.B.: As you can judge from the lengthy boilerplate this is not a good solution. You should avoid having multiple VMs at the same time. If you require multiple VMs for concurrency purposes, consider using a mature library like Lua Lanes rather than rolling your own (doing this right requires several thousands of lines of code).
#include <string>
#include <lua.hpp>
int setglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = lua_tostring(L, 2);
luaL_argcheck(L, key == "num", 2, "unknown global");
int value = luaL_checkinteger(L, 3);
luaL_argcheck(L, lua_isnumber(L, 3), 3, "not a number");
int *num = static_cast<int *>(lua_touserdata(L, lua_upvalueindex(1)));
*num = value;
lua_pop(L, 1);
return 0;
}
int getglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = lua_tostring(L, 2);
luaL_argcheck(L, key == "num", 2, "unknown global");
int num = *static_cast<int *>(lua_touserdata(L, lua_upvalueindex(1)));
lua_pop(L, 1);
lua_pushinteger(L, num);
return 1;
}
static const struct luaL_Reg globals_meta[] = {
{"__newindex", setglobal},
{"__index", getglobal},
{nullptr, nullptr} // sentinel
};
int main() {
int num = 2;
// script A
lua_State *L1 = luaL_newstate();
luaL_openlibs(L1);
luaL_newmetatable(L1, "globals_meta");
lua_pushlightuserdata(L1, &num);
luaL_setfuncs(L1, globals_meta, 1);
lua_newuserdata(L1, 0);
luaL_getmetatable(L1, "globals_meta");
lua_setmetatable(L1, -2);
lua_setglobal(L1, "globals");
luaL_dostring(L1, "print('Script A: ' .. globals.num) globals.num = 5");
// script B
lua_State *L2 = luaL_newstate();
luaL_openlibs(L2);
luaL_newmetatable(L2, "globals_meta");
lua_pushlightuserdata(L2, &num);
luaL_setfuncs(L2, globals_meta, 1);
lua_newuserdata(L2, 0);
luaL_getmetatable(L2, "globals_meta");
lua_setmetatable(L2, -2);
lua_setglobal(L2, "globals");
luaL_dostring(L2, "print('Script B: ' .. globals.num)");
lua_close(L1);
lua_close(L2);
}
As a challange to myself I implemented a complete global table which can communicate values of type nil, bool, int, double, and string between two Lua states. They can be named with everything that has a string representation.
-- To be on the safe side, just use numbers and strings as keys
globals[1] = "x"
globals.num = 5
-- Be careful when using table or function literals as keys
-- Two empty tables don't have the same representation
globals[{}] = 2 -- "table: 0x10d55a0" = 2
globals[{}] = 1 -- "table: 0x10ce2c0" = 1
I haven't checked all sorts of exceptional situations exhaustively, so no refunds!
#include <iostream>
#include <string>
#include <unordered_map>
#include <boost/variant.hpp>
#include <lua.hpp>
enum class nil {};
using Variant = boost::variant<nil, bool, int, double, std::string>;
int setglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = luaL_tolstring(L, 2, nullptr);
auto &globals = *static_cast<std::unordered_map<std::string, Variant> *>(
lua_touserdata(L, lua_upvalueindex(1)));
Variant &v = globals[key];
switch (lua_type(L, 3)) {
case LUA_TNIL:
v = nil{};
break;
case LUA_TBOOLEAN:
v = static_cast<bool>(lua_toboolean(L, 3));
lua_pop(L, 1);
break;
case LUA_TNUMBER:
if (lua_isinteger(L, 3)) {
v = static_cast<int>(luaL_checkinteger(L, 3));
} else {
v = static_cast<double>(luaL_checknumber(L, 3));
}
lua_pop(L, 1);
break;
case LUA_TSTRING:
v = std::string(lua_tostring(L, 3));
lua_pop(L, 1);
break;
default:
std::string error = "Unsupported global type: ";
error.append(lua_typename(L, lua_type(L, 3)));
lua_pushstring(L, error.c_str());
lua_error(L);
break;
}
return 0;
}
int getglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = luaL_tolstring(L, 2, nullptr);
auto globals = *static_cast<std::unordered_map<std::string, Variant> *>(
lua_touserdata(L, lua_upvalueindex(1)));
lua_pop(L, 1);
auto search = globals.find(key);
if (search == globals.end()) {
lua_pushstring(L, ("unknown global: " + key).c_str());
lua_error(L);
return 0;
}
Variant const &v = search->second;
switch (v.which()) {
case 0:
lua_pushnil(L);
break;
case 1:
lua_pushboolean(L, boost::get<bool>(v));
break;
case 2:
lua_pushinteger(L, boost::get<int>(v));
break;
case 3:
lua_pushnumber(L, boost::get<double>(v));
break;
case 4:
lua_pushstring(L, boost::get<std::string>(v).c_str());
break;
default: // Can't happen
std::abort();
break;
}
return 1;
}
static const struct luaL_Reg globals_meta[] = {
{"__newindex", setglobal},
{"__index", getglobal},
{nullptr, nullptr} // sentinel
};
int main() {
std::unordered_map<std::string, Variant> globals;
globals["num"] = 2;
// script A
lua_State *L1 = luaL_newstate();
luaL_openlibs(L1);
luaL_newmetatable(L1, "globals_meta");
lua_pushlightuserdata(L1, &globals);
luaL_setfuncs(L1, globals_meta, 1);
lua_newuserdata(L1, 0);
luaL_getmetatable(L1, "globals_meta");
lua_setmetatable(L1, -2);
lua_setglobal(L1, "globals");
if (luaL_dostring(L1, "print('Script A: ' .. globals.num)\n"
"globals.num = 5") != 0) {
std::cerr << "L1:" << lua_tostring(L1, -1) << '\n';
lua_pop(L1, 1);
}
// script B
lua_State *L2 = luaL_newstate();
luaL_openlibs(L2);
luaL_newmetatable(L2, "globals_meta");
lua_pushlightuserdata(L2, &globals);
luaL_setfuncs(L2, globals_meta, 1);
lua_newuserdata(L2, 0);
luaL_getmetatable(L2, "globals_meta");
lua_setmetatable(L2, -2);
lua_setglobal(L2, "globals");
if (luaL_dostring(L2, "print('Script B: ' .. globals.num)") != 0) {
std::cerr << "L1:" << lua_tostring(L2, -1) << '\n';
lua_pop(L2, 1);
}
lua_close(L1);
lua_close(L2);
}
While Lua states are separate by default, some binding libraries expose functionality to transfer information from one to the other.
For example, in sol, there are methods to serialize fairly arbitrary Lua data, including functions, to C++ data. You can then de-serialize that data into another Lua state, to effectively copy it (code link).
But you still will have two copies, in the end. You can't modify one Lua state from another directly.
Your last point, about exposing some getter/setter, is valid. You can have some data stored in C/C++ and have two different Lua states able to access it. You still have to bind that data to each VM separately.

How to store a value type in a userdata?

This SO article is the same thing, but the answer is unhelpful because the answer was in Lua and the question was about the C-API. So I'm asking again. Hopefully, others will benefit from this question.
I'm actually having 2 problems (I can't get y an z to work, and I can't get helloworld() to work)
I'm trying to get to this:
local x = MyCBoundLib.GetSomething()
print(x.y)
print(x.z)
Where x is a userdata. I keep getting attempt to index a userdata value
I know that "userdata isn't indexable without a metatable because it's C/C++ data"
In my C-code, I do something like this to try and wrap the object.
int push_Something(lua_State *L, void *object)
{
struct SomethingWrapper *w = (struct SomethingWrapper *)lua_newuserdata(L, sizeof(struct SomethingWrapper));
w->object = object;
luaL_setmetatable(L, "Something");
return 1;
}
Earlier, I tried to register a metatable called Something, like so:
luaL_newmetatable(L, "Something");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, some_funcs, 0);
lua_pop(L, 1);
Where some_funcs has:
static luaL_Reg const some_funcs [] =
{
{ "helloworld", l_helloworld },
{ NULL, NULL }
};
If I try print(x.helloworld()), I get the same error: attempt to index a userdata value
In none of my code do I know how to properly attach the value type y or z.
First, for helloworld your code works:
/* file: hw.c
* on Debian/Ubuntu compile with:
* `gcc -I/usr/include/lua5.2 -fpic -shared -o hw.so hw.c`
*/
#include <lua.h>
#include <lauxlib.h>
struct SomethingWrapper {
void *object;
};
static int l_helloworld(lua_State *L) {
lua_pushliteral(L, "Hello World!");
return 1;
}
static luaL_Reg const some_funcs[] = {
{ "helloworld", l_helloworld },
{ NULL, NULL }
};
int push_Something(lua_State *L, void *object) {
struct SomethingWrapper *w = lua_newuserdata(L, sizeof(*w));
w->object = object;
luaL_setmetatable(L, "Something");
return 1;
}
int luaopen_hw(lua_State *L) {
luaL_newmetatable(L, "Something");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, some_funcs, 0);
lua_pop(L, 1);
push_Something(L, NULL);
return 1;
}
and the test script:
-- file: hwtest.lua
local x = require( "hw" )
print( x.helloworld() )
Output is:
Hello World!
For accessing properties on a userdata you need to set __index to a function instead of a table. The function is called with two arguments (the userdata, and the key) whenever you try to access a field on the userdata, and you can query your C object and push the desired result.
It gets a little more complicated if you intend to support methods and properties at the same time, but the basic approach is the following: You use a function as __index metamethod. This function has access to a table of methods (e.g. via an upvalue or the registry, etc.) and tries to lookup the given key in that table. If you succeed you return that value. If you come up with nothing, you compare the given key to the valid property names of your C object and return the respective values if you get a match. (If you don't get a match you can return nil or raise an error -- that's up to you.)
Here is a reusable implementation of that approach (extracted from the moon toolkit):
static int moon_dispatch( lua_State* L ) {
lua_CFunction pindex;
/* try method table first */
lua_pushvalue( L, 2 ); /* duplicate key */
lua_rawget( L, lua_upvalueindex( 1 ) );
if( !lua_isnil( L, -1 ) )
return 1;
lua_pop( L, 1 );
pindex = lua_tocfunction( L, lua_upvalueindex( 2 ) );
return pindex( L );
}
MOON_API void moon_propindex( lua_State* L, luaL_Reg const methods[],
lua_CFunction pindex, int nups ) {
if( methods != NULL ) {
luaL_checkstack( L, nups+2, "not enough stack space available" );
lua_newtable( L );
for( ; methods->func; ++methods ) {
int i = 0;
for( i = 0; i < nups; ++i )
lua_pushvalue( L, -nups-1 );
lua_pushcclosure( L, methods->func, nups );
lua_setfield( L, -2, methods->name );
}
if( pindex ) {
lua_pushcfunction( L, pindex );
if( nups > 0 ) {
lua_insert( L, -nups-2 );
lua_insert( L, -nups-2 );
}
lua_pushcclosure( L, moon_dispatch, 2+nups );
} else if( nups > 0 ) {
lua_replace( L, -nups-1 );
lua_pop( L, nups-1 );
}
} else if( pindex ) {
lua_pushcclosure( L, pindex, nups );
} else {
lua_pop( L, nups );
lua_pushnil( L );
}
}
Usage:
/* [ -nup, +1, e ] */
void moon_propindex( lua_State* L,
luaL_Reg const* methods,
lua_CFunction index,
int nup );
This function is used for creating an __index metafield. If index is NULL but methods is not, a table containing all the functions in methods is created and pushed to the top of the stack. If index is not NULL, but methods is, the index function pointer is simply pushed to the top of the stack. In case both are non NULL, a new C closure is created and pushed to the stack, which first tries to lookup a key in the methods table, and if unsuccessful then calls the original index function. If both are NULL, nil is pushed to the stack. If nup is non-zero, the given number of upvalues is popped from the top of the stack and made available to all registered functions. (In case index and methods are not NULL, the index function receives two additional upvalues at indices 1 and 2.) This function is used in the implementation of moon_defobject, but maybe it is useful to you independently.
If you try to store arbitrary Lua values in a userdata (like the title suggests) -- you can't. But you can associate an extra table (called "uservalue") with each userdata and store arbitrary values there. The approach is similar to the one above, but instead of matching with predefined property names and accessing the C object directly you first push the uservalue table (using lua_getuservalue) and then lookup your field there.

Get lua table entry from C via integer key

I am currently using the following code to get a value from a table (cstring = const char*):
template<>
cstring luaTable::get(cstring name) {
prep_get(name); // puts table[name] at -1 in stack
cstring result;
if(!lua_isstring(L, -1)) {
report(name, "is not a string");
result = "";
}
else {
result = lua_tostring(L, -1);
}
lua_pop(L, 1);
return result;
}
void luaTable::prep_get(cstring name) {
lua_pushstring(L, name); // name at -1, table at -2
lua_gettable(L, -2);
// table[name] is now at position -1 in stack
}
This works perfectly for tables of form table = {a=10, b=2}. How can I modify it to get values from tables without keys such as table = {10, 2}?
I'm sure I'm missing something simple but can't seem to find the answer.
Thanks in advance,
Ben
Edit: added some pops
Okay sorry to answer my own question so soon - but a quick flash of inspiration lead to:
void luaTable::prep_get(cstring name) {
lua_pushstring(L, name); // name string at -1
if(lua_isnumber(L, -1)) { // call prep_get("i") for ith element etc
int key = lua_tonumber(L, -1);
lua_pop(L, 1); // remove the name string from -1
lua_pushnumber(L, key); // push name number to -1
}
lua_gettable(L, -2);
// result is now at position -1 in stack
}
which works as desired.
#user1483596 I don't think that solution would work. lua_isnumber will only return true if the value is of type number, and you just pushed a string, so it will always return false.
Instead, try something like this:
void luaTable::prep_get(cstring name) {
int num = strtol(name, 0, 0);
if (num > 0) {
lua_pushnumber(L, num);
} else {
lua_pushstring(L, name);
}
lua_gettable(L, -2);
}
Bear in mind though that it won't handle a special case. In Lua a[1] and a["1"] are different. If you use this function, you'll always treat numbers as array indices, even if they're not.
If you want to differentiate both cases, then you could overload prep_get and take a number.