LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 11-15-2017, 10:14 PM   #1
goingbackwards
LQ Newbie
 
Registered: Jul 2015
Posts: 16

Rep: Reputation: Disabled
Lua / C++: Keeping state


I'd like to load a script once and use it numerous times.


Here's my example, the result from step 3 is "attempt to call a string value"...

Is it the way I take data off the stack, how create new state, how call it???

Cheers

Code:
std::vector<std::string> lua_get_stack(lua_State *L)
{
	//	GRAB STACK AS VECTOR OF STRINGS
	std::vector<std::string> ls;
	int i;
	int top = lua_gettop(L);
	for (i = 1; i <= top; i++)
	{
		ls.push_back((std::string)lua_tostring(L, i));
	}
	return ls;
}

lua_State *lua_load_file(const char* filename)
{
	//	LOAD SCRIPT TO  NEW STATE
	lua_State *state = luaL_newstate();
	luaL_openlibs(state);
	
	int result = luaL_loadfile(state, filename);
	
	if ( result != LUA_OK )
	{
		print_error(state);
		return NULL;
	}
	return state;
}

std::vector<std::string> lua_call_script(lua_State *state)
{
	//	CALL SCRIPT RAW
	int result = lua_pcall(state, 0, LUA_MULTRET, 0);
	if ( result != LUA_OK )
	{
		//	LUA_ERRRUN: a runtime error.
    	//	LUA_ERRMEM: memory allocation error. For such errors, Lua does not call the error handler function.
		//	LUA_ERRERR: error while running the error handler function
		std::cout << "ERROR:		" << result << std::endl;
		
		print_error(state);
		std::vector<std::string> ls;
		return ls;
	}
	return lua_get_stack(state);
}

void test_02()
{
	
	lua_State *state = lua_load_file("test.lua");
	lua_State *state2 = lua_load_file("test2.lua");
	
	
	std::cout << "STEP:		1" << std::endl;
	std::vector<std::string> ls = lua_call_script(state);
	for(unsigned int i = 0; i < ls.size(); i++)
	{
		std::cout << "	" << ls[i] << std::endl;
	}
	
	std::cout << "STEP:		2" << std::endl;
	ls = lua_call_script(state2);
	for(unsigned int i = 0; i < ls.size(); i++)
	{
		std::cout << "	" << ls[i] << std::endl;
	}
	
	std::cout << "STEP:		3" << std::endl;
	ls = lua_call_script(state);
	for(unsigned int i = 0; i < ls.size(); i++)
	{
		std::cout << "	" << ls[i] << std::endl;
	}
	
	lua_close(state);
	lua_close(state2);
	
}
 
Old 11-17-2017, 04:44 PM   #2
norobro
Member
 
Registered: Feb 2006
Distribution: Debian Sid
Posts: 792

Rep: Reputation: 331Reputation: 331Reputation: 331Reputation: 331
I'm not an experienced Lua programmer but since no one else has answered I'll give it a shot.

From the docs: https://www.lua.org/manual/5.3/manual.html#lua_pcall
Quote:
lua_pcall
... Like lua_call, lua_pcall always removes the function and its arguments from the stack.
So when you call lua_pcall with "state" the second time the stack is empty.

I only know of two ways do this:
  1. call luaL_loadfile(), which performs an i/o operation, after calling lua_pcall to push your data onto the stack each time you want to execute the code in the file.
  2. load the file once into a string and push the string onto the stack before each lua_pcall.

Here's a simple example of #2:
Code:
$ cat test.lua
print("Hello World")

$ cat test2.lua
print("Goodbye World")
Code:
#include <iostream>
#include <fstream>
extern "C" {
    #include "lua5.3/lauxlib.h"
    #include "lua5.3/lualib.h"
}

int main() {
    std::ifstream file("test.lua");
    std::string str( (std::istreambuf_iterator<char>(file) ),
                     (std::istreambuf_iterator<char>()    ) );
    file.close();
    file.open("test2.lua", std::fstream::in);
    std::string str2( (std::istreambuf_iterator<char>(file) ),
                     (std::istreambuf_iterator<char>()    ) );
    file.close();
    lua_State *state = luaL_newstate();
    lua_State *state2 = luaL_newstate();
    luaL_openlibs(state);
    luaL_openlibs(state2);
    luaL_loadbuffer(state, str.c_str(), str.length(), str.c_str());
    lua_pcall(state, 0, LUA_MULTRET, 0);
    luaL_loadbuffer(state2, str2.c_str(), str2.length(), str2.c_str());
    lua_pcall(state2, 0, LUA_MULTRET, 0);
    int res = lua_pcall(state, 0, LUA_MULTRET, 0);  // Empty stack
    if(res != LUA_OK){
        std::cout << "Error= " << res << "  " << lua_tostring(state, -1) << "\n";
    }
    luaL_loadbuffer(state, str.c_str(), str.length(), str.c_str());
    lua_pcall(state, 0, LUA_MULTRET, 0);
    lua_close(state);
    lua_close(state2);
}
Output:
Code:
Hello World
Goodbye World
Error= 2  attempt to call a nil value  // I get this error with your code
Hello World
HTH

Last edited by norobro; 11-17-2017 at 07:27 PM. Reason: typo in code
 
1 members found this post helpful.
Old 11-17-2017, 05:35 PM   #3
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,258
Blog Entries: 24

Rep: Reputation: 4193Reputation: 4193Reputation: 4193Reputation: 4193Reputation: 4193Reputation: 4193Reputation: 4193Reputation: 4193Reputation: 4193Reputation: 4193Reputation: 4193
I have refrained from knocking this off the zero-reply until someone more knowledgable answered. Now that norobro has provided a much better response, I'll add my own incomplete observation..

Quote:
Originally Posted by goingbackwards View Post
Here's my example, the result from step 3 is "attempt to call a string value"...
Which would indicate to me that state at that point is not a valid lua_State *. Norobro's mention of stack exhaustion would seem to point in the same direction.
 
Old 11-17-2017, 08:59 PM   #4
goingbackwards
LQ Newbie
 
Registered: Jul 2015
Posts: 16

Original Poster
Rep: Reputation: Disabled
Ah, excellent! Just coming back to this, thankyou...

So the state is still valid, but its just popped off the instruction stack, and that needs to be copied in again using luaL_loadbuffer()... That makes sense (esp with an analogy to filling the cache before letting the ALU rip???)... I'll give it a go!
 
Old 11-18-2017, 01:20 AM   #5
goingbackwards
LQ Newbie
 
Registered: Jul 2015
Posts: 16

Original Poster
Rep: Reputation: Disabled
Yeah, enjoyed that... Thanks again

Code:

#include "LuaEngScript.h"







#include <cmath>

//	===================================================================
//	START API
//	===================================================================

static int l_sin (lua_State *L)
{
	double d = luaL_checknumber(L, 1);
	lua_pushnumber(L, 53);	//	sin(d));
	return 1;  // number of results
}


//	===================================================================
//	END API
//	===================================================================




LuaEngScript::LuaEngScript(std::string fn)
{
	filename = "";
	script = "";
	state = NULL;
	
	if(fn != "")
	{
		load(fn);
	}
}

LuaEngScript::~LuaEngScript()
{
	
	if(state)
	{
		lua_close(state);
	}
}




void LuaEngScript::load(std::string fn)
{
	filename = fn;
	
	if(filename == "")
	{
	
	}
	else
	{
		//	LOAD FILE
		file_load(filename);
		
		//lua_State *
		state = luaL_newstate();
		luaL_openlibs(state);
		
		lua_pushcfunction(state, l_sin);
    	lua_setglobal(state, "mysin");
    	
    	
    	
    	//	ADD A TABLE OF FUNCTIONS
    	lua_newtable(state);    // We will pass a table
		
		lua_pushstring(state, "sin");   // Push the table index
		lua_pushcfunction(state, l_sin);
		//lua_pushnumber(state, i*2); // Push the cell value
		lua_rawset(state, -3);      // Stores the pair in the table
		
		lua_pushstring(state, "cos");   // Push the table index
		lua_pushcfunction(state, l_sin);
		//lua_pushnumber(state, i*2); // Push the cell value
		lua_rawset(state, -3);      // Stores the pair in the table
		
		// By what name is the script going to reference our table?
		lua_setglobal(state, "eng");
    	
    	
    	
    	
		//luaL_loadbuffer(state, script.c_str(), script.length(), script.c_str());
    	//lua_pcall(state, 0, LUA_MULTRET, 0);
	}
}


void LuaEngScript::file_load(std::string fn)
{
	std::ifstream file(fn);
    //std::string str( (std::istreambuf_iterator<char>(file)), (std::istreambuf_iterator<char>()) );
    script = std::string( (std::istreambuf_iterator<char>(file)), (std::istreambuf_iterator<char>()) );
    file.close();
    
    //script = str;
}






void LuaEngScript::call_init()
{
	luaL_loadbuffer(state, script.c_str(), script.length(), script.c_str());
	lua_pcall(state, 0, LUA_MULTRET, 0);
	
	
	
	//double x = 10;
	//double y = 3;
	double z;

	// push functions and arguments
	lua_getglobal(state, "init");  // function to be called
	//lua_pushnumber(state, x);   // push 1st argument
	//lua_pushnumber(state, y);   // push 2nd argument

	// do the call (2 arguments, 1 result)
	//if (lua_pcall(state, 2, 1, 0) != 0)
	if (lua_pcall(state, 0, 1, 0) != 0)		//	zero args, one result
	{
		std::cout << "LuaEngScript::call_init()	Error call" << std::endl;
		//error(state, "error running function `f': %s", lua_tostring(state, -1));
		print_error(state);
		return;// stack;
	}
	
	
	
	
	
	
	
	// retrieve result
	if (!lua_isnumber(state, -1))
	{
		std::cout << "LuaEngScript::call_init()	Error return result" << std::endl;
		//error(state, "function `mult' must return a number");
		print_error(state);
		return;// stack;
	}
	z = lua_tonumber(state, -1);
	lua_pop(state, 1);  /* pop returned value */
	
	
	//printf("\nResult: %0.2f\n", z);
	std::cout << "LuaEngScript::call_init()	Result: " << z << std::endl;
	
	
	//lua_close(state);
	
	
}

void LuaEngScript::call_step(float delta)
{
	
	luaL_loadbuffer(state, script.c_str(), script.length(), script.c_str());
	lua_pcall(state, 0, LUA_MULTRET, 0);
	
	
	
	
	
	
	
	std::vector<double> data;
	add_global_table_num(state, "foo", data);
	
	
	std::map<std::string,std::string> data2;
	data2.insert( std::pair<std::string,std::string>("one", "apples") );
	data2.insert( std::pair<std::string,std::string>("two", "pairs") );
	add_global_table_strings(state, "mydata", data2);
	
	
	
	
	//double x = 10;
	//double y = 3;
	double z;

	// push functions and arguments
	lua_getglobal(state, "step");  // function to be called
	lua_pushnumber(state, delta);   // push 1st argument
	//lua_pushnumber(state, y);   // push 2nd argument

	// do the call (2 arguments, 1 result)
	//if (lua_pcall(state, 2, 1, 0) != 0)
	if (lua_pcall(state, 1, 1, 0) != 0)		//	one arg, one result
	{
		std::cout << "LuaEngScript::call_step()	Error call" << std::endl;
		//error(state, "error running function `f': %s", lua_tostring(state, -1));
		print_error(state);
		return;// stack;
	}
	
	
	
	
	
	
	
	// retrieve result
	if (!lua_isnumber(state, -1))
	{
		std::cout << "LuaEngScript::call_step()	Error return result" << std::endl;
		//error(state, "function `mult' must return a number");
		print_error(state);
		return;// stack;
	}
	z = lua_tonumber(state, -1);
	lua_pop(state, 1);  /* pop returned value */
	
	
	//printf("\nResult: %0.2f\n", z);
	std::cout << "LuaEngScript::call_step()	Result: " << z << std::endl;
	
	
	//lua_close(state);
	
	
}












void LuaEngScript::print_error(lua_State* state)
{
	//std::cout << "LuaEngScript::print_error()" << std::endl;
	// The error message is on top of the stack.
	// Fetch it, print it and then pop it off the stack.
	const char* message = lua_tostring(state, -1);
	puts(message);
	lua_pop(state, 1);
}



std::vector<std::string> LuaEngScript::lua_get_stack(lua_State *L)
{
	//	GRAB STACK AS VECTOR OF STRINGS
	std::vector<std::string> ls;
	int i;
	int top = lua_gettop(L);
	for (i = 1; i <= top; i++)
	{
		ls.push_back((std::string)lua_tostring(L, i));
	}
	return ls;
}



void LuaEngScript::add_global_table_num(lua_State *state, std::string name, std::vector<double> data)
{
	
	//	* Ok, now here we go: We pass data to the lua script on the stack.
	//	* That is, we first have to prepare Lua's virtual stack the way we
	//	* want the script to receive it, then ask Lua to run it.
	lua_newtable(state);    /* We will pass a table */
	
	//	* To put values into the table, we first push the index, then the
	//	* value, and then call lua_rawset() with the index of the table in the
	//	* stack. Let's see why it's -3: In Lua, the value -1 always refers to
	//	* the top of the stack. When you create the table with lua_newtable(),
	//	* the table gets pushed into the top of the stack. When you push the
	//	* index and then the cell value, the stack looks like:
	//	*
	//	* <- [stack bottom] -- table, index, value [top]
	//	*
	//	* So the -1 will refer to the cell value, thus -3 is used to refer to
	//	* the table itself. Note that lua_rawset() pops the two last elements
	//	* of the stack, so that after it has been called, the table is at the
	//	* top of the stack.
	for (int i = 1; i <= 5; i++)
	{
		lua_pushnumber(state, i);   // Push the table index
		lua_pushnumber(state, i*2); // Push the cell value
		lua_rawset(state, -3);      // Stores the pair in the table
	}

	// By what name is the script going to reference our table?
	lua_setglobal(state, name.c_str());
	
}

void LuaEngScript::add_global_table_strings(lua_State *state, std::string name, std::map<std::string,std::string> data)
{
	//	push new table
	lua_newtable(state);
	
	//	add data
	//for(unsigned int i = 0; i < data.size(); i++)
	for(std::map<std::string,std::string>::iterator it=data.begin(); it!=data.end(); ++it)
	{
		lua_pushstring(state, it->first.c_str());   // Push the table index
		lua_pushstring(state, it->second.c_str()); // Push the cell value
		lua_rawset(state, -3);      // Stores the pair in the table
	}

	//	lua variable name
	lua_setglobal(state, name.c_str());
	
}
with a lua file of:

Code:
io.write("ImgBug: main.lua\n");



function init (a)
	
	io.write("ImgBug: init()\n");
	
	return 0
end

function step (delta)
	io.write("ImgBug: step()\n");
	print("	delta", delta)
	print("	sin", mysin(10))
	
	print("	eng.sin", eng.sin(10))
	
	--test_01()
	
	
	print("	global table", foo[2])
	
	print("	global table", mydata.one)
	
	
	
	--N = N + 1
	--local N = N + 1
	N = 0
	--return (x^2 * math.sin(y))/(1 - x)
	return N
end


function test_01()
	
	--	TABLE EXAMPLE
	local t = {}
	t["foo"] = 123 -- assign the value 123 to the key "foo" in the table
	t[3] = "bar" -- assign the value "bar" to the key 3 in the table
	print("foo", t["foo"])
	print("3", t[3])
	
	t = {["foo"] = "bar", [123] = 456}
	print(".foo", t.foo)
	
	
end

function f (a, b)
	return a * b
end

Night all!
 
  


Reply

Tags
lua


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Lua and Java... ButterflyMelissa Programming 2 04-02-2012 10:07 AM
Conky + lua flyinggeorge Linux - Software 6 03-05-2012 08:47 PM
iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --set deadeyes Linux - Server 14 07-29-2009 04:30 AM
not work: iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 3306 -j DROP abefroman Linux - Security 1 07-18-2007 08:19 AM
LXer: State by state, Microsoft responds to creeping threat LXer Syndicated Linux News 0 05-01-2007 07:16 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 03:13 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration