Search code examples
c++lualua-api

C++ and Lua integration with arguments (...) (Lua 5.1)


I'm currently trying to pass a function from C++ to Lua. Problem is that this function has a parameter of (int, int, char *, ...). I'm pretty new at using Lua as well.

So I got all the arguments from the lua stack, and I put the relevant (...) ones into a vector. What now?

Here is the code in question:

static int L_PrintDebug(lua_State *state)
{
MVC_View * theView = MVC_View::GetInstance(MVC_Model::GetInstace());

int n = lua_gettop(state);

// check if number of arguments is less than 3
// then we stop as not enough parameters
if(n < 3)
{
    std::cout << "Error: printDebug(number,number, char *, ... )" << std::endl;
    lua_error(state);
}

// gets parameter
int x = lua_tointeger(state, 1);
int y = lua_tointeger(state, 2);
char * words = (char*) lua_tostring(state, 3);

//Get the rest of the arguments.
std::vector<float> Numbers;
for(int i = 4;i != n; i++)
{
    if(lua_type(state,i) == LUA_TNUMBER)
    {
        float theNumber = lua_tonumber(state,i);
        Numbers.push_back(theNumber);
    }
}

// call C++ function
//This is where I'm stuck.
theView->Printw(x,y,words);
}

This is the PrintW function:

void MVC_View::Printw (float x, float y, const char* format, ...) 
{ 
glRasterPos2f(x,y);
char text[256];
va_list ap;
if(format==NULL)
    return;
va_start(ap,format);
    vsprintf_s(text,format,ap);
va_end(ap);

glPushAttrib(GL_LIST_BIT);
    glListBase(m_base-32);
    glCallLists(strlen(text),GL_UNSIGNED_BYTE,text);
glPopAttrib();

 } 

This is where I pass it into Lua state.

m_theModel->theInterface.Pushfunction("PrintOnScreen",L_PrintDebug);

I hope to be able to call it in Lua as so,

PrintOnScreen(10, 100, "Camera 2 Pos: %f %f %f", Camx, Camy, Camz)

or

PrintOnScreen(10,100, "FPS: %f", fps)

Solution

  • The issue appears to be that you need to call a function with a va list, but you are constructing the arguments in a vector and there is no reasonable way to deal with constructing a va_list at run-time.

    I'd recomment making your print function take an arbitrary set of arguments, displaying them in order (a bit more like the chain of C++'s cout), and forget about using va_list altogether

    You can do this by iterating over the arguments and appending to your buffer based on the type of argument. If you are only dealing with text and floating point this is fairly simple. For example (Note: I have not tested this):

    char text[256]; /* Be careful! Buffer overflow potential for not checking lengths of sprintf and not using snprintf */
    char *buf = &text[0]; /* start buf at beginning of text */
    int x = lua_tointeger(state, 1);
    int y = lua_tointeger(state, 2);
    for (int i = 3, i < n; i++) {
        if (lua_type(state,i) == LUA_TNUMBER) {
             sprintf(buf, "%f", lua_tonumber(state,i));
        } else if (lua_type(state,i) == LUA_TSTRING) {
             sprintf(buf, "%s", lua_tostring(state,i));
        } else {
             // ERROR SOMEHOW
        }
        buf += strlen(buf); /* Move the end of buf for more appending
    }
    /* At this point, we've filled in text by walking through it with our buf pointer, and we have a complete string */
    glPushAttrib(GL_LIST_BIT);
    glListBase(m_base-32);
    glCallLists(strlen(text),GL_UNSIGNED_BYTE,text);
    glPopAttrib();
    

    You could then call in Lua like so:

    PrintOnScreen(10, 100, "Camera 2 Pos: ", Camx, " ", Camy, " ", Camz)
    

    or

    PrintOnScreen(10,100,"FPS: ", fps)
    

    This is slightly less convenient with lists of numbers that need to be space separated, but is, I think, easier to code for.

    Again, note that the example code above is untested, and may contain off-by-one errors or other bugs. Especially you should add bounds checking and use snprintf instead of sprintf. Or, use the c++ stringstream for the construction.