Jump to content

  • Log In with Google      Sign In   
  • Create Account


lua_pcall using luaL_loadstring


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
8 replies to this topic

#1 falconmick   Members   -  Reputation: 80

Like
0Likes
Like

Posted 10 April 2012 - 10:28 AM

ok, I've been working on a helper function to help simplify calling a lua function (so my group members who havn't spent 4 days beating their head against the keyboard can use it)

only problem... when I call
lua_getglobal(getLuaState(instEnum), funcName.c_str());
(getLuaState returns the desired lua_state and funcName = the function wanting to be added) it returns 0 (lua.h defined as: #define LUA_TNIL 0) aka it isn't accepting the lua files function add...
ok, this is EXACTLY what happens (aka my source)
.....
        // lua_States are defiend above and are working on code that just executes a string
		// iczOpener opens a zip and extracts char buffers of the data inside of the zip
IczOpener zipInstance("test.icz");
int fileSize = zipInstance.getFileLength("compTest4.lua");
char *buffer;
buffer = new char[fileSize];
zipInstance.getFileStream("compTest4.lua", buffer, fileSize);

int arguments[2];
arguments[0] = 2;
arguments[1] = 5;
//*arguments = new int[2];
//*arguments = tmpArgs;

// order MUST be compileLua -> pushLuaFunc -> pushVar -> doLua
int testing = LuaInstance::getInstance()->compileLua(buffer, LuaInstance::construction);
LuaInstance::getInstance()->pushLuaFunc(LuaInstance::construction, "add");
LuaInstance::getInstance()->pushVar(LuaInstance::construction, &arguments[0]);
LuaInstance::getInstance()->pushVar(LuaInstance::construction, &arguments[1]);
//compileLua

int reternedData;
reternedData = (int)(LuaInstance::getInstance()->doLua(2, true, LuaInstance::construction));
.....

and here is luaInstance:
H:
#ifndef LUAINSTANCE_H
#define LUAINSTANCE_H
#include <map>
#include <iostream>
#include <lua.hpp>
#include <luabind/luabind.hpp>
#include "CompCompiler.h"
#include "keyBinds.h"
/** \class LuaInstance
* \brief holds several instances of lua_State and grants access to them, using namespaces would have probably been a better
*		 solution, but there is insufficient information on how they work for me to be confident to use them properly
* @author Michael Crook
* @date 6/4/2012
*/
class LuaInstance
{
public:
// variables
  enum instID {
   construction = 0,
   keyboard = 1
  }; /**< instID enum, this is used to make calling of an instance more readable.. that way when you call an instance, other people reading your code will understand which instance it is */
// functions
  /**
  * default constructor... creates lua instances and places them into states.
  * if you want another luaState you have to add it in here! also don't forget to modify the instID enum
  */
  LuaInstance();
  ~LuaInstance();
  /**
  * LuaInstance is a singleton, this call will allow you to access LuaInstance
  * @return the lua instnace
  */
  static LuaInstance *getInstance();
  /**
  * returns the luaState specified in instEnum
  * @param instID instEnum, is used to access the coresponding lua state
  * @return a pointer to a lua state
  */
  lua_State *getLuaState(instID instEnum) const;
  /**
  * returns a pointer to an array of strings containing all of the errors thrown during lua execution for given lua state
  * this will clear the lua stack, so if you just want to clear the stack don't catch the string it returns
  * @param instID inst, the instance ID that you wish to get the stack for
  * @return std::string ** a pointer to a string array, "/0" = no more elements
  */
  std::string *stackDump(instID inst);
  int compileLua(std::string luaStream, instID instEnum);
  void *doLua(int NumArgs, bool hasRet, instID instEnum);
  void pushVar(instID instEnum, void *data);
  void pushLuaFunc(instID instEnum, std::string funcName);
private:
// variables
  std::map<instID, lua_State*> states; /**< holds a pointer to all of the avaiable lua instances */
  static LuaInstance *singleton; /**< a pointer to the single instance of LuaInstance */
// functions
};
#endif

CPP (cut down so you don't spend 100 years browsing it)
...


int LuaInstance::compileLua(std::string luaStream, instID instEnum)
{
std::cout << luaStream;
return(luaL_loadstring(getLuaState(instEnum), luaStream.c_str()));
}

void LuaInstance::pushLuaFunc(instID instEnum, std::string funcName)
{
lua_getglobal(getLuaState(instEnum), funcName.c_str());
int i = lua_type(getLuaState(instEnum), -1);
if (lua_type(getLuaState(instEnum), -1)!=LUA_TFUNCTION)
{
std::cout << funcName;
}
}

void LuaInstance::pushVar(instID instEnum, void *data)
{
lua_pushlightuserdata(getLuaState(instEnum), data);
}

void *LuaInstance::doLua(int NumArgs, bool hasRet, instID instEnum)
{
int i;
int numRet = 0;
void *returnVal = NULL;
std::string debugging;
std::string *debugging2;

NumArgs++; // number of arguments I inteperate as number of objects passed, +1 is for the function that it passes

lua_State *L = getLuaState(instEnum);

if(hasRet)
{
numRet = 1;
}

if(lua_pcall(L, NumArgs, numRet, 0) != 0)
{
debugging.append("error running function: ");
		debugging.append(lua_tostring(L, -1));
}

if(hasRet)
{
returnVal = lua_touserdata(L, -1);
lua_pop(L, 1);  /* pop returned value */
debugging2 = stackDump(instEnum);
}
return(returnVal);
}
...

test.icz -> compTest4.lua
function add ( x, y )
CompCompiler:setSomeData(10) // don't worry about this, it was a test (before i found out about checking for errors using getLuaState, it was there as the lua state had a working variable CompCompiler that I knew was callable
return(2)
end

when LuaInstance::compileLua is executed it returns 0 (success) and I get it to print out the file, it prints exactly what is in the lua file including return carriages, just so that nobody questions the icz loader (I'm pretty sure the icz loader is very working (i.e. not bugged up like my current problem) the issue I get is when I call pushLuaFunc that add isn't being accepted... and then after that I can say for certain that the lua string is not executing as CompCompiler:setSomeData(10) doesn't pause my debugging (I have the luabind'd class with a debug pause marker on setSomeData

Anybody want to help me Posted Image haha it's probably somthing simple, for ages I had problems as my NumArgs was one too little (I took args literary, not including the function as an argument, DOH)

Thanks for your time, I really appreciate it.

Sponsor:

#2 falconmick   Members   -  Reputation: 80

Like
0Likes
Like

Posted 11 April 2012 - 06:21 AM

maybe can somebody tell me how I can check what is in the global (_G) table in lua?
I am almost certain it is because it cannot find the global function I declared called "add"

ok, here's a simplified question, hopefully someone can help me.

I pass this string:
function add(x, y)
	 CompCompiler:setSomeData(10)
	 return(2)
end

into this code:

int LuaInstance::compileLua(std::string luaStream, instID instEnum)
{
return(luaL_loadstring(getLuaState(instEnum), luaStream.c_str()));
}


and apon doing that, I run this:

LuaInstance::getInstance()->pushLuaFunc(LuaInstance::construction, "add");
which uses this function:
void LuaInstance::pushLuaFunc(instID instEnum, std::string funcName)
{ // getLuaState(instEnum) returns desired state
lua_getglobal(getLuaState(instEnum), funcName.c_str());
}
which when i call
lua_type(getLuaState(instEnum), -1);
it returns 0,
#define LUA_TNIL  0
#define LUA_TBOOLEAN  1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER  3
#define LUA_TSTRING  4
#define LUA_TTABLE  5
#define LUA_TFUNCTION  6
#define LUA_TUSERDATA  7
#define LUA_TTHREAD  8
which means that it has returned nill... it should be returning 6, LUA_TFUNCTION.
which makes me assume that the code I have loaded into the lua state isn't global, or I have loaded it wrong, please help :(

#3 e‍dd   Members   -  Reputation: 2105

Like
1Likes
Like

Posted 11 April 2012 - 11:57 AM

Can you make a really really small, standalone example that illustrates the problem? For the time being, forget about having multiple Lua states, about binding CompCompiler in those states, about the LuaInstance singleton, zipped code, and so on.

#4 Gambini   Members   -  Reputation: 371

Like
1Likes
Like

Posted 11 April 2012 - 03:15 PM

luaL_loadstring does not run the string, it only compiles it. Because you are only compiling the string, the function hasn't been defined yet for Lua, it is just sitting there in binary form on the stack. Two suggestions:
Do luaL_dostring, which calls luaL_loadstring and then runs the string.
Or, use luaL_dofile which does the same thing as luaL_dostring, but with a file.

After it gets executed, then the function will exist in the global table and you can retrieve it like you expected you could earlier.

If there are more specific questions, then ask away since I didn't go too in-depth on the "why" or "how".

#5 falconmick   Members   -  Reputation: 80

Like
0Likes
Like

Posted 11 April 2012 - 10:09 PM

luaL_loadstring does not run the string, it only compiles it. Because you are only compiling the string, the function hasn't been defined yet for Lua, it is just sitting there in binary form on the stack. Two suggestions:
Do luaL_dostring, which calls luaL_loadstring and then runs the string.
Or, use luaL_dofile which does the same thing as luaL_dostring, but with a file.

After it gets executed, then the function will exist in the global table and you can retrieve it like you expected you could earlier.

If there are more specific questions, then ask away since I didn't go too in-depth on the "why" or "how".


So there is no way to compile to code and not execute?

#6 falconmick   Members   -  Reputation: 80

Like
0Likes
Like

Posted 11 April 2012 - 11:32 PM

just another doh moment I found pout that this whole time i didnt know that if you have some lua wraped in a function -> end it isn't executed when you call

luaL_dostring(), it just loads it into byte code, thanks for the help



#7 Gambini   Members   -  Reputation: 371

Like
1Likes
Like

Posted 11 April 2012 - 11:44 PM

Is there a reason you do not want to execute the code? If they are just function definitions, then I do not see a downside to executing the code. If there are commands outside of a function that you don't want to run, maybe you should move those to a different file or something which you control when it runs. However, maybe you are thinking that compiling does more than it really does.

When you do loadstring, it converts the .lua file in to a Lua binary and puts it on the top of the stack as a function. It only converts. Nothing else. It does not have any effect on the Lua enviornment other than existing as a bunch of 1s and 0s. Think of it as in C++ you create a .dll, but you don't link with it yet.

When you execute that binary from loadstring, it is similar to actually linking with the .dll, and you can use the things defined in it. You have to remember that in Lua, functions are first-class data types similar to numbers and tables. Your add function could also be defined as
add = function(x,y)...your stuff here......end
and would produce the same outcome as you had it before, but this makes it easier to see the following:

When the code is executed, it goes through all of the file and runs all of the code. That seems entirely obvious, but with the function syntax being the way you used it, it isn't exactly easy to see how the functions come in to existence. Maybe the next example will help
--file example.lua
tbl = {}

add = function(x,y)
	return x + y
end

local a = 0
b = 2
c = add(a,b) -- c = 2
So, when that is compiled (loadstring/loadfile), the global table is unchanged. Nothing has been executed, and so nothing can be changed. However, after execution, the global table will look something like this
_G = { tbl = {}, add = function(x,y) return x+y end, b = 2, c = 2 .......whatever else was in _G before }
And the variable 'a' is local, so it isn't defined in the global scope.

There is a way to use loadstring to compile a string to run later, but you lose the advantage of having a named function. For that, look at the docs for luaL_ref.

However, there is really no downside to executing the code if all it does is define a function. It won't execute the function, if that is what you were worried about (unless you actually call the function, like I did with the 'c' variable in the example).

Edit: I see you replied while I was typing this out. I'll leave this here for people who google it later.

#8 falconmick   Members   -  Reputation: 80

Like
0Likes
Like

Posted 12 April 2012 - 07:02 AM

well, one last thing, then I'm done with lua setup (and it's time to use it)

how can I pass a string as an argument into a function...
I have passing int's setup and working using: lua_pushnumber
but when I use : lua_pushstring
lua_pcall fails (i.e. doesn't return 0) and returns 2, LUA_ERRRUN
I then use lua_tostring(L, -1) to find the reason for the fail.
it says "attempt to call a string value"
this happens inside of this lua function:
function tstRetStr(insTr)
return(inStr)
end
(this code was to test sending a string as an argument and then receiving it)

I'm probably just sending the string incorrectly. Anyone got any cluse...

btw, here's how I setup the lua stack before execution and then execute it, all function calls work as intened (as in ive checked the lua stack and seen that they are there.)
LuaInstance::getInstance()->pushLuaFunc(LuaInstance::construction, "tstRetStr");
  LuaInstance::getInstance()->pushString(LuaInstance::construction, "testing Sending a String");
  std::string s_reternedData2;
  s_reternedData2 = (char*)(LuaInstance::getInstance()->doLua(0, true, LuaInstance::construction));


and this is the functions it calls
void LuaInstance::pushString(instID instEnum, std::string data)
{
lua_pushstring(getLuaState(instEnum), data.c_str());
///* uncomment for debugging
//int i = lua_type(getLuaState(instEnum), -1);
//*/
}
void LuaInstance::pushLuaFunc(instID instEnum, std::string funcName)
{
lua_getglobal(getLuaState(instEnum), funcName.c_str());
}
void *LuaInstance::doLua(int NumArgs, bool hasRet, instID instEnum)
{
int i, luaData;
int numRet = 0;
int pcallResult;
void *returnVal = NULL;
std::string debugging; // on creation of user UI, at the end of doLua, this will be sent to the error handler, if "" is sent, no errors, if string exists, there's errors
//std::string *debugging2;//temporary returnTypes
bool b_tmpRet;
int i_tmpRet;
const char * s_tmpRet;
lua_State * ls_tmpRet;
size_t *strSize;
strSize = new size_t();
//NumArgs++; // number of arguments I inteperate as number of objects passed, +1 is for the function that it passeslua_State *L = getLuaState(instEnum);
if(hasRet)
{
  numRet = 1;
}/*
int a, b, c, d;
a = lua_type(getLuaState(instEnum), 1);
b = lua_type(getLuaState(instEnum), 2);
c = lua_type(getLuaState(instEnum), 3);
d = lua_type(getLuaState(instEnum), 4);
*/
	pcallResult = lua_pcall(L, NumArgs, numRet, 0);/*
int a, b, c, d, e, f, g;
a = lua_type(getLuaState(instEnum), 1);
b = lua_type(getLuaState(instEnum), 2);
c = lua_type(getLuaState(instEnum), 3);
d = lua_type(getLuaState(instEnum), 0);
e = lua_type(getLuaState(instEnum), -1);
f = lua_type(getLuaState(instEnum), -2);
g = lua_type(getLuaState(instEnum), -3);

int aa, bb, cc;
aa = lua_tointeger(getLuaState(instEnum), 1);
bb = lua_tointeger(getLuaState(instEnum), 0);
cc = lua_tointeger(getLuaState(instEnum), 1);
*/
if(hasRet) // if the function is expecting data to be returned
{
  luaData = lua_type(getLuaState(instEnum), -1);
  if(pcallResult != 0)
  {
   luaData = 9; // this indicates syntax error in function
  }
  switch(luaData) // switching the diferent possible return types to be placed inside of
  {
  case LUA_TNIL:
   debugging.append("error running function: ");
   debugging.append("no data returned");
   break; // nill returned
  case LUA_TBOOLEAN:
   b_tmpRet = lua_toboolean(getLuaState(instEnum), -1);
   memcpy(&returnVal, &b_tmpRet, sizeof b_tmpRet);
   break;
  case LUA_TLIGHTUSERDATA:
   debugging.append("error running function: ");
   debugging.append("returning bound objects is currently not supported");
   //returnVal = lua_touserdata(L, -1);
  case LUA_TNUMBER:
   i_tmpRet = lua_tointeger(getLuaState(instEnum), -1);
   memcpy(&returnVal, &i_tmpRet, sizeof i_tmpRet);
   break;
  case LUA_TSTRING:
   s_tmpRet = lua_tolstring(getLuaState(instEnum), -1, strSize);
   memcpy(&returnVal, &s_tmpRet, *strSize);
   break;
  case LUA_TTABLE:
   debugging.append("error running function: ");
   debugging.append("tables not supported");
   break;
  case LUA_TFUNCTION:
   debugging.append("error running function: ");
   debugging.append("returning functions not supported");
   break;
  case LUA_TUSERDATA:
   debugging.append("error running function: ");
   debugging.append("returning bound objects is currently not supported");
   //returnVal = lua_touserdata(L, -1);
   break;
  case LUA_TTHREAD:
   ls_tmpRet = lua_tothread(getLuaState(instEnum), -1);
   memcpy(&returnVal, &ls_tmpRet, sizeof ls_tmpRet);
   break;
  case 9:
   debugging.append("error running function: ");
   debugging.append(lua_tostring(L, -1));
   break;
  default:
   debugging.append("error running function: ");
   debugging.append("return of unknown datatype");
   break;
  }
  lua_pop(L, 1);  // pop returned value
}
else
{
  if(pcallResult != 0)
  {
   debugging.append("error running function: ");
   debugging.append(lua_tostring(L, -1));
  }
}
return(returnVal);
}
I'm fairly confident with both doLua and pushLuaFunc

#9 rip-off   Moderators   -  Reputation: 7977

Like
0Likes
Like

Posted 12 April 2012 - 09:47 AM

You are passing 0 as "NumArgs". Lua pops this many arguments off the stack, and attempts to call the next value with these arguments. In your case, the string you pushed is what Lua gets when it tries to do this.

Also, your doLua leaks and is actually extremely dangerous, as it requires the user to get the data type correct. I would write something like this instead:
bool pop(lua_State *L, std::string &string)
{
    // check type, and pop & return if appropriate
}

bool pop(lua_State *L, int n)
{
    // check type, and pop & return if appropriate
}


bool call(lua_State *L, int argc)
{
    int result = lua_pcall(L, argc, 0, 0);
    return check_error(result);
}


template<typename T>
bool call(lua_State *L, int argc, T &result)
{
    int result = lua_pcall(L, argc, 0, 0);
    if(check_error(result))
    {
        return false;
    }
    return pop(L, result);
}
In fact, done right you can actually write code like this:
LuaInstance &construction = /* ... */;
std::string result;
if(construction.call("tstRetStr",  "testing Sending a String", result))
{
    // use result
}
Note how we can hide most of the bug-prone details, such as maintaining the argument count and hoping the return value is of the correct type.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS