2

I have written this method:

std::string Utils::GetFileContents(const char* filePath)
{
    std::ifstream in(filePath, std::ios::binary);

    if (in)
    {
        std::string contents;
        in.seekg(0, std::ios::end);
        contents.resize(in.tellg());
        in.seekg(0, std::ios::beg);
        in.read(&contents[0], contents.size());
        in.close();
        return(contents);
    }

    throw(errno + " ERROR: Could not open file.");
}

In another method, I have these instructions:

lua_State* state = luaL_newstate();

const char* code = Utils::GetFileContents(path).c_str();
luaL_dostring(state, code);

lua_close(state);

If you run your debugger in the previous method, you get a dangling pointer at the code variable. I do not understand why.

I found a way to make this work - to basically store code in a std::string and then change the next line to luaL_dostring(state, code.c_str());.

It doesn't make sense to me, as in both cases, code is stored as a const char*.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Valentin Popescu
  • 174
  • 2
  • 12
  • Where is your `std::string`? What happens when it "vanishes"? – tkausl Feb 14 '22 at 19:03
  • 2
    `Utils::GetFileContents(path)` returns a temporary `std::string` instance, where `c_str()` dangles where the scope of the return value was left. So you need to have a copy existing in your client scope. – πάντα ῥεῖ Feb 14 '22 at 19:04
  • 3
    "...in both cases, code is stored as a const char*." A `const char *` cannot store a string. It can only refer to a string that exists somewhere else. When you use `c_str()` you get a pointer that refers to the string's data, but that pointer does not store that data. You need the string object to keep existing if you want to pointer to keep working. – François Andrieux Feb 14 '22 at 19:06
  • 1
    "*It doesn't make sense to me*. " -- The bigger problem, since you said this, is that you may believe that `const char *` means "string data". It doesn't. A `const char *` is a pointer, nothing more, nothing less -- it is not a string. A `const char *` has all of the vulnerabilities that any other pointer has (pointing to invalid memory, pointing to an object that no longer exists, etc.) – PaulMcKenzie Feb 14 '22 at 19:18
  • 1
    _"I don't understand why I have a dangling pointer"_ You seem to understand that you have a _pointer_. When working with any pointer, you should be able to answer the question "when will this pointer dangle?" That is, _what is it pointing to_ and _when will the thing pointed to no longer exist_? – Drew Dormann Feb 14 '22 at 19:23
  • @FrançoisAndrieux best explanation so far, I would say. – Valentin Popescu Feb 14 '22 at 20:05

1 Answers1

3

The function returns an object of the type std::string

std::string Utils::GetFileContents(const char* filePath)

You are assigning a pointer with the address of the first character of the returned temporary string

const char* code = Utils::GetFileContents(path).c_str();

After this declaration the returned temporary object will be destroyed. So the pointer code is invalid and using it in the next call

luaL_dostring(state, code);

invokes undefined behavior.

You could write for example

std::string code = Utils::GetFileContents(path);
luaL_dostring(state, code.c_str());
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335