Создание фабричного метода.
От: Dufrenite Дания  
Дата: 09.09.07 10:53
Оценка:
Требуется сгенерировать статический метод, который возвращает объект типа, с которым работает макрос. Вопрос: как это лучше сделать. Вот код на котором я остановился (здесь выполняется десериализация полей из BinaryReader):

        internal СоздатьМетодДесериализации(
            поля : list[IField],
            атрибуты : NemerleAttributes,
            конструктор : IMethod
            ) : ClassMember
        {
            def проанализироватьПоля(поле : IField, acc)
            {
                def имя = Macros.UseSiteSymbol(поле.Name);
                <[ def $(имя : name) = reader.ReadInt32() ]> :: acc;
            };

            Message.Hint($"$(конструктор.Name)");
            Message.Hint($"$(конструктор.Header.ParametersReferences)");
            def загрузки = поля.FoldLeft([], проанализироватьПоля).Reverse();
            def телоМетода = <[{..$загрузки }]>;
            def атрибуты = Modifiers(атрибуты %| NemerleAttributes.Static, []);
            <[decl: ..$атрибуты Загрузить(reader : System.IO.BinaryReader) : void $телоМетода ]>
        }


Для такого вызова


[BinarySerializableRecord]
internal sealed class Класс1
{
    private _поле1 : int;
}


генерируется следующий код:


[BinarySerializable]
internal sealed class Класс1
{
    private _поле1 : int;

    internal this(_поле1 : int)
    {
        this._поле1 = _поле1;
    }

    internal static Загрузить(reader : System.IO.BinaryReader) : void
    {
        def _поле1 = reader.ReadInt32();
    }
}


Вопрос: как сделать, чтобы в качестве возвращаемого значения был Класс1, и в конце вызывался передаваемый конструктор?


internal static Загрузить(reader : System.IO.BinaryReader) : Класс1
{
    def _поле1 = reader.ReadInt32();
    Класс1(_поле1)
}
Re: Создание фабричного метода.
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.09.07 13:49
Оценка:
Здравствуйте, Dufrenite, Вы писали:

D>Вопрос: как сделать, чтобы в качестве возвращаемого значения был Класс1, и в конце вызывался передаваемый конструктор?


Если класс помечен мета-атрибутом Record, то конечно можно сделать унивирсальный макрос, а так... даже не ясно как действовать. Конструкторов может быть более одного. Причем ни один из конструторов может не заполнять все поля. И еще... Как обеспечить соответствие между порядком/значением параметров конструктора и порядком сериализации полей?

В общем, мне кажется, что у тебя не продумана сама иделогия. Или ты ее не полностью раскрыл. Технических же проблем в твоем изложении тоже вроде как не видно. Так что на что отвечать не ясно. Ты бы сформулировал свои воросы более четко.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Создание фабричного метода.
От: Dufrenite Дания  
Дата: 12.09.07 06:31
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Ты бы сформулировал свои воросы более четко.


Окей, действительно поторопился. Короче, проблема в том, что я ещё до конца не разобрался в том, как связать макро-синтаксис Nemerle и структуры данных компилятора.
Проблема в следующем: надо сгенерировать метод в котором вызывается конструктор класса. Причём этот конструктор я генерирую в том же макросе. Я просто хочу понять — как мне сгенерировать вызов этого самого конструктора? Какой тип мне для этого нужно использовать ClassMember.Function или IMethod? И как это записать?
Re[3]: Создание фабричного метода.
От: Dufrenite Дания  
Дата: 12.09.07 06:37
Оценка:
На всякий случай, вот весь код макроса, который пока есть:

    /// <summary>
    /// Генерация методов сериализации.
    /// </summary>
    [MacroUsage(MacroPhase.BeforeInheritance,
                MacroTargets.Class,
                Inherited = false,
                AllowMultiple = false)]
    public macro BinarySerializableRecord(typeBuilder : TypeBuilder, params _ : list [PExpr])
    {
        typeBuilder.DisableImplicitConstructor();
    }

    /// <summary>
    /// Генерация методов сериализации.
    /// </summary>
    [MacroUsage(MacroPhase.WithTypedMembers,
                MacroTargets.Class,
                Inherited = false,
                AllowMultiple = false)]
    public macro BinarySerializableRecord(typeBuilder : TypeBuilder, params _ : list [PExpr])
    {
        def поля = typeBuilder.GetFields(
            BindingFlags.Instance %|
            BindingFlags.Public %|
            BindingFlags.NonPublic %|
            BindingFlags.DeclaredOnly
            );

        typeBuilder.GetModifiers().AddCustomAttribute(<[ MIG.Engine.Data.BinarySerializableAttribute ]>);
        def атрибутыКласса = typeBuilder.GetModifiers().Attributes;
        def атрибутыКонструкторов = ОпределитьАтрибутыКонструкторов(атрибутыКласса);
        def атрибутыМетодов = ОпределитьАтрибутыМетодов(атрибутыКласса);

        def добавитьМетод(метод)
        {
            typeBuilder.DefineAndReturn(метод).HasBeenUsed = true;
        }

        def конструктор()
        {
            typeBuilder.GetConstructors(
                BindingFlags.Instance %|
                BindingFlags.Public %|
                BindingFlags.NonPublic %|
                BindingFlags.DeclaredOnly
                ).Head
        }

        if (typeBuilder.IsValueType)
        {
            Message.Hint("Структура.");
            добавитьМетод(СоздатьКонструктор(поля, атрибутыКонструкторов));
            добавитьМетод(СоздатьМетодСериализации(поля, атрибутыМетодов));
            добавитьМетод(СоздатьМетодДесериализации(поля, атрибутыМетодов, конструктор()));
        }
        else
        {
            Message.Hint($"Базовый класс: $(typeBuilder.BaseType.FullName).");
            if (typeBuilder.BaseType.FullName == "System.Object")
            {
                добавитьМетод(СоздатьКонструктор(поля, атрибутыКонструкторов));
                Message.Hint(СоздатьКонструктор(поля, атрибутыКонструкторов).GetType().ToString());
                добавитьМетод(СоздатьМетодСериализации(поля, атрибутыМетодов));
                добавитьМетод(СоздатьМетодДесериализации(поля, атрибутыМетодов, конструктор()));
            }
            else if (typeBuilder.GetModifiers().GetCustomAttributes().
                Exists(_ is <[ MIG.Engine.Data.BinarySerializableAttribute ]>))
            {
            }
            else
            {
                Message.Error(
                    "Сериализуемый класс можно наследовать только от сериализуемого (" +
                    "базовый класс не помечен атрибутом [BinarySerializable])."
                    );
            }
        }
    }

    /// <summary>
    /// Вспомогательный класс для макроса BinarySerializableRecord.
    /// </summary>
    internal module BinarySerializableRecordHelper
    {
        internal СоздатьКонструктор(поля : list[IField], атрибуты : NemerleAttributes) : ClassMember
        {
            def вызовБазовогоКонструктора = <[ base () ]>;

            def проанализироватьПоля(поле : IField, acc)
            {
                def имя = Macros.UseSiteSymbol(поле.Name);
                def параметрКонструктора = <[ parameter: $(имя : name) : $(поле.GetMemType() : typed) ]>;
                def присвоение = <[ this.$(имя : name) = $(имя : name) ]>;
                def (присвоения, параметрыКонструктора) = acc;
                (присвоение :: присвоения, параметрКонструктора :: параметрыКонструктора)
            };

            def (присвоения, параметрыКонструктора) = поля.FoldLeft(([], []), проанализироватьПоля);
            def телоКонструктора = <[ { ..$(вызовБазовогоКонструктора :: присвоения) } ]>;
            def атрибуты = Modifiers(атрибуты, []);
            <[ decl: ..$атрибуты this (..$(параметрыКонструктора.Reverse())) $телоКонструктора ]>;
        }

        internal СоздатьМетодСериализации(поля : list[IField], атрибуты : NemerleAttributes) : ClassMember
        {
            def проанализироватьПоля(поле : IField, acc)
            {
                def имя = Macros.UseSiteSymbol(поле.Name);
                <[ writer.Write($(имя : name)) ]> :: acc;
            };

            def сохранения = поля.FoldLeft([], проанализироватьПоля).Reverse();
            def телоМетода = <[{..$сохранения}]>;
            def атрибуты = Modifiers(атрибуты, []);
            <[decl: ..$атрибуты Сохранить(writer : System.IO.BinaryWriter) : void $телоМетода ]>
        }

        internal СоздатьМетодДесериализации(
            поля : list[IField],
            атрибуты : NemerleAttributes,
            конструктор : IMethod
            ) : ClassMember
        {
            def проанализироватьПоля(поле : IField, acc)
            {
                def имя = Macros.UseSiteSymbol(поле.Name);
                <[ def $(имя : name) = reader.ReadInt32() ]> :: acc;
            };

            Message.Hint($"$(конструктор.Name)");
            Message.Hint($"$(конструктор.Header.ParametersReferences)");
            def загрузки = поля.FoldLeft([], проанализироватьПоля).Reverse();
            def телоМетода = <[{..$загрузки }]>;
            def атрибуты = Modifiers(атрибуты %| NemerleAttributes.Static, []);
            <[decl: ..$атрибуты Загрузить(reader : System.IO.BinaryReader) : void $телоМетода ]>
        }

        internal ОпределитьАтрибутыКонструкторов(атрибутыТипа : NemerleAttributes) : NemerleAttributes
        {
            if (атрибутыТипа & NemerleAttributes.Abstract != NemerleAttributes.None)
            {
                // Класс абстрактный.
                NemerleAttributes.Protected
            }
            else
            {
                ОпределитьАтрибутыМетодов(атрибутыТипа);
            }
        }

        internal ОпределитьАтрибутыМетодов(атрибутыТипа : NemerleAttributes) : NemerleAttributes
        {
            def атрибуты = атрибутыТипа & (NemerleAttributes.Public %| NemerleAttributes.Internal);
            if (атрибуты != NemerleAttributes.None) атрибуты else NemerleAttributes.Internal;
        }
    }
Re[3]: Создание фабричного метода.
От: VladD2 Российская Империя www.nemerle.org
Дата: 12.09.07 12:14
Оценка: 2 (1)
Здравствуйте, Dufrenite, Вы писали:

D>Я просто хочу понять — как мне сгенерировать вызов этого самого конструктора? Какой тип мне для этого нужно использовать ClassMember.Function или IMethod? И как это записать?


Вызов можно сгенерировать просто в квази-цитировании... как-то так:
<[ (nameOfType : name)() ]>

Или я чего-то непонимаю?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Создание фабричного метода.
От: Dufrenite Дания  
Дата: 12.09.07 13:17
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Вызов можно сгенерировать просто в квази-цитировании... как-то так:

VD>
VD><[ (nameOfType : name)() ]>
VD>

VD>Или я чего-то непонимаю?

Я разобрался, что я неправильно делал. Вот такой код не компилировался:

def телоМетода = <[{ ..$загрузки Класс1(..$(конструктор.Header.ParametersReferences)) }]>;


с сообщением об ошибке:
Error: parse error near identifier 'Класс1': unexpected token after ellipsis expression

Я изменил код на этот и всё получилось:


def операторы = загрузки.Append([<[ Класс1(..$(конструктор.Header.ParametersReferences)) ]>]);
def телоМетода = <[{ ..$операторы }]>;


Если добавить разделитель, то сообщение об ошибке изменяется, но всё равно остаётся непонятным:

def телоМетода = <[{ ..$загрузки ; Класс1(..$(конструктор.Header.ParametersReferences)) }]>;


вот:
Error: List of expression parameters outside of quoted sequence: use <[ { .. $x } ]> pattern

В общем не понятно...
Re[5]: Создание фабричного метода.
От: VladD2 Российская Империя www.nemerle.org
Дата: 15.09.07 11:07
Оценка:
Здравствуйте, Dufrenite, Вы писали:

D>Я разобрался, что я неправильно делал. Вот такой код не компилировался:


D>
D>def телоМетода = <[{ ..$загрузки Класс1(..$(конструктор.Header.ParametersReferences)) }]>;
D>


D>с сообщением об ошибке:

D>Error: parse error near identifier 'Класс1': unexpected token after ellipsis expression

А что понимается под "загрузки"? Если это попытка задать пространство имен, то так нельзя. Тут недавно обсуждался вопрос "как засунуть класс в нужное пространство имен". Поищи... если тебе именно это нужно.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Создание фабричного метода.
От: Dufrenite Дания  
Дата: 15.09.07 16:11
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>А что понимается под "загрузки"?


Загрузки, это последовательность операторов десериализации. Что то типа:
def _поле1 = reader.ReadInt32();

Получается, что одной инструкцией я не могу разыменовать список операторов и вызов конструктора. Приходится генерировать отдельный оператор для вызова конструктора и прицеплять его к списку операторов десериализации, тогда всё срабатывает.
Re[7]: Создание фабричного метода.
От: Иванков Дмитрий Россия  
Дата: 15.09.07 17:17
Оценка:
Здравствуйте, Dufrenite, Вы писали:

D>Получается, что одной инструкцией я не могу разыменовать список операторов и вызов конструктора. Приходится генерировать отдельный оператор для вызова конструктора и прицеплять его к списку операторов десериализации, тогда всё срабатывает.


А, тогда все просто. Проблема в том как парсится цитирование, у ".." видимо "приоритет" слишком низкий.
Так что пишем например так:
def телоМетода = <[{ (..$загрузки); Класс1(..$(конструктор.Header.ParametersReferences)) }]>;
Re[8]: Создание фабричного метода.
От: Dufrenite Дания  
Дата: 16.09.07 08:39
Оценка:
Здравствуйте, Иванков Дмитрий, Вы писали:

ИД>А, тогда все просто. Проблема в том как парсится цитирование, у ".." видимо "приоритет" слишком низкий.

ИД>Так что пишем например так:
ИД>
ИД>def телоМетода = <[{ (..$загрузки); Класс1(..$(конструктор.Header.ParametersReferences)) }]>;
ИД>


К сожалению такой вариант не прокатывает, так как скобки также попадают в результирующий код, а компилятор такого не переваривает.
Re[9]: Создание фабричного метода.
От: Иванков Дмитрий Россия  
Дата: 16.09.07 15:54
Оценка:
Здравствуйте, Dufrenite, Вы писали:

D>Здравствуйте, Иванков Дмитрий, Вы писали:


ИД>>А, тогда все просто. Проблема в том как парсится цитирование, у ".." видимо "приоритет" слишком низкий.

ИД>>Так что пишем например так:
ИД>>
ИД>>def телоМетода = <[{ (..$загрузки); Класс1(..$(конструктор.Header.ParametersReferences)) }]>;
ИД>>


D>К сожалению такой вариант не прокатывает, так как скобки также попадают в результирующий код, а компилятор такого не переваривает.


С круглыми это я загнул, фигурные нужны
С круглыми просто получается видимо не (выражение), а какой-нибудь плохой (tuple).
Re[8]: Создание фабричного метода.
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.09.07 12:13
Оценка:
Здравствуйте, Иванков Дмитрий, Вы писали:

ИД>А, тогда все просто. Проблема в том как парсится цитирование, у ".." видимо "приоритет" слишком низкий.

ИД>Так что пишем например так:
ИД>
ИД>def телоМетода = <[{ (..$загрузки); Класс1(..$(конструктор.Header.ParametersReferences)) }]>;
ИД>


Все сложнее. Разименование .. работает только в скобках. Причем в зависимости от типа скобок оно работает по разному. Так что в этом конкретном случае надо ввести слишние фигурные скобки:
<[{ { ..$загрузки } Класс1(..$(конструктор.Header.ParametersReferences)) }]>;
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.