Макрос property
также обрабатывает несколько дополнительных случаев использования, но сейчас давайте сосредоточимся на наиболее распространенном. Наша реализация этого макроса будет выглядеть так:
macro def_getter_setter(decl)
@{{decl}}
def {{decl.var}} : {{decl.type}}
@{{decl.var}}
end
def {{decl.var}}=(@{{decl.var}} : {{decl.type}})
end
end
Мы можем определить переменную экземпляра, используя @{{decl}}
, потому что она автоматически расширится до нужного формата. Мы могли бы также использовать @{{decl.var}} : {{decl. type}}
, но другой путь был короче и лучше обрабатывал значения по умолчанию. Более длинная форма должна будет явно проверить и установить значение по умолчанию, если таковое имеется, тогда как более короткая форма сделает это за нас. Однако тот факт, что вы можете реконструировать узел вручную, используя предоставляемые им методы, не является совпадением. Узлы AST — это абстрактные представления чего-либо внутри программы, например, объявление типа, метода или выражение оператора if
, поэтому имеет смысл только то, что вы можете построить то, что представляет узел, используя сам узел.
Остальная часть нашего макроса def_getter_setter
строит методы получения и установки для определенной переменной экземпляра. Отсюда мы можем пойти дальше и использовать его:
class Foo
def_getter_setter name : String?
def getter setter number : Int32 = 123
property float : Float64 = 3.14
end
obj = Foo.new
pp obj.name
obj.name = "Bob"
pp obj.name
pp obj.number
pp obj.float
Запуск этой программы приведет к следующему выводу:
nil
"Bob"
123
3.14
И вот оно! Успешная повторная реализация наиболее распространенной формы макроса property
! Здесь легко увидеть, как можно использовать макросы, чтобы уменьшить количество шаблонов и повторений в вашем приложении.
Последняя концепция макросов, которую мы собираемся обсудить в этой главе, — это макро-хуки, которые позволяют нам подключаться к различным событиям Crystal.
Изучение макро-хуков
Перехватчики макросов — это специальные определения макросов, которые в некоторых ситуациях вызываются компилятором Crystal во время компиляции. К ним относятся следующие:
•@type
— это наследующий тип.
•@type
— включаемый тип.
•@type
— расширяемый тип.
•
• Def
.
•
Первые три и
abstract class Parent
macro inherited
puts "#{{{@type.name}}} inherited Parent"
end
end
module MyModule
macro included
puts "#{{{@type.name}}} included MyModule"
end
macro extended
puts "#{{{@type.name}}} extended MyModule"
end
end
class Child < Parent
include MyModule
extend MyModule
end
Предыдущий код выведет следующий результат:
Child inherited Parent
Child included MyModule
Child extended MyModule
Эти перехватчики могут быть весьма полезны, если вы хотите добавить методы/переменные/константы к другому типу в случаях, когда обычная семантика наследования/модуля не работает. Примером этого может служить случай, когда вы хотите добавить к типу методы экземпляра и класса при включении модуля. Из-за того, как работает включение/расширение модулей, в настоящее время невозможно добавить оба типа методов к типу из одного модуля.