Jump to content

[Lua] setmetatable, учимся работать с метатаблицами


Recommended Posts

Сразу оговорюсь, данный урок требует основных знаний о таблицах 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 by Kernell
  • Like 1
Link to post

Вроде б все понятно, но не хватает что ли вызова с помощью : типа MyTable:nulled()

И немного жизненных примеров, хотя бы на словах ) спасибо

Link to post
Вроде б все понятно, но не хватает что ли вызова с помощью : типа 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 post

А ты не мог бы по поводу деструкторов прояснить несколько моментов

На сколько я знаю, как таковых их нету в Lua, но есть ли возможность реализации?

И как вызвать метод класса из этого же метода? Тоесть допустим self:destroy() возможен внутри метода этого же класса?

Link to post
А ты не мог бы по поводу деструкторов прояснить несколько моментов

На сколько я знаю, как таковых их нету в Lua, но есть ли возможность реализации?

И как вызвать метод класса из этого же метода? Тоесть допустим self:destroy() возможен внутри метода этого же класса?

Да, возможен, но вызов будет только вручную, реализация точно такая же как и выше, просто добавляете нужный метод в таблицу.

А для userdata объектов есть событие __gc на которое можно повесить деструктор

Link to post

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...