#ifndef LUAVALUE_H #define LUAVALUE_H #pragma once #include "LuaConvert.h" #include "LuaReference.h" #include "LuaException.h" #include "LuaHeaders.h" #include #include #include namespace luacpp { /** * @brief The values of a LuaValue */ enum class ValueType { NONE, NIL, BOOLEAN, LIGHTUSERDATA, STRING, NUMBER, TABLE, FUNCTION, USERDATA, THREAD, }; /** * @brief Represents a Lua-value * * This class holds a reference to a lua value and provides type checking to ensure that the value is still the same. */ class LuaValue { public: /** * @brief Creates a LuaValue. * This is done by pushing the value onto the lua stack and creating a reference to it. * * @param state The lua state * @param value The value to be referenced * @return luacpp::LuaValue A new LuaValue instance which references the specified value */ template static LuaValue createValue(lua_State* state, ValueType&& value) { LuaValue retVal(state); convert::pushValue(state, std::forward(value)); retVal.setReference(UniqueLuaReference::create(state)); // Remove the value again lua_pop(state, 1); return retVal; } static LuaValue createNil(lua_State* L); /** * @brief Default constructor, creates an invalid LuaValue */ LuaValue(); /** * @brief Initializes the lua value * * The instance does not point to a lua-value after the constructor has finished. To reference * a value use #setReference(LuaReferencePtr) * * @param state The lua state */ LuaValue(lua_State* state); /** * @brief Copy-constructor * @param other The other LuaValue. * @note The noexcept is a promise that all members remain nothrow-copyable * (currently a shared_ptr, a raw pointer, and an enum). */ LuaValue(const LuaValue& other) noexcept; LuaValue& operator=(const LuaValue& other) noexcept; /** * @brief Move-constructor * @param other The other LuaValue. */ LuaValue(LuaValue&& other) noexcept; LuaValue& operator=(LuaValue&& other) noexcept; /** * @brief Releases the reference */ virtual ~LuaValue(); /** * @brief Sets a new LuaReference. * * @param ref The new lua reference. */ virtual void setReference(const LuaReference& ref); /** * @brief Gets the LuaReference. * * This reference is used to reference the actual lua value. * * @return The LuaReference instance. */ virtual const LuaReference getReference() const; /** * @brief Gets the lua type of this value. * @return One of the LUA_T* defines. */ virtual ValueType getValueType() const; /** * @brief Checks if the value is of the specified type. * * @return bool @c true when it is, @c false if it isn't. */ inline bool is(ValueType check) const { return _luaType == check; } /** * @brief Sets a new value, possible changing the type * * @param value The new value */ template void setValue(const Type& value) { // Push the new value convert::pushValue(_luaState, value); // And create a reference for it setReference(UniqueLuaReference::create(_luaState)); } /** * @brief Gets the value or throws an exception * * @return Type The value * * @exception LuaException Thrown when the conversion failed. */ template Type getValue() const { _reference->pushValue(_luaState); Type target; if (!convert::popValue(_luaState, target)) { lua_pop(_luaState, 1); throw LuaException("Failed to pop value"); } else { return target; } } /** * @brief Same as above but allows passing a reference to a value where to store the lua value * * @exception LuaException Thrown when the conversion failed. */ template void getValue(Type&& od) const { _reference->pushValue(_luaState); if (!convert::popValue(_luaState, std::forward(od))) { lua_pop(_luaState, 1); throw LuaException("Failed to pop value"); } } /** * @brief Specifies if the lua value is valid. * * @return bool @c true if it can be used and have an underlying reference, @c false otherwise. */ bool isValid() const; /** * @brief Pushes this lua value onto the stack. * @param thread The thread stack onto which this value should be pushed. May be nullptr for the default state of * this value * @param manualStackAllocation Set to true if you manually allocate sufficient stack size before calling this function. Keep false unless you know what you are doing. */ bool pushValue(lua_State* thread, bool manualStackAllocation = false) const; lua_State* getLuaState() const; protected: lua_State* _luaState{nullptr}; //!< The lua state of this value. LuaReference _reference; ValueType _luaType = ValueType::NONE; }; /** * @brief Checks for equality of the lua values. * @param lhs The left value * @param rhs The right lua value. * @return `true` when the value are equal as specified by the lua "==" operator. */ template bool operator==(const LuaValue& lhs, const Type& rhs) { lhs.pushValue(lhs.getLuaState()); convert::pushValue(lhs.getLuaState(), rhs); bool result = lua_equal(lhs.getLuaState(), -2, -1) != 0; lua_pop(lhs.getLuaState(), 2); return result; } /** * @brief Checks if the other value is bigger than this value. * @param lhs The left lua value. * @param rhs The right lua value. * @return `true` when the second value is bigger than this value as specified * by lua. */ template bool operator<(const LuaValue& lhs, const Type& rhs) { lhs.pushValue(lhs.getLuaState()); convert::pushValue(lhs.getLuaState(), rhs); bool result = lua_lessthan(lhs.getLuaState(), -2, -1) != 0; lua_pop(lhs.getLuaState(), 2); return result; } template bool operator!=(const LuaValue& lhs, const Type& rhs) { return !(lhs == rhs); } template bool operator>(const LuaValue& lhs, const Type& rhs) { return !(lhs <= rhs); } template bool operator<=(const LuaValue& lhs, const Type& rhs) { return (lhs == rhs) || (lhs < rhs); } template bool operator>=(const LuaValue& lhs, const Type& rhs) { return !(lhs < rhs); } namespace convert { void pushValue(lua_State* luaState, const LuaValue& value); bool popValue(lua_State* luaState, LuaValue& target, int stackposition = -1, bool remove = true); } } #endif