Files
Goober5000andClaude Fable 5 9bf5b6acf4 harden move operations in w_bank and LuaThread
w_bank: add a move constructor.  object_copy_including_array_member
returns by value, and without a move constructor the return fell back
to the shallow copy constructor whenever NRVO was not applied (e.g.
MSVC debug builds), after which the local's destructor deleted the
freshly allocated arrays -- leaving the stored bank dangling and
double-deleting at model unload.  Also add noexcept to the move
operations, replace the destructor call in move-assignment with
explicit delete[]s (using an object after its lifetime has ended is
undefined behavior), guard against self-move, and document that the
defaulted copy constructor is intentionally shallow for
object_copy_including_array_member's copy-then-replace pattern.

LuaThread: declare the defaulted move operations noexcept.  This
exposed that LuaTable was not movable at all (its user-declared
destructor suppressed the implicit moves), so LuaThread's implicit
move spec was formally throwing -- a hard error on gcc 9, which
still requires the spec on a defaulted redeclaration to match the
implicit one.  Restore LuaTable's rule of five and declare the
LuaValue and LuaFunction copy operations noexcept (they only copy a
shared_ptr, a raw pointer, and an enum), making the noexcept claims
hold throughout the hierarchy.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-06 13:33:49 -04:00

78 lines
1.6 KiB
C++

#pragma once
#include "LuaConvert.h"
#include "LuaTypes.h"
#include "LuaValue.h"
#include "LuaFunction.h"
#include <iterator>
namespace luacpp {
/**
* @brief Class to improve handling of lua threads (aka coroutines).
*
* This class provides a high-level interface to lua threads.
*
* @see LuaConvert
*/
class LuaThread : public LuaValue {
LuaReference deleterUserdata;
LuaTable deleterTable;
LuaFunction deleterFunc;
public:
using ErrorCallback = std::function<bool(lua_State* mainState, lua_State* thread)>;
struct ResumeState
{
bool completed = false;
LuaValueList returnVals;
};
/**
* @brief Creates a new empty thread.
*/
static LuaThread create(lua_State* L, const LuaFunction& func);
/**
* @brief Default constructor
*/
LuaThread();
//Copying threads is VERY illegal
LuaThread(const LuaThread&) = delete;
LuaThread& operator=(const LuaThread&) = delete;
LuaThread(LuaThread&&) noexcept;
LuaThread& operator=(LuaThread&&) noexcept;
~LuaThread() override;
/**
* @brief Sets a new reference.
* This overload checks if the passed reference is a thread
*
* @param ref The new reference
* @return void
*/
void setReference(const LuaReference& ref) override;
void setErrorCallback(ErrorCallback errorCallback);
ResumeState resume(const LuaValueList& params) const;
lua_State* getThreadHandle() const;
private:
LuaThread(lua_State* luaState, lua_State* threadHandle);
lua_State* _thread = nullptr;
ErrorCallback _errorCallback;
};
namespace convert {
bool popValue(lua_State* luaState, LuaThread& target, int stackposition = -1, bool remove = true);
}
} // namespace luacpp