Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LuaPlus: How to make a function return a table?

I'm wondering how I you can create and register a function from the C++-side that returns a table when called from the Lua-side.
I've tried a lot of things but nothing did really work. :/

(sorry for the long code) This for example won't work, because Register() expects a "luaCFunction"-styled function:

LuaPlus::LuaObject Test( LuaPlus::LuaState* state ) {
    int top = state->GetTop();
    std::string var( state->ToString(1) );

    LuaPlus::LuaObject tableObj(state);
    tableObj.AssignNewTable(state);

    if (var == "aaa")
        tableObj.SetString("x", "ABC");
    else if (var == "bbb")
        tableObj.SetString("x", "DEF");
    tableObj.SetString("y", "XYZ");
    return tableObj;
}
int main()
{
    LuaPlus::LuaState* L = LuaPlus::LuaState::Create(true);
     //without true I can't access the standard libraries like "math.","string."...
     //with true, GetLastError returns 2 though (ERROR_FILE_NOT_FOUND)
     //no side effects noticed though

    LuaPlus::LuaObject globals = L->GetGlobals();

    globals.Register("Test",Test);

    char pPath[MAX_PATH];
    GetCurrentDirectory(MAX_PATH,pPath);
    strcat_s(pPath,MAX_PATH,"\\test.lua");
    if(L->DoFile(pPath)) {
        if( L->GetTop() == 1 ) // An error occured
            std::cout << "An error occured: " << L->CheckString(1) << std::endl;
    }
}

When I try to set it up as a luaCFunction-function it just crashes (0x3) and says:
Assertion failed: 0, file C:\......\luafunction.h, line 41

int Test( LuaPlus::LuaState* state ) {
    int top = state->GetTop();
    std::string var( state->ToString(1) );

    LuaPlus::LuaObject tableObj(state);
    tableObj.AssignNewTable(state);

    if (var == "aaa")
        tableObj.SetString("x", "ABC");
    else if (var == "bbb")
        tableObj.SetString("x", "DEF");
    tableObj.SetString("y", "XYZ");

    tableObj.Push();

    return state->GetTop() - top;
}

For clarification: from the Lua side I wanted it to be callable like:

myVar = Test("aaa")
Print(myVar) -- output: ABC

EDIT: The Print function comes from here. And was basically the cause for this to not work. Print can only print strings not tables... The C++ code from above works fine if you just return 1.

This is the documentation that came with my LuaPlus version btw: http://luaplus.funpic.de/

I really hope you can help me.. I'm already starting to think that it is not possible. :'(

edit: I totally forgot to say that using PushStack() lead into an error because "the member does not exist"...

like image 803
Forivin Avatar asked Nov 07 '13 02:11

Forivin


2 Answers

After some painstaking probing from the long comment discussion, I'm posting this answer to help summary the situation and hopefully to offer some useful advice.

The main issue the OP was running into was that the wrong print function was being called in the lua test script. Contrary to the original code shown the real code the OP was testing against was calling Print(myVar) which is a custom provided lua_CFunction and not the builtin print function.

Somehow along the way, this ended up creating some instantiation of template <typename RT> class LuaFunction and calling the overloaded operator()(). From inspecting the luafunction.h from luaPlus any lua errors that occurs inside this call will get swallowed up without any kind of logging (not a good design decision on luaPlus's part):

  if (lua_pcall(L, 0, 1, 0)) {
      const char* errorString = lua_tostring(L, -1);  (void)errorString;
      luaplus_assert(0);
  }

To help catch future errors like this, I suggest adding a new luaplus_assertlog macro. Specifically, this macro will include the errorString so that the context isn't completely lost and hopefully help with debugging. This change hopefully won't break existing uses of luaplua_assert from other parts of the API. In the long run though, it's probably better to modify luaplus_assert so it actually includes something meaningful.

Anyway here's a diff of the changes made:

LuaPlusInternal.h

@@ -81,5 +81,6 @@
 } // namespace LuaPlus

 #if !LUAPLUS_EXCEPTIONS
+#include <stdio.h>
 #include <assert.h>
 #define luaplus_assert(e) if (!(e)) assert(0)
@@ -84,5 +85,6 @@
 #include <assert.h>
 #define luaplus_assert(e) if (!(e)) assert(0)
+#define luaplus_assertlog(e, msg) if (!(e)) { fprintf(stderr, msg); assert(0); }
 //(void)0
 #define luaplus_throw(e) assert(0)
 //(void)0

LuaFunction.h

@@ -21,7 +21,7 @@
 class LuaFunction
 {
 public:
-   LuaFunction(LuaObject& _functionObj)
+   LuaFunction(const LuaObject& _functionObj)
    : functionObj(_functionObj) {
  }

@@ -36,7 +36,7 @@

    if (lua_pcall(L, 0, 1, 0)) {
      const char* errorString = lua_tostring(L, -1);  (void)errorString;
-           luaplus_assert(0);
+           luaplus_assertlog(0, errorString);
    }
    return LPCD::Type<RT>::Get(L, -1);
  }

In the change above, I opted not to use std::cerr simply because C++ streams tend to be heavier than plain-old C-style io functions. This is especially true if you're using mingw as your toolchain -- the ld linker is unable to eliminate unused C++ stream symbols even if your program never uses it.

With that in place, here's an example where an unprotected call is made to a lua function so you can see the errorString printed out prior to the crash:

// snip...
int main(int argc, const char *argv[])
{
    LuaStateAuto L ( LuaState::Create(true) );
    LuaObject globals = L->GetGlobals();
    globals.Register("Test", Test);
    globals.Register("Print", Print);
    if(argc > 1)
    {
      /*
      if (L->DoFile(argv[argc - 1]))
        std::cout << L->CheckString(1) << '\n';
      /*/
      L->LoadFile( argv[argc - 1] );
      LuaFunction<int> f ( LuaObject (L, -1) );
      f();
      //*/
    }
}

Running the above will trigger the crash but will include a semi-helpful error message:

g++ -Wall -pedantic -O0 -g   -I ./Src -I ./Src/LuaPlus/lua51-luaplus/src plustest.cpp -o plustest.exe lua51-luaplus.dll

plustest.exe plustest.lua
plustest.lua:2: bad argument #1 to 'Print' (string expected, got table)Assertion failed!

Program: G:\OSS\luaplus51-all\plustest.exe
File: ./Src/LuaPlus/LuaFunction.h, Line 39

Expression: 0

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
like image 88
greatwolf Avatar answered Oct 02 '22 01:10

greatwolf


first you may try to register the function using RegisterDirect(), this may avoid lua_CFunction's problem, check the luaplus manual.like this

LuaPlus::LuaObject globals = L->GetGlobals();

globals.RegisterDirect("Test",Test);

second if I remeber to create a table have two solutions,like this

//first
LuaObject globalsObj = state->GetGlobals();  
LuaObject myArrayOfStuffTableObj = globalsObj.CreateTable("MyArrayOfStuff"); 

//second
LuaObject aStandaloneTableObj;  
aStandaloneTableObj.AssignNewTable(state);  

check whether you have use the right function.

third I remember the lua stack object is not the luaobject, they have a conversion, may be you can try this

LuaStackObject stack1Obj(state, 1);
LuaObject nonStack1Obj = stack1Obj;

forth, like the function Test() you have give above, the table tableObj you have pushing onto the lua stack, you must remember to clear the object.

like image 26
rangercyh Avatar answered Oct 02 '22 03:10

rangercyh