6

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"...

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Forivin
  • 14,780
  • 27
  • 106
  • 199
  • 1
    What happens if you replace `return state->GetTop() - top;` with `return 1;`? (In the last line of the `Test` function) and what does `errorString` contain? – dualed Nov 08 '13 at 10:05
  • Yes, I get the same error. – Forivin Nov 08 '13 at 14:44
  • How are you calling the test script from C++? – greatwolf Nov 09 '13 at 06:43
  • Yes, I try to put the important parts together: http://pastebin.com/Ccd93DPQ I'll put it in the question now too. – Forivin Nov 09 '13 at 14:43
  • Did you already try narrowing down the problem by eliminating variables? eg. trying `L->DoFile("test.lua")`, putting test.lua in the same working directory etc. Can you get a stacktrace when the assert fails? – greatwolf Nov 10 '13 at 02:04
  • Note I don't mean *variables* in the programming sense but external possible factors. – greatwolf Nov 10 '13 at 02:11
  • The file runs fine. But as soon as Test() is called it stops gives me this error message and crashes with 0x3. I checked "top" and "state->ToString(1)" in the beginning and in the end of the function: top was always 1 state->ToString(1) was always "aaa" (the parameter I passed). – Forivin Nov 10 '13 at 07:45
  • Have you tried the LuaPlusCallback wrapped Register version? The managed example from http://luaplus.funpic.de/#table8? – Etan Reisner Nov 11 '13 at 14:39
  • If you are building LuaPlus yourself you could try logging/printing/etc. the errorString on line 40 in luafunction.h instead of just throwing it away as is currently happening. – Etan Reisner Nov 11 '13 at 14:40
  • I tried the callback function and it gave me a lot of errors: http://250kb.de/u/131111/j/Nluz4tKZfkAF.jpg ... I don't really understand what you mean by logging the errorString? I mean, it's already printing this error. The problem is that I don't know what it means or better what I'd have to do to avoid it. :/ – Forivin Nov 11 '13 at 17:17
  • 1
    LuaPlus is getting the error from the lua_pcall and then doing absolutely nothing with it. It is then failing an assertion which gives no additional information. That errorString variable (unless it is being used somewhere magically that I didn't see) might very well contain useful information. – Etan Reisner Nov 11 '13 at 19:34
  • @Forivin Sorry I was busy the last few days; Again, please tell us the contents of `errorString` (http://pastebin.com/Pmwq8E0p Line 40, and others); Set a breakpoint for assertion in your debugger, and inspect the local variable. If the library is compiled with optimizations this may not exist though. So you could inspect the top of the Lua stack, since the error message will still be there. – dualed Nov 13 '13 at 15:15
  • @Forivin Can you upload your complete example project somewhere and add a link to it in your question? There's clearly a subtle issue happening that isn't being captured in your question. – greatwolf Nov 14 '13 at 05:31
  • Oh man, I just checked the errorString and was able to fix the problem. For some reason the test.lua file didn't update anymore when I compiled the solution. So while I thought the content would be "print(Test("aaa").x)" it was actually "print(Test("aaa"))". -.- I feel so stupid... I totally misunderstood you dualed, when you first asked me for the contents of "errorString". Well, add it as an answer and I will accept it. Thank you so much for all your help everyone. – Forivin Nov 14 '13 at 22:06
  • @Forivin Make sure to do this soon otherwise the bounty will just disappear into the ether. :( – greatwolf Nov 14 '13 at 22:37
  • I'm confused. What was in errorString? I don't understand the solution either. Was the problem when trying to print the returned table somehow? – Etan Reisner Nov 15 '13 at 00:19
  • The solution was to use the code from my question (the second one of course) and just return 1. It didn't work, because I made a mistake in the test.lua script. The luascript was trying to print the table instead of an index of the table. That's what caused all this.. So basically I'm super stupid and I'm sorry for it. :) @greatwolf I think I can't because he posted it as a comment and not as an answer. – Forivin Nov 15 '13 at 00:28
  • Attempting to print a table should absolutely not crash. Something is still very wrong if that is happening. And returning 1 shouldn't be any different than the `gettop() - top` version assuming that you only push one element on the stack. If you push more than that then it is obviously going to be different since it will return more things. – Etan Reisner Nov 15 '13 at 00:44
  • I also still want to know what was in errorString exactly. – Etan Reisner Nov 15 '13 at 00:45
  • I'm also wondering the same thing as @EtanReisner. `L->DoFile` just calls `luaL_dofile` which uses `lua_pcall`. If there *was* an issue with your test script why didn't `if(L->DoFile(pPath))` catch and print the error? – greatwolf Nov 15 '13 at 01:25
  • Sorry. I didnt used the original "print" function I used the "Print" function. It comes from this tutorial: http://www.zynox.net/luaplus-1-compiling-basic-usage/ and it can't handle tables. The errorString contained: "....\test.lua:1 bad argument #1 to 'Print' (string expected, got table)" I really don't know why this wasn't catched by L->DoFile. I guess DoFile just returns false if the file does not exist or so.. – Forivin Nov 15 '13 at 01:51
  • The error was being caught by the pcall. That's why you had that error in errorString. The problem is that LuaPlus in that function takes the error string throws it away and then generates an entirely context-less assertion instead. That's marvelously stupid and unhelpful. – Etan Reisner Nov 15 '13 at 02:08
  • 1
    Unfortunately the end result of all of this mess is that I think the question, as written and asked, is entirely incorrect and ultimately unhelpful for anyone else. I don't know what SO guidelines are about that but I would probably suggest retracting the question or, at very least, editing it to include the actual lua script being run and the Print function involved. – Etan Reisner Nov 15 '13 at 02:12
  • True. :/ I will put the wrong luascipt in the question then.. – Forivin Nov 15 '13 at 02:27
  • Letting a bounty expire like this would be criminal so I added an answer just before it ended. @Forivin Let me know if anything below is unclear. – greatwolf Nov 16 '13 at 03:22
  • @Forivin Any chance for you to award the bounty to my answer? The grace period is ending soon and it would really suck for the rep to just disappear into the void. If you still have any related concerns, I'll do my best to address them. – greatwolf Nov 17 '13 at 00:37
  • oO I already marked your answer as the accepted one. Did I miss anyhting? Do I have to do something else? Edit: I just found out that I had to click the +100 button. sorry :p – Forivin Nov 17 '13 at 00:59
  • yeah, it's kind of annoying that way. Since it's the grace period, you have to manually award the bounty :/ – greatwolf Nov 17 '13 at 01:06

2 Answers2

1

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.

rangercyh
  • 37
  • 4
1

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.
greatwolf
  • 20,287
  • 13
  • 71
  • 105