Проблема с атрибутами полей в макросах
От: evilbeaver  
Дата: 04.04.10 10:30
Оценка:
Такой вопрос.
Есть макрос на тип, который внутри себя перебирает поля. Некоторые поля могут быть помечены атрибутами с параметром.
Каким образом (и возможно ли вообще) получить эти атрибуты в виде собственно объектов атрибутов?
Пока все мои изычскания в этой области закончились тем, что можно узнать наличие атрибута через Modifiers.IsCustomAttributeDefined.
Но дело в том, что атрибут содержит строку и мне нужно ее выцепить.
Макросы Атрибуты
Re: Проблема с атрибутами полей в макросах
От: hardcase Пират http://nemerle.org
Дата: 04.04.10 11:11
Оценка:
Здравствуйте, evilbeaver, Вы писали:

E>Есть макрос на тип, который внутри себя перебирает поля. Некоторые поля могут быть помечены атрибутами с параметром.

E>Каким образом (и возможно ли вообще) получить эти атрибуты в виде собственно объектов атрибутов?


def fb : FieldBuilder = ....
def mods = fb.GetModifiers();
def custom_attributes = mods.GetCustomAttributes(); // список PExpr
http://nemerle.org/Banners/?t=Developer!&g=dark /* иЗвиНите зА неРовнЫй поЧерК */
Получение информации об Custom Attribute-ах из макросов
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.04.10 17:23
Оценка:
#Имя: FAQ.nemerle.GetCustomAttributeInfo001
Здравствуйте, evilbeaver, Вы писали:

E>Каким образом (и возможно ли вообще) получить эти атрибуты в виде собственно объектов атрибутов?


Никаким. До окончания компиляции атрибуты не боле чем выражения (PExpr). Но их можно проанализировать.

E>Пока все мои изычскания в этой области закончились тем, что можно узнать наличие атрибута через Modifiers.IsCustomAttributeDefined.

E>Но дело в том, что атрибут содержит строку и мне нужно ее выцепить.

Это не очень просто, но реализуемо. Как верно заметил hardcase, атрибуты можно вынуть через GetModifiers().GetCustomAttributes(). Проблема в том, что при этом возвращается list[PExpr], то есть список выражений которые описывают атрибуты.

Нужно перебрать эти выражения и найти то что описывает искомый атрибут. Потом нужно разобрать это выражение (с помощью паттерн-матчинга) и извлечь строку.

Ниже приведен пример демонстрирующий этот подход.
Вот макрос применимый к полям позволяющий разобрать кастом-атрибут типа "Ns1.MyAttr":
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;

namespace GetInfoFromCustomAttributeMacro
{
  [Nemerle.MacroUsage (MacroPhase.WithTypedMembers, MacroTargets.Field, Inherited = true, AllowMultiple = false)]
  public macro TestMacro(tb : TypeBuilder, fld : FieldBuilder)
  {
    Helper.DoWork(Nemerle.Macros.ImplicitCTX(), tb, fld)
  }
  
  module Helper
  {
    public DoWork(typer : Typer, _tb : TypeBuilder, fld : FieldBuilder) : void
    {
      def attrs = fld.GetModifiers().GetCustomAttributes();
      //assert2(false); // если раскоментарить, то можно будет поглядеть на работу макроса под отладчиком!
      foreach (a in attrs)
      {
        | <[ $attr($(str : string)) ]> => // Распознаем атрибут которому передан один не именованный параметр типа string.
          def typedAttr = typer.BindFixedType(attr); // пытаемся связать "имя макроса" с имеющимися на время компиляции типами.
          
          // Теперь мы можем сверять полное имя типа. Если связыание не удастся, будет выдано сообщение об ошибке.
          when (typedAttr.TypeInfo.FullName == "Ns1.MyAttr")
            Message.Hint(a.Location, $"Attribute: '$a' attr: '$attr' str: '$str' typedAttr: '$typedAttr'");
          
        | _ => ()
      }
    }
  }
}

А вот применение данного макроса:
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Console;
using System.Linq;
using GetInfoFromCustomAttributeMacro;

using Ns1;

namespace Ns1
{
  [Record]
  public class MyAttr : Attribute
  {
    public AttrField : string;
  }
}

namespace Ns2
{
  [Record]
  public class MyAttr : Attribute
  {
    public AttrField : string;
  }
}

module Program
{
  [Ns1.MyAttr("Test str"), TestMacro, MyAttr("Other string"), Ns2.MyAttr("Test str")]
  field1 : int = 0;
  
  Main() : void
  {
    _ = field1;
    WriteLine("Hi!");
    _ = ReadLine();
  }
}


При компиляции проекта (надо не забыть подключить ссылка на маро-проект в тестовый проект) будет выданы два предупреждения:

Main.n(33,4):Warning: hint: Attribute: 'Ns1.MyAttr("Test str")' attr: 'Ns1.MyAttr' str: 'Test str' typedAttr: 'Ns1.MyAttr'
Main.n(33,39):Warning: hint: Attribute: 'MyAttr("Other string")' attr: 'MyAttr' str: 'Other string' typedAttr: 'Ns1.MyAttr'


Обратите внимание, что на атрибут "Ns1.MyAttr" сообщение не выдано!

ЗЫ

Это что касается получения значений атрибутов которые уже объявлены в
библиотеках или объявляются в конечном проекте.

Если же нужно просто помечать поля какими-то своими атрибутмаи, то намного проще создать свой
макро-атрибут. Для каждого применения такого макро-атрибута будет вызываться макрос в теле
которого можно произвести необходимые операции. При этом значения параметров будет доступны
непосредственно, без возни.
http://nemerle.org/Banners/?g=dark
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Получение информации об Custom Attribute-ах из макросов
От: evilbeaver  
Дата: 05.04.10 06:10
Оценка:
Здравствуйте, VladD2:

Что-то мне подсказывало, что именно так и придется делать.
Тогда вопрос вдогонку про макроатрибуты. Действия у меня производятся в момент обработки макроса на тип. Соответственно если я опишу макроатрибут, то мне необходимо будет как-то передать(сохранить) полученное значение. Единственный вариант, который я вижу — завести что-то типа синглтона, хранящего всю эту инфу. Так ли это? или есть какой-то более красивый способ?
Re[2]: Получение информации об Custom Attribute-ах из макрос
От: hardcase Пират http://nemerle.org
Дата: 05.04.10 06:28
Оценка:
Здравствуйте, evilbeaver, Вы писали:

E>Тогда вопрос вдогонку про макроатрибуты. Действия у меня производятся в момент обработки макроса на тип. Соответственно если я опишу макроатрибут, то мне необходимо будет как-то передать(сохранить) полученное значение. Единственный вариант, который я вижу — завести что-то типа синглтона, хранящего всю эту инфу. Так ли это? или есть какой-то более красивый способ?



Да, так, как правило, и делается: заводятся поля для хранения промежуточных данных модуле, который осуществляет логику макроса, в данном случае Helper.
http://nemerle.org/Banners/?t=Developer!&g=dark /* иЗвиНите зА неРовнЫй поЧерК */
Re[3]: Получение информации об Custom Attribute-ах из макрос
От: evilbeaver  
Дата: 05.04.10 06:37
Оценка:
Здравствуйте, hardcase, Вы писали:

H>Да, так, как правило, и делается: заводятся поля для хранения промежуточных данных модуле, который осуществляет логику макроса, в данном случае Helper.


Ок, спасибо за помощь.
Re[2]: Получение информации об Custom Attribute-ах из макрос
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.04.10 12:05
Оценка:
Здравствуйте, evilbeaver, Вы писали:

E>Что-то мне подсказывало, что именно так и придется делать.


Телепатия не не самое хорошо развитое у меня свойство. Так что я был бы признателен, если перед ответом шла цитата того на что дается ответ.

E>Тогда вопрос вдогонку про макроатрибуты. Действия у меня производятся в момент обработки макроса на тип. Соответственно если я опишу макроатрибут, то мне необходимо будет как-то передать(сохранить) полученное значение. Единственный вариант, который я вижу — завести что-то типа синглтона, хранящего всю эту инфу. Так ли это? или есть какой-то более красивый способ?



Можно чуть проще... введя статическое поле, но по сути это тоже самое что синглтон.
Более красивого способа нет. Кроме того используя статические данные нужно быть аккуратным, так как в режиме интеграции макрос может срабатывать многократно. Лучше хранить данные в хэш-таблице где ключом выступают TypeBuilder-ы или MemberBuilder-ы.

У ClassMember есть свойство UserData, но его использование может пересечься с другими макросами.

Я добавил в в TypeBuilder свойство:
public UserData : Map[object, object] { get; set; }

По умолчанию оно равно null. В него можно помещать нужную информацию. Данное свойство будет доступно в ревизии 8725.
http://nemerle.org/Banners/?g=dark
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.