// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef RZ_LUA_OBJECT #define RZ_LUA_OBJECT #include "nel/misc/smart_ptr.h" #include "nel/misc/rgba.h" // #include "lua_helper.h" class CLuaEnumeration; /** * Wrapper to a lua value * * Useful to navigate through lua tables without having to deal with the stack. * * The following types are tracked by reference : * - lua table * - lua user data * - lua functions * * The following types are kept by value : * * - lua numbers * - lua strings ? * - lua boolean * - lua light user datas * - lua 'pointers' * * Each reference object has an id giving its path in order to track bugs more easily */ class CLuaObject { public: CLuaObject() {} ~CLuaObject(); // Build this object by popping it from the given lua state CLuaObject(CLuaState &state, const char *id =""); CLuaObject(CLuaState &state, const std::string &id); // Build this object from another object CLuaObject(const CLuaObject &other); // Copy refrence to another lua object CLuaObject &operator=(const CLuaObject &other); // Get id for that object const std::string &getId() const { return _Id; } // Set id for that object void setId(const std::string &id) { _Id = id; } // See if the obj bool isValid() const; // Pop a new value for this lua object from the top of the stack. The stack must not be empty void pop(CLuaState &luaState, const char *id =""); // Push the object that is being referenced on the stack // An assertion is raised if 'pop' hasn't been called or // if the lua state has been destroyed void push() const; // Get the lua state in which the object resides. CLuaState *getLuaState() const; // Release the object. 'pop' must be called to make the object valid again void release(); // type queries int type() const; const char *getTypename() const; bool isNil() const; bool isNumber() const; bool isBoolean() const; bool isString() const; bool isFunction() const; bool isCFunction() const; bool isTable() const; bool isUserData() const; bool isLightUserData() const; bool isRGBA() const; // equality bool rawEqual(const CLuaObject &other) const; // conversions (no throw) : the actual value of object is not modified!! NLMISC::CRGBA toRGBA() const; // default to black if not a crgba bool toBoolean() const; lua_Number toNumber() const; std::string toString() const; lua_CFunction toCFunction() const; void *toUserData() const; const void *toPointer() const; // implicit conversions (no throw) operator bool() const; operator float() const; operator double() const; operator std::string() const; /** create a sub table for this object, with a string as a key * This object must be a table or an exception if thrown */ CLuaObject newTable(const char *tableName) throw(ELuaNotATable); /** Set a value in a table. * If this object is not a table then an exception is thrown * NB : value should came from the same lua environment * \TODO other type of keys */ void setValue(const char *key, const CLuaObject &value) throw(ELuaNotATable); void setValue(const std::string &key, const CLuaObject &value) throw(ELuaNotATable) { setValue(key.c_str(), value); } void setValue(const char *key, const std::string &value) throw(ELuaNotATable); void setValue(const char *key, const char *value) throw(ELuaNotATable); void setValue(const char *key, bool value) throw(ELuaNotATable); void setValue(const char *key, TLuaWrappedFunction value) throw(ELuaNotATable); void setValue(const char *key, double value) throw(ELuaNotATable); void setValue(const std::string &key, const std::string &value) throw(ELuaNotATable) { setValue(key.c_str(), value); } void setNil(const char *key) throw(ELuaNotATable); void setNil(const std::string &key) throw(ELuaNotATable) { setNil(key.c_str()); } /** Erase a value in a table by its key. * If this object is not a table then an exception is thrown. * \TODO other type of keys */ void eraseValue(const char *key) throw(ELuaNotATable); void eraseValue(const std::string &key) throw(ELuaNotATable) { eraseValue(key.c_str()); } // test is this object is enumerable bool isEnumerable() const; // Enumeration of a table. If the object is not a table, an exception is thrown. CLuaEnumeration enumerate() throw(ELuaNotATable); // retrieve metatable of an object (or nil if object has no metatable) CLuaObject getMetaTable() const; // set metatable for this object bool setMetaTable(CLuaObject &metatable); /** Access to a sub element of a table (no throw). * if the element is not a table, then 'nil' is returned * TODO nico : add other key types if needed * TODO nico : version that takes destination object as a reference in its parameter to avoid an object copy */ CLuaObject operator[](double key) const; CLuaObject operator[](const char *key) const; CLuaObject operator[](const std::string &key) const { return operator[](key.c_str()); } /** Checked access to a sub element of a table. An exception is thrown is the element is not a table. */ CLuaObject at(const char *key) const throw (ELuaNotATable); CLuaObject at(const std::string &key) const { return at(key.c_str()); } // Test is that table has the given key. The object must be a table or an exception is thrown bool hasKey(const char *key) const; /** debug : recursively get value (useful for table) * \param maxDepth (0 for no limit) * \param alreadySeen pointer to lua tables that have already been displayed by the command (to avoid infinite recursion when a cycluic graph is encountered) */ std::string toStringRecurse(uint depth = 0, uint maxDepth = 20, std::set *alreadySeen = NULL) const; /** dump the value in the log (includes tables) * \param alreadySeen pointer to lua tables that have already been displayed by the command (to avoid infinite recursion when a cycluic graph is encountered) */ void dump(uint maxDepth = 20, std::set *alreadySeen = NULL) const; // concatenate identifiers, adding a dot between them if necessary. If right is a number then brackets are added static std::string concatId(const std::string &left, const std::string &right); // If this object is a function, then call it and return true on success bool callNoThrow(int numArgs, int numRet); // Call a method of this table by name (no throw version) bool callMethodByNameNoThrow(const char *name, int numArgs, int numRet); private: NLMISC::CRefPtr _LuaState; std::string _Id; }; /** enumeration of the content of a lua table * * Example of use : * *\code CLuaObject table; table.pop(luaState); // retrieve table from the top of a lua stack CLuaEnumeration enueration = table.enumerate(); while (enumeration.hasNext()) { nlinfo('key = %s", enumeration.nextKey().toString().c_str()); nlinfo('value = %s", enumeration.nextValue().toString().c_str()); enumeration.next(); }; \endcode * * There is a macro called 'ENUM_LUA_TABLE' to automate that process. * Previous code would then be written as follow : * * *\code CLuaObject table; table.pop(luaState); // retrieve table from the top of a lua stack ENUM_LUA_TABLE(table, enumeration); { nlinfo('key = %s", enumeration.nextKey().toString().c_str()); nlinfo('value = %s", enumeration.nextValue().toString().c_str()); }; \endcode * */ class CLuaEnumeration { public: // is there a next key,value pair in the table bool hasNext() { return _HasNext; } // Return next key. Assertion if 'hasNext' is false const CLuaObject &nextKey() const; // Return next value. Assertion if 'hasNext' is false CLuaObject &nextValue(); // Go to the next value. Assertion if there's no such value void next(); private: friend class CLuaObject; // current value & key CLuaObject _Table; CLuaObject _Key; CLuaObject _Value; CLuaObject _NextFunction; // pointer to the global 'next' function bool _HasNext; // construction from a table on the stack CLuaEnumeration(CLuaObject &table); }; /** macro to ease lua table enumeration * \param object A CLuaObject which must be a table, and on which enumeration is done. An exception will be thrown as 'CLuaObject::enumerate' is * called if this is not the case * \param enumerator The enumerator object */ #define ENUM_LUA_TABLE(object, enumerator) for(CLuaEnumeration enumerator = (object).enumerate(); enumerator.hasNext(); enumerator.next()) //opitmized lua string for fast comparison class CLuaString { public: explicit CLuaString(const char *value = ""); const char *getPtr() const; void pushOnStack() const; operator const char *() const { return getPtr(); } private: const char *_Str; mutable const char *_Ptr; mutable CLuaState::TRefPtr _LuaState; // ref ptr so that statics get rebuilt on lua restart mutable CLuaObject _InLua; CLuaState &getLua() const; void build() const; }; inline bool operator==(const char* lh, const CLuaString& rh) { return std::string(lh) == std::string(rh.getPtr()); } inline bool operator==( const CLuaString& lh, const CLuaString& rh) { return std::string(lh.getPtr()) == std::string(rh.getPtr()); } inline bool operator==(const CLuaString& lh, const char* rh) { return std::string(rh) == std::string(lh.getPtr()); } inline bool operator==( const CLuaString& lh, const std::string& rh) { return std::string(lh.getPtr()) == rh; } inline bool operator==(const std::string& lh, const CLuaString& rh) { return lh == std::string(rh.getPtr()); } class CLuaHashMapTraits { public: static const size_t bucket_size = 4; static const size_t min_buckets = 8; CLuaHashMapTraits() {} // hasher for lua string -> they are unique pointers for each string, so just hash a pointer instead of a string... size_t operator()(const char *value) const { return ((size_t) value) >> 3; } // equality for lua string for hash_map -> they are unique pointer -> compare pointers instead of string content bool operator()(const char *lhs, const char *rhs) const { return lhs < rhs; } }; #endif