I want to how we can expose properties to lua library.
luaL_openlib( L, kName, kVTable, 1 ); // leave "library" on top of stack
With this, I am able to expose only functions as kVTable refers to luaL_Reg
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
Eg: With the above code. I can do following.
local myLib = require "plugin.myLib"
myLib.newFunc();
However, I want to expose Lua Table to the library as CONSTANTS variable.
myLib.CONSTANTS.SOME_CONST_1
myLib.CONSTANTS.SOME_CONST_2
etc. Please let me know how can I expose lua Table from my library as property.
As luaL_openlib leaves the library table on top on the stack, you can use regular C API to add new fields and subtables to it:
luaL_openlib( L, kName, kVTable, 1 ); // leaves "library" on top of stack
lua_pushstring(L, "CONSTANTS");
lua_newtable(L); // this will be CONSTANTS subtable
lua_pushstring(L, "SOME_CONST_1");
lua_pushnumber(L, 42); // SOME_CONST_1 value
lua_settable(L, -3); // sets SOME_CONST_1
lua_pushstring(L, "SOME_CONST_2");
lua_pushnumber(L, 12345); // SOME_CONST_2 value
lua_settable(L, -3); // sets SOME_CONST_2
lua_settable(L, -3); // sets CONSTANTS table as field of the library table
return 1;
If you use C++, you can use a binding library, such as the header-only luabridge to bind some data to named tables in Lua. Transforming your example into LuaBridge, call this function after you initialize your Lua state:
void register_constants (lua_State* L) {
luabridge::getGlobalNamespace(L)
.beginNamespace("myLib")
.beginNamespace("CONSTANTS")
.addVariable("SOME_CONST_1",&some_const_1,false/*read-only*/)
.addVariable("SOME_CONST_2",&some_const_2,false/*read-only*/)
.endNamespace()
.endNamespace()
;
}
...
lua_State* L=lua_open();
register_constants(L);
...
you can access the constants as your last code snippet
Caveat, lector, because Lua C API is, well, C API.
The whole burden with loadlib and friends was because functions are much harder to pass using C (C funcions aren't first class values). So my best bet would be to set all those constants using regular stack API.
In general, it is supposed to be used to exchange runtime data, of course, but there's no inherent reason why you shouldn't be able to fill your tables with that when loading the module.
Related
I am trying to register a c++ function in Lua.
But getting this error:
CScript.cpp|39|error: argument of type 'int (CScript::)(lua_State*)' does not match 'int (*)(lua_State*)'|
EDIT:
int CApp::SetDisplayMode(int Width, int Height, int Depth)
{
this->Screen_Width = Width;
this->Screen_Height = Height;
this->Screen_Depth = Depth;
return 0;
}
int CScript::Lua_SetDisplayMode(lua_State* L)
{
// We need at least one parameter
int n = lua_gettop(L);
if(n < 0)
{
lua_pushstring(L, "Not enough parameter.");
lua_error(L);
}
int width = lua_tointeger(L, 1);
int height = lua_tointeger(L, 2);
int depth = lua_tointeger(L, 3);
lua_pushinteger(L, App->SetDisplayMode(width, height, depth));
return 0;
}
And in main:
lua_register(L, "setDisplayMode", Lua_SetDisplayMode);
You can not use a method of a class as a normal function, unless it is declared static. You have to define a normal function, which finds out what object you want the method to be called in, and then call the method.
The main reason it's not possible to use a class method as a callback from a C function (and remember that the Lua API is a pure C library), is because the computer doesn't know which object the method should be called on.
The answer is actually surprisingly simple; if you use lua_pushcclosure instead of lua_pushcfunction, you can pass parameters to your called function:
lua_pushlightuserdata(_state, this);
lua_pushcclosure(_state, &MyClass::lua_static_helper, 1);
int MyClass::lua_static_helper(lua_State *state) {
MyClass *klass = (MyClass *) lua_touserdata(state, lua_upvalueindex(1));
return klass->lua_member_method(state);
}
You cannot directly register a C++ non-static member function in Lua using just the basic Lua C API.
However, any of the various mechanisms that exist for easily associating C++ code with Lua will allow you to do so. toLua++, SWIG, Luabind, etc. If you're serious about using C++ objects with Lua, I suggest picking one of those and using it, rather than writing your own version. I personally use Luabind (most of the time; SWIG has its place in the toolbox), as it is the one that doesn't have some form of code generation. It's all done purely in C++, so there's no pre-pass step that generates a C++ source file.
You can work around the limitation by storing your active this pointer in a static variable as well. This introduces the problem of being unable to have two of these classes operating at the same time, but it works.
static CScript* luaThis; // This is a private variable inside CScript.
Then, inside your CScript constructor (or some kind of 'activate' function), you can just specify:
luaThis = this;
Then, when your static functions are called (they can even be private functions if they are registered from within the class), you have access to all your member information via the luaThis pointer.
lua_pushinteger(L, luaThis->App->SetDisplayMode(width, height, depth));
The problem, as I said, is that this restricts you to one active CScript at a time (since another callback from another Lua state would use luaThis while it is pointing to the wrong things). If you need multiple active instances ever, you can come up with some lookup mechanism using the incoming lua_State* as a key.
std::map<lua_State*, CScript*> lookups; // Just an idea, if it comes to this.
Hope that helps!
I'm writing a Lua library in C++ that uses callbacks for certain functionalities. For testing I have 2 Lua functions, Register and Call. They are implemented in C++ like this:
int Lua_Register(lua_State* l){
int n = lua_gettop(l);
if(n==1){
if(lua_isfunction(l, -1)){
printf("Register\n")
lua_pushvalue(l, -1);
r = luaL_ref(l, LUA_REGISTRYINDEX);
}
}
return 1;
}
int Lua_Call(lua_State* l){
lua_rawseti(l, LUA_REGISTRYINDEX, r);
lua_call(l, 0, 0);
return 1;
}
and then in Lua:
Register(function()
Log("hi!")
end)
Call()
But all I see in the console is a lot of lines containing Register, followed by the message: C stack overflow. What I assume the problem is, is that I'm storing Register, rather than the anonymous function in the argument, which would create an infinite loop. How can I solve this?
Basically, You're trying to overwrite Lua registry with non-existant value instead of executing already set value.
What are You doing in short:
--calling Register()
lua_pushvalue(l, -1); -- now 2 copies of closure on top of stack
r = luaL_ref(l, LUA_REGISTRYINDEX); --one consumed, put into registry, 1 left
--calling Call()
--this C API call has got a new piece of stack
-- which does not contain that leftover of closure copy!
lua_rawseti(l, LUA_REGISTRYINDEX, r); --overwrite Lua registry entry with what?
lua_call(l, 0, 0); --What the heck are we calling now?
Thanks siffijoe and Etab Reisner for clarification about that new piece of stack.
What You should be doing:
I still don't really understand what actually are You trying to do, but in Your Lua code sample to execute properly (closure gets called by Call(), You should retrieve the closure before executing instead of overwriting with something which does not exist on top of the Lua stack. Something like this:
int Lua_Call(lua_State* l){
lua_rawgeti(l, LUA_REGISTRYINDEX, r); // <--- retrieve closure from registry!
lua_call(l, 0, 0); // <--- consider using lua_pcall()
return 0; // <--- the call does not leave anything useful on the stack.
}
NOTE: decide, which C API functions of Yours returns something, which ones does not. And change the return value to proper one.
Reference: luaL_ref(), lua_rawgeti(), lua_pcall() and misused lua_rawseti().
In the following example a userdata value is created of type MyType and a table is created with a metafunction __tostring which calls LI_MyType__tostring. The code creates a closure-based lua OOP. My gripe with the example provided is it appears as though there is only one way to associate userdata with a method call, via upvalues. In and of itself, this isn't problematic unless I want to share the same metatable across instances.
In an ideal world - and what I'm hoping to unearth with this question - is there a way to associate an upvalue with a value (e.g. userdata) without associating it with a function call via an upvalue? I'm hoping there is a trick that will let me continue to use closure-based lua OOP and share the same metatable across instances. I'm not optimistic, but I figured I'd ask to see if someone has a suggestion or a non-obvious trick.
using FuncArray = std::vector<const ::luaL_Reg>;
static const FuncArray funcs = {
{ "__tostring", LI_MyType__tostring },
};
int LC_MyType_newInstance(lua_State* L) {
auto userdata = static_cast<MyType*>(lua_newuserdata(L, sizeof(MyType)));
new(userdata) MyType();
// Create the metatable
lua_createtable(L, 0, funcs.size()); // |userdata|table|
lua_pushvalue(L, -2); // |userdata|table|userdata|
luaL_setfuncs(L, funcs.data(), 1); // |userdata|table|
lua_setmetatable(L, -2); // |userdata|
return 1;
}
int LI_MyType__tostring(lua_State* L) {
// NOTE: Blindly assume that upvalue 1 is my userdata
const auto n = lua_upvalueindex(1);
lua_pushvalue(L, n); // |userdata|
auto myTypeInst = static_cast<MyType*>(lua_touserdata(L, -1));
lua_pushstring(L, myTypeInst->str()); // |userdata|string|
return 1; // |userdata|string|
}
I'm hoping there's a way of performing something like (this is pseudo-code!):
// Assume that arg 1 is userdata
int LI_MyType__tostring(lua_State* L) {
const int stackPosition = -1;
const int upvalueIndex = 1;
const auto n = lua_get_USERDATA_upvalue(L, stackPosition, upvalueIndex);
lua_pushvalue(L, n); // |userdata|
auto myTypeInst = static_cast<MyType*>(lua_touserdata(L, -1));
lua_pushstring(L, myTypeInst->str()); // |userdata|string|
return 1; // |userdata|string|
}
I know this is similar to how things would be for the "normal" metatable style of OOP, but I want to keep things closure based and avoid introducing the colon syntax.
Another way of asking this question would be, is there a way to share metatables across userdata instances while using a closure-based OOP? Using lua's syntax from the scripting side of things, I don't think it's possible, but I'm hoping there's something that can be done on the C side of things.
UPDATE (2013-10-10): Based on #lhf's answer to use lua_setuservalue() and lua_getuservalue() the protocol I've settled on which allows me to reuse metatables is this:
Register a single metatable object using luaL_newmetatable(). This metatable can now be shared across userdata instances because no upvalues are used when registering the metatable.
Create a userdata value (lua_newuserdata()).
Assign the correct metatable to the userdata value (lua_setmetatable()).
Create and populate an instance method calls/attributes table with one upvalue, the userdata.
Use lua_setuservalue() on userdata to store a reference to the per-instance attribute/method table.
Change various metamethods (e.g. __index) to use the userdata's uservalue table.
As a consequence:
upvalues are never used in metamethods
upvalues are only used in a value's instance methods
there is only one extra table per instance of a given class
It's still not possible to escape creating a method/attribute table per userdata, but that overhead is nominal. It would be nice if obj.myMethod() would pass obj to function myMethod() somehow without using :, but that's exactly what : does because this isn't possible another way (unless you do make use of an upvalue).
lua_setuservalue seems to be exactly what you need. There is also of course lua_getuservalue.
(I'm skipping the C++ code and answering the question in the title.)
I don't think you should be trying to do it exactly, for a few reasons.
If you call object.method(), and you try to infer the object from the instance that was created, you're blocking your ability to pass function pointers around that behave on any object given.
You have cyclic references to objects that will never get garbage collected (each instance's function pointing back to the instance).
Just get the object from slot 1, and check its type matches your userdata. (luaL_checkudata)
If its not an object and tostring is called for example, just output that its a class of object name, instead of the instance details. It makes far more sense, and may well make debugging simpler if the object reports what it actually is, rather than trying to be too clever and misleading you.
This is probably an easy question, but I am stumped. This is for Lua 5.1.
I have a script which runs in its own environment. In that environment, I have a variable called "plugin" I set from C++ like so:
lua_getfield(L, LUA_REGISTRYINDEX, getScriptId()); // Put script's env table onto the stack -- env_table
lua_pushstring(L, "plugin"); // -- env_table, "plugin"
luaW_push(L, this); // -- env_table, "plugin", *this
lua_rawset(L, -3); // env_table["plugin"] = *this -- env_table
lua_pop(L, -1); // Cleanup -- <<empty stack>>
Before running my Lua script, I set the function environment like so:
lua_getfield(L, LUA_REGISTRYINDEX, getScriptId()); // Push REGISTRY[scriptId] onto stack -- function, table
lua_setfenv(L, -2); // Set that table to be the env for function -- function
When my script runs, it can see and interact with the plugin variable, as expected. So far, so good.
At one point, the Lua script calls a C++ function, and in that function, I want to see if the plugin variable is set.
I have tried many many things, and I can't seem to see the plugin variable. Here are but 4 things I tried:
lua_getfield(L, LUA_ENVIRONINDEX, "plugin");
bool isPlugin = !lua_isnil(L, -1);
lua_pop(L, 1); // Remove the value we just added from the stack
lua_getfield(L, LUA_GLOBALSINDEX, "plugin");
bool isPlugin2 = !lua_isnil(L, -1);
lua_pop(L, 1); // Remove the value we just added from the stack
lua_getglobal(L, "plugin");
bool isPlugin3 = !lua_isnil(L, -1);
lua_pop(L, 1); // Remove the value we just added from the stack
lua_pushstring(L, "plugin");
bool isPlugin4 = lua_isuserdata(L, -1);
lua_pop(L, 1);
Unfortunately, all the isPlugin variables return false. It is as if the C++ function called from Lua cannot see a variable set in a Lua environment.
Any idea how I can see the plugin variable from C++?
Thanks!
Every function in Lua has it's own environment. They don't inherit the environment of whomever calls them. So if your C++ function doesn't use the environment that has this plugin variable, then it won't see it.
You could pass the environment to the C function as part of the closure (see lua_pushcclosure). I don't know the kind of setup you have, but I can see three ways this can pan out:
1) Your C function is registered in the same environment as the function - good, will work.
2) Your C function is registered in the global environment but the Lua functions which will call it all reside in one specific environment - will still work if the environment exists when the function is registered (so it can be added to the closure).
3) Your C function is registered in the global environment and can be called by different Lua functions working in different environments - will not work anymore.
If it's 2 or 3, there might be no drawbacks if you change the implementation to use variant 1.
Edit: Alright, so that won't work. There is a way to obtain under-the-hood information if you are willing to stray a bit from the Lua API. DISCLAIMER: I am working with 5.2, so I am trying to adapt my methods for 5.1. I could not test this, and it might not work.
First you will need to #include "lstate.h"
This is the lua_State structure in 5.1:
struct lua_State {
CommonHeader;
lu_byte status;
StkId top; /* first free slot in the stack */
StkId base; /* base of current function */
global_State *l_G;
CallInfo *ci; /* call info for current function */
const Instruction *savedpc; /* `savedpc' of current function */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
CallInfo *end_ci; /* points after end of ci array*/
CallInfo *base_ci; /* array of CallInfo's */
int stacksize;
int size_ci; /* size of array `base_ci' */
unsigned short nCcalls; /* number of nested C calls */
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
TValue l_gt; /* table of globals */
TValue env; /* temporary place for environments */
GCObject *openupval; /* list of open upvalues in this stack */
GCObject *gclist;
struct lua_longjmp *errorJmp; /* current error recover point */
ptrdiff_t errfunc; /* current error handling function (stack index) */
};
Let's assume L is your lua_State*.
As you can see, L->ci holds the current callinfo, and the callinfo array is contained between L->base_ci and L->end_ci.
So the Lua function which called your C function is situated at (L->end_ci-2) (which should be the same as (L->ci-1)), and its stack id (StkId) is (L->end_ci-2)->func.
We can trick the Lua API into letting you work with stack ids which are below the current calling function by doing something like this:
StkId saved = L->base;
L->base = L->base_ci->base;
int idx = (L->end_ci-2)->func - L->base+1;
lua_getfenv(L, idx);
L->base = saved;
The environment table should be on top of the stack now.
Edit: The Lua API checks for a valid index are a bit tricky. This should fool them.
I am trying to register a c++ function in Lua.
But getting this error:
CScript.cpp|39|error: argument of type 'int (CScript::)(lua_State*)' does not match 'int (*)(lua_State*)'|
EDIT:
int CApp::SetDisplayMode(int Width, int Height, int Depth)
{
this->Screen_Width = Width;
this->Screen_Height = Height;
this->Screen_Depth = Depth;
return 0;
}
int CScript::Lua_SetDisplayMode(lua_State* L)
{
// We need at least one parameter
int n = lua_gettop(L);
if(n < 0)
{
lua_pushstring(L, "Not enough parameter.");
lua_error(L);
}
int width = lua_tointeger(L, 1);
int height = lua_tointeger(L, 2);
int depth = lua_tointeger(L, 3);
lua_pushinteger(L, App->SetDisplayMode(width, height, depth));
return 0;
}
And in main:
lua_register(L, "setDisplayMode", Lua_SetDisplayMode);
You can not use a method of a class as a normal function, unless it is declared static. You have to define a normal function, which finds out what object you want the method to be called in, and then call the method.
The main reason it's not possible to use a class method as a callback from a C function (and remember that the Lua API is a pure C library), is because the computer doesn't know which object the method should be called on.
The answer is actually surprisingly simple; if you use lua_pushcclosure instead of lua_pushcfunction, you can pass parameters to your called function:
lua_pushlightuserdata(_state, this);
lua_pushcclosure(_state, &MyClass::lua_static_helper, 1);
int MyClass::lua_static_helper(lua_State *state) {
MyClass *klass = (MyClass *) lua_touserdata(state, lua_upvalueindex(1));
return klass->lua_member_method(state);
}
You cannot directly register a C++ non-static member function in Lua using just the basic Lua C API.
However, any of the various mechanisms that exist for easily associating C++ code with Lua will allow you to do so. toLua++, SWIG, Luabind, etc. If you're serious about using C++ objects with Lua, I suggest picking one of those and using it, rather than writing your own version. I personally use Luabind (most of the time; SWIG has its place in the toolbox), as it is the one that doesn't have some form of code generation. It's all done purely in C++, so there's no pre-pass step that generates a C++ source file.
You can work around the limitation by storing your active this pointer in a static variable as well. This introduces the problem of being unable to have two of these classes operating at the same time, but it works.
static CScript* luaThis; // This is a private variable inside CScript.
Then, inside your CScript constructor (or some kind of 'activate' function), you can just specify:
luaThis = this;
Then, when your static functions are called (they can even be private functions if they are registered from within the class), you have access to all your member information via the luaThis pointer.
lua_pushinteger(L, luaThis->App->SetDisplayMode(width, height, depth));
The problem, as I said, is that this restricts you to one active CScript at a time (since another callback from another Lua state would use luaThis while it is pointing to the wrong things). If you need multiple active instances ever, you can come up with some lookup mechanism using the incoming lua_State* as a key.
std::map<lua_State*, CScript*> lookups; // Just an idea, if it comes to this.
Hope that helps!