Search code examples
c++stringc++11lualuajit

Strange output for luajit ffi function returning string


I have a function like the following returning a c-string from an another function returning a std::string.

const char* GetFilePath(const char* aFilename)
{
    return FileSystem->GetFilePath(aFilename).c_str();
}

If I call this function from lua I only get garbage. If I modify the function to return for example a "Test" it works.

I think it is because the returned std::string's destructor will be called and therefore delets the string making the c-string invalid.

My question is how can I prevent this? How can I get this working?

UPDATE: I expose this function to Lua with the following.

local ffi = require('ffi')
ffi.cdef[[
const char* GetFilePath(const char* aFilename)
]]

x = ffi.string(GetFilePath("Script.lua"))
io.write(x)

This code just prints some random garbage. But if I modify the C-Wrapper function to just to return a C-Style string I get the desired output.

Update 2: For example if I do something like the following:

const char* GetFilePath(const char* aFilename)
{
    return aFilename;
}

It works as expected. Also when I expose some other functions returning an const char*. BUT if I do the following:

const char* GetFilePath(const char* aFilename)
{
    return std::string(aFilename).c_str();
}

I get random garbage. My original C++-Function returns an std::string.


Solution

  • If you insist on using the luajit FFI for this instead of using the C api, you are going to have to write some more complicated C++.

    The issue is that, any function that returns const char * in C++, cannot be generated by calling c_str() on a local or temporary std::string because it will become invalid before lua gets a chance to use it.

    The simplest trick to get around this is to use a static local variable, which won't be destroyed right after the function returns.

    const char* GetFilePath(const char* aFilename)
    {
        static std::string long_lived;
        long_lived = FileSystem->GetFilePath(aFilename);
        return long_lived.c_str();
    }
    

    There's some extra overhead here -- the long_lived string will be allocated until GetFilePath is called again, or your program ends. But these strings are small, so this overhead doesn't matter much.