Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 26.08.11 11:54
Оценка:
Здрасьте. Пытаюсь написать свой макро-аттрибут.

    [MacroUsage(MacroPhase.BeforeTypedMembers, MacroTargets.Class)]
    public macro Disposable(typeBuilder : TypeBuilder)
    {
        DisposableImpl.DoTransform(Macros.ImplicitCTX(), typeBuilder)
    }
    
    module DisposableImpl
    {
        public DoTransform(typer : Typer, typeBuilder : TypeBuilder) : void
        {
            Macros.DefineCTX(typer);
            
            // TODO: Add implementation here.
            typeBuilder.AddImplementedInterface(<[System.IDisposable]>);

            def members = typeBuilder.GetMembers();
            mutable disposable = List.[IMember]();
            
            foreach(member in members)
            {
                def type = member.DeclaringType;
                def sTypes = type.GetSuperTypes();
                
                foreach(superType in sTypes)
                {
                    when(superType.GetType() == typeof(IDisposable))
                    {
                        disposable.Add(member);
                    }
                }
            }
        }
    }


Т.е. хочу дописать к реализуемым интерфейсам IDisposable, да собрать все члены класса, которые реализуют IDsposable.
Приписываю над классом волшебную надпись: [Disposable] — получаю привет от компилятора: «The AddImplementedInterface method can be called only before MacroPhase.BeforeTypedMembers phase.»

То есть, «можешь добавлять интерфейсы только в макросах, срабатывающих на стадии MacroPhase.BeforeTypedMembers», но ведь я и так указал эту стадию? %)
Товарисчи, наставьте на путь истинный пожалуйста.
Re: Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 26.08.11 12:13
Оценка:
Прошу прощения. Разобрался.
Re: Вопрос про typeBuilder.AddImplementedInterface
От: VladD2 Российская Империя www.nemerle.org
Дата: 26.08.11 14:37
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Т.е. хочу дописать к реализуемым интерфейсам IDisposable, да собрать все члены класса, которые реализуют IDsposable.

А>Приписываю над классом волшебную надпись: [Disposable] — получаю привет от компилятора: «The AddImplementedInterface method can be called only before MacroPhase.BeforeTypedMembers phase.»

А>То есть, «можешь добавлять интерфейсы только в макросах, срабатывающих на стадии MacroPhase.BeforeTypedMembers», но ведь я и так указал эту стадию? %)


Скажу больше. Реально проблем можно избежать только если использовать фазу MacroPhase.BeforeInheritance, так как именно после нее компилятор производит проверки связанные с наследованием и реализацией интерфейсов.

Но делу это никак не помешает. Дело в том, что макрос может работать на разных стадиях. На стадии MacroPhase.BeforeInheritance добавляешь, с помощью AddImplementedInterface, интерфейс и метод Dispose, а на стадии MacroPhase.WithTypedMembers собираешь нужную для реализации метода Dispose информацию и генерируешь его тело.

В твоем же случае имеет смысл вообще завести два макроса. Один будет работать на фазе MacroPhase.BeforeInheritance и только лишь добавлять реализацию IDisposable, а второй будет вешаться на добавляемый метод Dispose и будет генерировать его тело. Это позволит не искать Dispose. Второй макрос, естественно, должен работать на стадии MacroPhase.WithTypedMembers и иметь MacroTargets.Method.

Кроме того ты совершенно не верно пытаешься получить информацию о членах реализующих IDisposable.

Ниже приведен пример реализации таких макросов.

Disposable.n — реализация макроса применяемого к классу (типу).
  Скрытый текст
using Nemerle;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;

namespace MacroTestLib
{
  // Выполняется на фазе BeforeInheritance, так как этот макрос должен изменять список 
  // реализованных интерфейсов.
  [MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Class)]
  macro Disposable(typeBuilder : TypeBuilder)
  {
    // Добавляем "наследование" IDisposable.
    typeBuilder.AddImplementedInterface(<[ System.IDisposable ]>);
    // Добавляем метод Dispose. Метод помечаем макроатрибутом ImplementDispose.
    // Он "вызовится" позже.
    typeBuilder.Define(<[ decl: [MacroTestLib.ImplementDispose] public Dispose() : void { } ]>);
  }
}

ImplementDispose.n -реализация макроса применяемого к методу Dispose.
  Скрытый текст
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Compiler.Typedtree;
using Nemerle.Text;
using Nemerle.Utility;

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

namespace MacroTestLib
{
  // Макрос работает на фазе WithTypedMembers, так как ему нужна информация о типх членов.
  /// Данный макрос должен применяться к методу Dispose!
  [MacroUsage(MacroPhase.WithTypedMembers, MacroTargets.Method)]
  macro ImplementDispose(typeBuilder : TypeBuilder, method : MethodBuilder)
  {
    ImplementDisposeImpl.DoTransform(Macros.ImplicitCTX(), typeBuilder, method)
  }
  
  module ImplementDisposeImpl
  {
    public DoTransform(typer : Typer, typeBuilder : TypeBuilder, method : MethodBuilder) : void
    {
      Macros.DefineCTX(typer); // это нружно для цитаты <[ ttype: System.IDisposable ]>
      
      // assert2(false);
      
      // Так надо получать описание типа в макросах. Использовать System.Type в макросах нельзя!
      def iDisposableType = <[ ttype: System.IDisposable ]>;
      
      def isMemberTypeImplementIDisposable(member : IMember) : bool
      {
        // Вызвать Dispose() имеет смысл только у полей и свойств определенных в текущем классе. Фильтрум их...
        (member is IProperty || member is IField) 
        // Получаем тип члена и проверям, что он унифицируется с IDisposable. А использовать System.Type в макросах нельзя!
        && member.GetMemType().TryRequire(iDisposableType)
      }
      
      // Получаем все экземплярные поля и свойства типы которых реализуют IDisposable.
      def members = typeBuilder.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance 
                                         | BindingFlags.Public       | BindingFlags.NonPublic)
                               .Filter(isMemberTypeImplementIDisposable);
      
      // Преобразуем список членов в список выражений проиводящий обращение к ним и вызвающих от них Dispose().
      // Оператор ?. позволяет защититься от вызова по нулевому указателю.
      def exprs = members.Map(m => <[ (this.$(m.Name :  usesite) : System.IDisposable)?.Dispose() ]>);
      
      // Заменяем тело Dispose-а данного класа. Цитата ..$х раскрывает список выражений.
      method.Body = <[ { ..$exprs } ]>; 
    }
  }
}

Test.n — тестовое приложение использующее макрос.
  Скрытый текст
using System.Console;
using System.IO;

using MacroTestLib;

[Record]
[Disposable]
class Test
{
  private FileStream : FileStream;
  private Str : string;
  private Reader : TextReader;
}

module Program2
{
  Main() : void
  {
    def x = Test(null, "", null);
    x.Dispose();
  }
}

Код метода Dispose полученный с помощью декомпиляции в C#.
  Скрытый текст
public void Dispose()
{
    IDisposable fileStream = this.FileStream;
    if (fileStream != null)
    {
        fileStream.Dispose();
    }
    IDisposable reader = this.Reader;
    if (reader != null)
    {
        reader.Dispose();
    }
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 27.08.11 03:02
Оценка:
ёмаё, Влад, ЧЕЛОВЕЧИЩЕ, спасибо тебе большое!
Re[2]: Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 27.08.11 17:39
Оценка:
VladD2, можно вопрос про System.Type?
Почему нельзя его в паттерн матчинге проверять?

В таком ключе:
match(typeof(obj))
{
| typeof(IDisposable) =>
}

?
Re[3]: Вопрос про typeBuilder.AddImplementedInterface
От: catbert  
Дата: 27.08.11 18:22
Оценка:
Здравствуйте, Аноним, Вы писали:

А>VladD2, можно вопрос про System.Type?

А>Почему нельзя его в паттерн матчинге проверять?

А>В таком ключе:

А>
А>match(typeof(obj))
А>{
А>| typeof(IDisposable) =>
А>}
А>

А>?

Потому что справа от знака => в match можно использовать только константы (даже не константы — литералы). Хотя typeof(IDisposable) — к сожалению, не литерал. Но зачем писать так? Можно по-другому:

match (obj)
{
| disp is IDisposable => 
  {
    // здесь disp — это obj, приведенный к IDisposable
  }
| _ => ...
}
Re[4]: Вопрос про typeBuilder.AddImplementedInterface
От: catbert  
Дата: 27.08.11 18:23
Оценка:
Здравствуйте, catbert, Вы писали:

C> Хотя typeof(IDisposable) — к сожалению, не литерал.


* вместо "хотя" там "а"
Re[4]: Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 27.08.11 19:05
Оценка:
catbert, спасибо большое за разъяснения!

C>Потому что справа от знака => в match можно использовать только константы (даже не константы — литералы).

наверное, все-таки «слева от знака».
Re[3]: Вопрос про typeBuilder.AddImplementedInterface
От: hardcase Пират http://nemerle.org
Дата: 27.08.11 20:17
Оценка:
Здравствуйте, Аноним, Вы писали:

А>VladD2, можно вопрос про System.Type?

А>Почему нельзя его в паттерн матчинге проверять?

А>В таком ключе:

А>
А>match(typeof(obj))
А>{
А>| typeof(IDisposable) =>
А>}
А>

А>?

Можно:
match(obj)
{
  | _ is IDisposable =>
  ...
}
/* иЗвиНите зА неРовнЫй поЧерК */
Re[3]: Вопрос про typeBuilder.AddImplementedInterface
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.08.11 00:26
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Почему нельзя его в паттерн матчинге проверять?


Его вообще нельзя использовать в макросах. System.Type доступен только в рантайме. Во время компиляции типов определенных в проектах может просто не быть.

Кроме того компилятор (а значит и макросы) могут быть собраны для платформы отличной от той для которой будет собираться приложение.

Ну, код вроде:
superType.GetType()

возвратит тип объекта на который указывает superType, т.е. что-то вроде FixedType или TypeVar.

А они тебе точно не нужны, да и IDisposable не реализуют — 100%.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 28.08.11 11:39
Оценка:
hardcase, спасибо!
Re[4]: Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 28.08.11 11:48
Оценка:
VladD2, в который раз благодарю за разъяснения!
Re[2]: Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 02.09.11 21:53
Оценка:
Здравствуйте, VladD2.

Пытаюсь изучать Nemerle. Попробовал скомпилить этот пример. Ругается на
def iDisposableType = <[ ttype: System.IDisposable ]>;

there is no member named `Env' in Nemerle.Compiler.TypeBuilder with type ?
и
there is no member named `LookupType' in ? with type ?

В чем может быть проблема? Интеграцию брал из http://rsdn.ru/forum/nemerle/4404024.1.aspx
Автор: kochetkov.vladimir
Дата: 01.09.11
(NemerleSetup-net-4.0-v1.0.211.0.msi)
Re[3]: Вопрос про typeBuilder.AddImplementedInterface
От: Аноним  
Дата: 02.09.11 22:08
Оценка:
Прошу прощения. Моя ошибка, разобрался.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.