Lua5.2 embedded in C++ - c++

I'm trying for the first time to have Lua embedded in C++. I've been searching for 2 days now, but most of internet tutos use lua5.1, which is incompatible with lua5.2. So I read a bit of lua documentation, example source code, and I end up with this :
main.cpp :
#include "luainc.h"
#include <iostream>
int main(){
int iErr = 0;
lua_State *lua = luaL_newstate (); // Open Lua
luaopen_io (lua); // Load io library
if ((iErr = luaL_loadfile (lua, "hw.lua")) == 0)
{
std::cout<<"step1"<<std::endl;
if ((iErr = lua_pcall (lua, 0, LUA_MULTRET, 0)) == 0)
{
std::cout<<"step2"<<std::endl;
lua_getglobal (lua, "helloWorld"); // Push the function name onto the stack
if (lua_type(lua, lua_gettop(lua)) == LUA_TNIL) {
// if the global variable does not exist then we will bail out with an error.
std::cout<<"global variable not found : helloworld"<<std::endl;
/* error so we will just clear the Lua virtual stack and then return
if we do not clear the Lua stack, we leave garbage that will cause problems with later
function calls from the application. we do this rather than use lua_error() because this function
is called from the application and not through Lua. */
lua_settop (lua, 0);
return -1;
}
// Function is located in the Global Table
/* lua_gettable (lua, LUA_GLOBALSINDEX); */ //lua5.1
lua_pcall (lua, 0, 0, 0);
}
}
lua_close (lua);
return 0;
}
hw.lua :
-- Lua Hello World (hw.lua)
function helloWorld ()
io.write ("hello World")
end
luainc.h :
#ifndef __LUA_INC_H__
#define __LUA_INC_H__
extern "C"
{
#include </home/renardc/Documents/Programmation/Lua_CPP/lua-5.2.2/src/lua.h>
#include </home/renardc/Documents/Programmation/Lua_CPP/lua-5.2.2/src/lauxlib.h>
#include </home/renardc/Documents/Programmation/Lua_CPP/lua-5.2.2/src/lualib.h>
}
#endif // __LUA_INC_H__
I have no error, the output is :
step1
step2
which should mean that my "helloworld" function has been found. But as I can not see "Hello World" in the output, I suspect that the function has not been called. What am I doing wrong ?
This is how I compile my program :
g++ main.cpp -L/usr/local/include -I/usr/local/include -llua

First, why not #include "lua.hpp", which comes with Lua and does mostly what your luainc.hdoes?
There are two problems with your code:
You don't emit any error message when luadL_loadfile fails.
You use lua_pcall to call helloWorld but does not test its return value.
When you change lua_pcall to lua_call you get this error message:
attempt to index global 'io' (a nil value)
This means that you forgot to set the global io after calling luaopen_io. Just add lua_setglobal(lua,"io")and it works. Unlike Lua 5.1, Lua 5.2 does not set globals automatically when you open libraries, unless the library itself does it, which is discouraged.
You'll probably be better off calling luaL_openlibs to open all standard Lua libraries with no surprises.
You may as well use luaL_dofile instead of luaL_loadfile and save the first lua_pcall. You still have to check the return value.

Related

Lua: Cant open shared library when lua is wrapped in C++

EDIT: Nearly got the answer, I just dont completely understand it, see last paragraph.
I try to build a shared lua library and use it within a larger project. When calling the script which loads the shared library from shell everything works. However, when I wrap the script within another shell, I get a runtime error when loading the library. Dependent on the script it is just any call to a lua function from c (i.e. lua_pushnumber). Here is a minimal example.
totestlib.cpp:
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
int init(lua_State *L) {
lua_toboolean(L, -1);
return 0;
}
static const struct luaL_Reg testlib[] = {
{"init", init},
{NULL, NULL}
};
extern "C"
int luaopen_libtotestlib(lua_State *L) {
luaL_newlib(L, testlib);
return 1;
}
Compiled with: g++ -shared -fPIC -I./lua-5.4.4/src -L./lua-5.4.4/src totestlib.cpp -o libtotestlib.so
testlib.lua (testing shared library):
testlib.lua
print("start")
testlib = require("libtotestlib")
print("done")
testlib.init(true)
print("called")
Calling the lua script using ./lua-5.4.4/src/lua testlib.lua works. Everything is printed. Wrapping script in the following c++ code does not work:
call_testlib.cpp
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
#include <unistd.h>
static lua_State *L;
int main(int argc, char *argv[]) {
L = luaL_newstate();
luaL_openlibs(L);
int tmp = luaL_loadfile(L, "testlib.lua");
if(tmp != 0) {
return 1;
}
tmp = lua_pcall(L, 0, 0, 0);
if(tmp != 0) {
printf("error pcall\n");
return 1;
}
}
Compiled with g++ call_testlib.cpp -o ./call_testlib -I./lua-5.4.4/src -L./lua-5.4.4/src -llua it prints "error pcall". If I print the error message on the lua stack, I get:
string error loading module 'libtotestlib' from file './libtotestlib.so':
./libtotestlib.so: undefined symbol: luaL_checkversion_
In this case the undefined symbol is luaL_checkversion_ (which I dont call myself), but with other scripts it is usually the first lua_... function that I call.
I have tried several things to fix this. For example, linking -llua when compiling the shared library, but this does not work (and should not be the problem as calling the script itself works). I also tried to load preload the library from c++ (as done in this question) instead of from lua, but I guess it does not really make a difference and I am getting the same error. I also uninstalled all lua versions from my path to make sure I always use the same version.
What is the difference between calling the script directly from shell and calling it inside a c function? Am I doing something wrong?
EDIT: Nearly got the answer. When using MYCFLAGS= -fPIC when compiling lua I can link lua to the shared library. At least this one works, but does not seem like a good solution to me and does not really answer my question: Why can lua itself (from shell) somehow add these symbols to the library while the wrapped c version can not? Additionally, my program has lua once linked in the shared library and once in the compiled C++ project (not optimal imo).

Accessing lua_State from another application

Now imagine you have two programs with different lua instances. One is the main program, the second is the dll you coded for it.
In my question, I will name the main program as main, dll i child from now on. We load the child into the Main process, detouring it and somehow accessing lua_State.
My main question is, can we do lua_pcall or dofile via the lua_State we grab while the main program is running?
Sample code
Main program:
#include <lua.hpp>
bool loadFile(lua_State* L) {
// run the Lua script
luaL_dofile(L, "helloworld.lua");
if (lua_pcall(L, 0, 0, eh) != 0)
{
std::string err = luaL_checkstring(L, -1);
lua_pop(L, 1);
}
}
int main()
{
// create new Lua state
lua_State *lua_state;
lua_state = luaL_newstate();
loadFile(lua_state);
}
Child program:
#include <lua.hpp>
#include "hookingLibrary.h"
typedef int(__fastcall* main_loadFile_Proto)(lua_State* L);
main_loadFile_Proto main_loadFile_Ptr;
lua_State * L lastState;
uint64_t main_loadFile_Addr = 0x0;
int main_loadFile_Detour(lua_State* L) {
lastState = L;
return main_loadFile_Ptr(L);
}
int main()
{
// detouring etc.
// I do not put detouring codes here. I am just declaring it as an
// opinion.
HookingLibrary::hook((LPVOID)(uintptr_t)main_loadFile_Addr, &main_loadFile_Detour, (LPVOID*)&main_loadFile_Ptr);
do{
Sleep(100);
}while(!lastState);
// create new Lua state
lua_State *lua_state;
lua_state = lastState;
// run the Lua script
luaL_dofile(lua_state, "helloworld.lua");
// close the Lua state
lua_close(lua_state);
}
Now imagine you have two programs with different lua instances. One is the main program, the second is the dll you coded for it.
This statement is not very clear, it depends on your expectations. I see 2 possible answers.
Create a DLL which implement some additional functions for Lua. The DLL library can be used later by the main program. In this case, there is only 1 instance of lua_state, there is only 1 Lua interpreter. This Lua interpreter may be created by the DLL or by the main function.
The interface of DLL is something like that:
#ifndef DLL_AUGMENTED
#define DLL_AUGMENTED
#include "lua.h"
lua_State *DLL_CreateAugmentedLuaInterpreter ();
void DLL_FreeLuaInterpreter ();
#endif
And can be used by main:
#include "lua-augmented.h"
int main (int argc, char **argv)
{
lua_State *LuaState = DLL_CreateAugmentedLuaInterpreter ();
// TODO: use the augmented Lua instance
DLL_FreeLuaInterpreter(LuaState);
return 0;
}
There is a need to have 2 Lua instances, as written One is the main program, the second is the dll. In this case, it's more difficult because a IPC Interprocess Communication need to be implemented with sockets or pipes. The best is to look for LuaSocket library.
Interprocess communication in Lua with Example?

What is the difference between lua 5.0.2 modules and 5.3.5?

i try to write some c++ code for lua. First i used the latest version 5.3.5 and i was able to register some new functions. But the final program that i want to write the code for uses 5.0.2. After i compiled the old source and build the dll with lua 5.0.2, require cannot read the file
Lua 5.0.2 Copyright (C) 1994-2004 Tecgraf, PUC-Rio
> require("remaster_IO.dll")
stdin:1: error loading package `remaster_IO.dll' (remaster_IO.dll:1: `=' expected near `É')
stack traceback:
[C]: in function `require'
stdin:1: in main chunk
[C]: ?
This is the dll code:
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
__declspec(dllexport) int luaopen_remaster_IO(lua_State* L);
}
#define lua_register(L,n,f) \
(lua_pushstring(L, n), \
lua_pushcfunction(L, f), \
lua_settable(L, LUA_GLOBALSINDEX))
#include <iostream>
#include <Windows.h>
int rema_add1(lua_State* L) {
double d = luaL_checknumber(L, 1); // get item 1
lua_pushnumber(L, d + 1);
return 1; // number of items returned
}
int luaopen_remaster_IO(lua_State* L) {
Beep(200, 200);
std::cout << "Hello World!!!" << std::endl;
lua_register(L, "average", rema_add1);
return 1;
}
If you look at the documentation (scroll down to require, back then it didn't have per-function anchors yet…), you'll see that require in 5.0 indeed only loads Lua files. If you don't have access to loadlib, you're probably out of luck.
(There's even a good chance that they compiled Lua in such a way that dynamic linking of libraries is not supported at all – so even if you were aware of a bug that you could use to find, push & (re-)add loadlib's pointer as a Lua function value, that might not be enough…)

'Attempt to index a string value' error while loading a c++ module in lua

I'm trying to use a function written in C++ from lua. Given below is the cpp file:
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
static int add_5(lua_State *L)
{
double d = lua_tonumber(L, 1); /* get argument */
d=d+5;
lua_pushnumber(L, d); /* push result */
return 1; /* number of results */
}
static const struct luaL_Reg mylib [] =
{
{"add_5", add_5},
{NULL, NULL} /* sentinel */
};
extern "C"
{
int luaopen_mylib (lua_State *L)
{
//luaL_newlib(L, mylib);
luaL_register(L, NULL, mylib);
return 1;
}
}
I compiled the above code by g++ using the following command:
g++ -shared -o mylib.so test.cpp -fPIC
I'm getting the following error on the lua interpreter:
Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio
> local temp = require "mylib"
attempt to index a string value
stack traceback:
[C]: ?
[C]: in function 'require'
stdin:1: in main chunk
[C]: ?
Please note that I can't upgrade the version of Lua due to some reasons.
The second argument to luaL_register is the library name. You can leave it as NULL, but if you do, luaL_register will try to insert the registered functions into the table it expects to find on the top of the stack (and in your code there's no table on top of the stack). For the general case of registering a library, it's easiest to pass your library name as the second parameter.
Note that LHF suggests not doing it that way, since it automatically puts the libary's table into the global table, whereas the user of the library might want to have it only as a local variable. The alternative is to create your own table with lua_newtable before calling luaL_register (with a null name).

Neko Dlls in Haxe C++ target

I am trying to use Neko dlls (written in C++) with the C++ target of Haxe. I am able to call the functions in haxe but not able to pass values.
This is the C++ code -
value Hello(value h)
{
cout << val_int(h);
return val_int(1);
}DEFINE_PRIM(Hello, 1);
This is the Haxe code -
class Main
{
var load = cpp.Lib.loadLazy( "ndll" , "Hello", 1 );
static function main()
{
load(1);
}
}
It executes only if the function does not take parameters. Also, the value that is returned form the C++ function to Haxe is null.
This code actually works perfectly when I compile for the neko target, but it doesn't seem to work with the cpp target.
Any help is appreciated.
Here's the fully corrected C++ code :
#define IMPLEMENT_API
/* Will be compatible with Neko on desktop targets. */
#if defined(HX_WINDOWS) || defined(HX_MACOS) || defined(HX_LINUX)
#define NEKO_COMPATIBLE
#endif
#include <hx/CFFI.h>
#include <stdio.h>
/* Your hello function. */
value hello(value h)
{
printf("%i\n", val_int(h));
return alloc_int(1);
}
DEFINE_PRIM(hello, 1);
/* Main entry point. */
extern "C" void mylib_main()
{
// Initialization code goes here
}
DEFINE_ENTRY_POINT(mylib_main);
What's important is that every value given as an argument to a primitive or returned by a primitive must be of the type value. That's why your parameter and return didn't work.
val_int is used to convert a value into a native C type, so your printing code was correct. But your return was wrong : you can't return a C int type when the function expects you to return a value to Haxe. You need to create a new Haxe Int type and return it. This is done with the help of alloc_int.
Here's the Haxe part of the code as a reference :
class Main
{
static var hello = cpp.Lib.load("myLib", "hello", 1);
static function main()
{
var myReturnedInt:Int = hello(1);
}
}
A few helpful links :
Neko C FFI
Neko FFI tutorial
CPP FFI notes
In order for this to work, you'll have to add to the header of your cpp file:
#define IMPLEMENT_API
#include <hx/CFFI.h>
(instead of neko's headers)
If you want the ndll to run on both neko and hxcpp, you should also add
#define NEKO_COMPATIBLE
before the hx/CFFI.h include.
You can compile using whatever is best for you, but I recommend using a Build.xml to generate your ndll, since it will automatically add the include and lib paths correctly for hxcpp's headers. You can see an example of a very simple Build.xml here:
http://pastebin.com/X9rFraYp
You can see more documentation about hxcpp's CFFI here: http://haxe.org/doc/cpp/ffi