Kernell Posted April 24, 2013 Share Posted April 24, 2013 (edited) Сразу оговорюсь, данный урок требует основных знаний о таблицах Lua На самом деле в просторах сети полно учебников по данной теме, поэтому решил я написать этот учебник просто потому что его тут нет (естественно копировать всё из других учебников я не стану, а постараюсь рассказать своими словами) Зачем это нужно В целом, всё это, позволяет нам изменить поведение Lua в определённых ситуациях 1. Возможность реализации ООП в Lua 2. Перегрузка операторов (это так же может быть необходимым для П.1) Что это такое Метатаблица - это обычная таблица в которой описываются определённые события (например сложение, вычитание, сравнивание и т.д.) В качестве названия события выступает индекс таблицы, а её значение - обработчик события (метод) Как использовать Для использования достаточно просто обозначить таблице нашу мета-таблицу MyTable = {}; MyTableMeta = { __index = MyTable }; setmetatable( MyTable, MyTableMeta ); Методы нашей мета-таблицы можно определить в любое время, в силу для таблицы они вступают сразу после определения. Ниже список всех доступных событий и их описание __index( self, key ) чтение по ключу __newindex( self, key, value ) запись по ключу __call( self, ... ) вызов как функции __add( self, arg ) сложение __sub( self, arg ) вычитание __mul( self, arg ) умножение __div( self, arg ) деление __mod( self, arg ) остаток от деления __pow( self, arg ) возведение в степень __unm( self ) унарный минус __concat( self, arg ) конкатенация строк __len( self ) длина __eq( self, arg ) оператор "равно" __lt( self, arg ) оператор "меньше" __le( self, arg ) оператор "меньше или равно" __tostring( self ) Вызывается при попытке перевести объект в строковое представление (например с помощью функции tostring) __gc Вызывается для userdata-объектов при сборке мусора __metatable Если задать это поле в метатаблице, то getmetatable будет просто возвращать его значение, а setmetatable вернет ошибку. Пример Рассмотрим работу метатаблиц на примере события __call. Допустим мы хотим чтобы таблицу Vector3 можно было использовать как функцию. local Vector3 = {}; local Vector3_meta = { -- Добавляем наше событие __call = function( self, fX, fY, fZ ) -- self это указатель на нашу таблицу Vector3 return { X = fX, Y = fY, Z = fZ }; end; }; setmetatable( Vector3, Vector3_meta ); -- Устанавливаем метатаблицу для Vector3 Готово! Теперь если вызвать Vector3( 1, 2, 3 ) то нам вернётся новый объект - таблица с полями X, Y и Z. Теперь немного усложним задачу, дадим нашему объекту свою метатаблицу Vector3_meta_object = -- Это будет метатаблица для всех новых объектов { -- Добавляем событие сложения __add = function( vec1, vec2 ) -- vec1 - таблица слева, vec2 - таблица справа return Vector3( vec1.X + vec2.X, vec1.Y + vec2.Y, vec1.Z + vec2.Z ); -- Создаём новый объект end; }; Vector3 = {}; Vector3_meta = { __call = function( self, fX, fY, fZ ) local vecObject = { X = fX, Y = fY, Z = fZ }; setmetatable( vecObject, Vector3_meta_object ); -- Тут мы указываем метатаблицу нашему новому объекту return vecObject; end; }; setmetatable( Vector3, Vector3_meta ); -- Проверяем vec1 = Vector3( 12, 34, 56 ); vec2 = Vector3( 78, 90, 12 ); vec3 = vec1 + vec2; print( vec3.X, vec3.Y, vec3.Z ); -- 90 124 68 В итоге при сложении двух векторов (двух таблиц с этой метатаблицей), у нас получится новый объект с новыми X Y Z. print( vec3.X, vec3.Y, vec3.Z ); - На мой взгляд это немного не удобно, намного удобнее было бы просто передать вектор в функцию print, давайте исправим, для этого нам понадобится событие __tostring function Vector3_meta_object.__tostring( self ) return "( " .. self.X .. ", " .. self.Y .. ", " .. self.Z .. " )"; end -- Проверяем vec1 = Vector3( 12, 34, 56 ); vec2 = Vector3( 78, 90, 12 ); vec3 = vec1 + vec2; print( vec3 ); -- ( 90, 124, 68 ); Заключение Как видно на примерах, в этом нет ничего сложного. Как и говорилось выше, с помощью данного функционала можно реализовать полноценные классы. Есть пару библиотек которые автоматизируют этот процесс, одна из них идёт в комплекте с Lua (require "classlib"), а вторая валяется где-то тут. Задавайте свои вопросы, постараюсь на них ответить Материал написан специально для mtasa.com. Копирование запрещено Edited September 13, 2016 by Kernell 1 Link to comment
Kenix Posted April 24, 2013 Share Posted April 24, 2013 Хорошо объяснил. Молодец. Есть пару библиотек которые автоматизируют этот процесс, одна из них идёт в комплекте с Lua (require "classlib"), а вторая валяется где-то тут. viewtopic.php?f=141&t=33091 >> http://pastebin.com/vRpVbcDy Link to comment
TheNormalnij Posted April 25, 2013 Share Posted April 25, 2013 Вроде б все понятно, но не хватает что ли вызова с помощью : типа MyTable:nulled() И немного жизненных примеров, хотя бы на словах ) спасибо Link to comment
Kernell Posted April 26, 2013 Author Share Posted April 26, 2013 Вроде б все понятно, но не хватает что ли вызова с помощью : типа MyTable:nulled()И немного жизненных примеров, хотя бы на словах ) спасибо Просто добавьте функцию nulled() в класс Vector3. Так же добавьте событие __index = Vector3. Vector3_meta_object.__index = Vector3; function Vector3:Nulled( vArgument ) self.m_vSomeValue = vArgument; -- self - указатель на объект (скрыто переданный аргумент). end vec1 = Vector3( 192, 168, 0 ); vec1:Nulled( "blablabla" ); Link to comment
Flaker Posted April 28, 2013 Share Posted April 28, 2013 А ты не мог бы по поводу деструкторов прояснить несколько моментов На сколько я знаю, как таковых их нету в Lua, но есть ли возможность реализации? И как вызвать метод класса из этого же метода? Тоесть допустим self:destroy() возможен внутри метода этого же класса? Link to comment
Kernell Posted April 29, 2013 Author Share Posted April 29, 2013 А ты не мог бы по поводу деструкторов прояснить несколько моментовНа сколько я знаю, как таковых их нету в Lua, но есть ли возможность реализации? И как вызвать метод класса из этого же метода? Тоесть допустим self:destroy() возможен внутри метода этого же класса? Да, возможен, но вызов будет только вручную, реализация точно такая же как и выше, просто добавляете нужный метод в таблицу. А для userdata объектов есть событие __gc на которое можно повесить деструктор Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now