OberonCore
https://forum.oberoncore.ru/

Как изменить тип записи во время исполнения программы?
https://forum.oberoncore.ru/viewtopic.php?f=2&t=2835
Страница 1 из 1

Автор:  Alexander Shiryaev [ Суббота, 11 Сентябрь, 2010 14:22 ]
Заголовок сообщения:  Как изменить тип записи во время исполнения программы?

Код:
MODULE M;

    IMPORT SYSTEM;

    TYPE
        Msg* = ABSTRACT RECORD END;
        MsgA* = RECORD (Msg)
            a-, b-: INTEGER
        END;
        MsgB* = RECORD (Msg)
            x-, y-, z-: SHORTREAL
        END;

        MsgHandler* = POINTER TO ABSTRACT RECORD
        END;

    PROCEDURE (h: MsgHandler) Handle- (IN msg: Msg), NEW, ABSTRACT;

    PROCEDURE DataToMsg (type: INTEGER; IN a: ARRAY OF BYTE; len: INTEGER; VAR msg: ANYREC);
    BEGIN
        SetType(msg, type); (* как это сделать? *)
        SYSTEM.MOVE( SYSTEM.ADR(a), SYSTEM.ADR(msg), len )
    END DataToMsg;

    PROCEDURE DataReceived* (id: BYTE; IN a: ARRAY OF BYTE; len: INTEGER; h: MsgHandler);
        TYPE
            MsgX = RECORD (Msg) END;
        VAR ok: BOOLEAN; type: INTEGER; msg: MsgX;
    BEGIN
        ok := FALSE;
        CASE id OF 10H:
            IF len = SIZE(MsgA) THEN
                ok := TRUE;
                type := GetType(MsgA) (* как это сделать? *)
            END
        | 11H:
            IF len = SIZE(MsgB) THEN
                ok := TRUE;
                type := GetType(MsgB) (* как это сделать? *)
            END
        ELSE
        END;

        IF ok THEN
            DataToMsg(type, a, len, msg);
            h.Handle(msg)
        END
    END DataReceived;

END M.

MODULE M1;

    IMPORT M, StdLog;

    TYPE
        MsgHandler = POINTER TO RECORD (M.MsgHandler) END;

    PROCEDURE (h: MsgHandler) Handler (IN msg: Msg);
    BEGIN
        WITH msg: M.MsgA DO
            StdLog.String("A:"); StdLog.Int(msg.a); StdLog.Int(msg.b); StdLog.Ln
        | msg: M.MsgB DO
            StdLog.String("B:"); StdLog.Real(msg.x); StdLog.Real(msg.y); StdLog.Real(msg.z); StdLog.Ln
        END
    END Handler;

    PROCEDURE Test*;
        VAR a: ARRAY 8 OF BYTE;
            h: MsgHandler;
    BEGIN
        a[0] := 1; a[1] := 1; a[2] := 0; a[3] := 0;
        a[4] := 2; a[5] := 1; a[6] := 0; a[7] := 0;
        NEW(h);
        M.DataReceived(10H, a, 8, h)
    END Test;

END M1.


Желательно с помощью Kernel.

Автор:  Иван Кузьмицкий [ Суббота, 11 Сентябрь, 2010 14:48 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Зачем такое нужно?

Автор:  Alexander Shiryaev [ Суббота, 11 Сентябрь, 2010 14:59 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Хочу реализовать обмен "сообщениями" между 2-мя компьютерами. Обрабатывать сообщения удобно таким способом:

Код:
PROCEDURE (h: Handler) MsgReceived (IN msg: Msg);
BEGIN
    WITH msg: MsgA DO
        ...
    | msg: MsgB DO
        ...
    ...
    END
END MsgReceived;


а не таким:

Код:
PROCEDURE MsgReceived (id: INTEGER; IN data: ARRAY OF BYTE; len: INTEGER);
BEGIN
    CASE id OF
    00H:
        ...
    | 01H:
        ...
    ...
    END
END MsgReceived;

Автор:  Пётр Кушнир [ Суббота, 11 Сентябрь, 2010 16:15 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Обмен сообщениями между компами я организовал через джаббер, с использованием базового сообщения абстрактного типа. код примерно такой, постараюсь удалить весь джабер и оставить суть:
Код:
IMPORT SYSTEM, Kernel, Meta

   TYPE
MsgPtr* = POINTER TO Message;
Message* = ABSTRACT RECORD (AbfBus.Message) END;
Alien* = POINTER TO RECORD (Message)
         data* : Items.Item;
      END;
   Converter* = POINTER TO ABSTRACT RECORD END;
   PROCEDURE (c : Converter) ItemToMsg*(item : Items.Item; VAR msg : MsgPtr; res : INTEGER), NEW, ABSTRACT;
   PROCEDURE (c : Converter) MsgToItem*(VAR msg : Message; OUT item : Items.Item), NEW, ABSTRACT;
   StdConv = POINTER TO RECORD(Bus.Converter) END;
   
PROCEDURE LookupItem (VAR r: ANYREC; OUT i: Meta.Item);
   VAR type: Kernel.Type; mod: Kernel.Module; attr: Kernel.ItemAttr;
   BEGIN   (* create a meta item for a global variable passed as VAR parameter *)
      attr.obj := Meta.varObj;
      attr.typ := Meta.recTyp;
      attr.vis := Meta.exported;
      attr.adr := SYSTEM.ADR(r);
      attr.mod := NIL;
      attr.desc := SYSTEM.VAL(Kernel.Type, SYSTEM.TYP(r));
      attr.ptr := NIL;
      attr.ext := NIL;
      attr.mod := NIL;
      Meta.GetThisItem(attr, i)
   END LookupItem;   

PROCEDURE (c : StdConv) ItemToMsg(item : Items.Item; VAR msg : Bus.MsgPtr; res : INTEGER);
   VAR name, summ : ARRAY 127 OF CHAR; this : Meta.Item; x : ANYPTR; hash : SternMd5.Hash; i : INTEGER; fnum, fp : ARRAY 127 OF CHAR; al : Bus.Alien; sp : Bus.Special;
BEGIN
      name:=item.GetParam(Const.type)$;
      Meta.LookupPath(name, this);
      IF this.Valid() THEN
         x:=this.New();
         ASSERT(x#NIL);
         msg:=x(Bus.MsgPtr);
         LookupItem(msg, this);
         i:=0; Inc(fnum);
      ELSE
         NEW(al); al.data:=item;
         msg:=al;
      END;
   END ItemToMsg;



Вложения:
code.zip [6.38 КБ]
Скачиваний: 671

Автор:  Пётр Кушнир [ Суббота, 11 Сентябрь, 2010 16:21 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

В общем, суть в том, что мы наследуем тип сообщения от абстрактного типа, реализовываем нужный нам тип сообщения, затем передаём его произвольным образом на другой комп, при этом передаём тип сообщения(я передаю название типа например "MyModule.MyMsg"), если на другом компе имеется модуль с тем же самым типом сообщения, создаём новый экземпляр нужного типа(x:=this.New()) и сразу подключаем к нему Meta-элемент(LookupItem) и заполняем его из полученных данных.

Автор:  Пётр Кушнир [ Суббота, 11 Сентябрь, 2010 16:24 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Тип сообщения наследуется от абстрактного типа межможульной шины сообщений http://forum.oberoncore.ru/viewtopic.php?f=47&t=269&start=0&hilit=Advanced+BlackBox+Framework - AbfBus.Message с целью дальнейшего распространения полученных из сети сообщений по шине, не отходя от кассы, так сказать

Автор:  Alexander Shiryaev [ Суббота, 11 Сентябрь, 2010 18:13 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

В итоге сделал так:

Код:
MODULE TmpM3;

   IMPORT SYSTEM, Kernel;

   TYPE
      Msg* = POINTER TO ABSTRACT RECORD END;
      MsgHandler* = POINTER TO ABSTRACT RECORD END;
      Table* = POINTER TO ABSTRACT RECORD END;

   PROCEDURE (h: MsgHandler) Handle- (msg: Msg), NEW, ABSTRACT;

   PROCEDURE (t: Table) Lookup- (id: BYTE; OUT len: BYTE; OUT type: INTEGER; OUT ok: BOOLEAN), NEW, ABSTRACT;

   PROCEDURE DataReceived* (id: BYTE; IN a: ARRAY OF BYTE; len: BYTE; h: MsgHandler; table: Table);
      VAR type: INTEGER; len1: BYTE; ok: BOOLEAN;
         msg: Msg;
   BEGIN
      table.Lookup(id, len1, type, ok);
      IF ok & (len = len1) THEN
         Kernel.NewObj( msg, SYSTEM.VAL( Kernel.Type, type) );
         SYSTEM.MOVE( SYSTEM.ADR(a), SYSTEM.ADR(msg^), len );
         ASSERT(msg # NIL);
         h.Handle(msg)
      END
   END DataReceived;

END TmpM3.


Код:
MODULE TmpM4;

   (* этот модуль должен создаваться автоматически *)

   IMPORT SYSTEM, TmpM3;

   TYPE
      MsgA* = POINTER TO MsgADesc;
      MsgADesc* = RECORD (TmpM3.Msg)
         a-, b-: INTEGER
      END;
      MsgB* = POINTER TO MsgBDesc;
      MsgBDesc* = RECORD (TmpM3.Msg)
         x-, y-, z-: SHORTREAL
      END;

      Table = POINTER TO RECORD (TmpM3.Table) END;

   VAR
      table-: Table;

   PROCEDURE (t: Table) Lookup (id: BYTE; OUT len: BYTE; OUT type: INTEGER; OUT ok: BOOLEAN);
   BEGIN
      CASE id OF 10H:
         len := SIZE(MsgADesc); type := SYSTEM.TYP(MsgADesc);
         ok := TRUE
      | 11H:
         len := SIZE(MsgBDesc); type := SYSTEM.TYP(MsgBDesc);
         ok := TRUE
      ELSE
         ok := FALSE
      END
   END Lookup;

BEGIN
   NEW(table)
END TmpM4.


Код:
MODULE TmpM5;

   IMPORT TmpM3, TmpM4, StdLog;

   TYPE
      MsgHandler = POINTER TO RECORD (TmpM3.MsgHandler) END;

   PROCEDURE (h: MsgHandler) Handle (msg: TmpM3.Msg);
   BEGIN
      WITH msg: TmpM4.MsgA DO
         StdLog.String("A:"); StdLog.Int(msg.a); StdLog.Int(msg.b); StdLog.Ln
      | msg: TmpM4.MsgB DO
         StdLog.String("B:");
            StdLog.Real(msg.x); StdLog.Real(msg.y); StdLog.Real(msg.z);
            StdLog.Ln
      END
   END Handle;

   PROCEDURE Test*;
      VAR h: MsgHandler;
         a: ARRAY 12 OF BYTE;
   BEGIN
      a[0] := 1; a[1] := 1; a[2] := 0; a[3] := 0;
      a[4] := 2; a[5] := 1; a[6] := 0; a[7] := 0;
      NEW(h);
      TmpM3.DataReceived(10H, a, 8, h, TmpM4.table)
   END Test;

END TmpM5.

!(Q)TmpM5.Test

Автор:  Илья Ермаков [ Воскресенье, 12 Сентябрь, 2010 21:41 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Ваша сетевая библиотеке в Блэкбоксе получателе должна вызывать какой-то обработчик, передавая ему запись нужного типа. Чтобы привести байтовый буфер к нужному типу, можете использовать функцию SYSTEM.THISRECORD. Она недокументирована, пример можно увидеть в Meta:

Код:
   PROCEDURE (VAR rec: Item) CallWith* (proc: PROCEDURE(VAR rec, par: ANYREC); VAR par: ANYREC), NEW;
   BEGIN
      ASSERT(rec.ext = NIL, 31);
      ASSERT(rec.ptr # NIL, 20);
      ASSERT(rec.typ = recTyp, 21);
      ASSERT(rec.obj = varObj, 22);
      ASSERT((rec.mod = NIL) OR (rec.mod.refcnt >= 0), 23);
      proc(SYSTEM.THISRECORD(rec.adr, SYSTEM.VAL(INTEGER, rec.desc)), par)
   END CallWith;

Автор:  Alexander Shiryaev [ Понедельник, 13 Сентябрь, 2010 11:29 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Спасибо! Как раз то, что нужно.

Код:
MODULE TmpM3;

   IMPORT SYSTEM;

   TYPE
      Msg* = ABSTRACT RECORD END;
      MsgHandler* = POINTER TO ABSTRACT RECORD END;
      Table* = POINTER TO ABSTRACT RECORD END;

   PROCEDURE (h: MsgHandler) Handle- (IN msg: Msg), NEW, ABSTRACT;

   PROCEDURE (t: Table) Lookup- (id: BYTE; OUT len: BYTE; OUT type: INTEGER; OUT ok: BOOLEAN), NEW, ABSTRACT;

   PROCEDURE DataReceived* (id: BYTE; IN a: ARRAY OF BYTE; len: BYTE; h: MsgHandler; table: Table);
      VAR type: INTEGER; len1: BYTE; ok: BOOLEAN;
   BEGIN
      table.Lookup(id, len1, type, ok);
      IF ok & (len = len1) THEN
         h.Handle( SYSTEM.THISRECORD( SYSTEM.ADR(a), type ) )
      END
   END DataReceived;

END TmpM3.

Автор:  Пётр Кушнир [ Понедельник, 13 Сентябрь, 2010 14:47 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

мегахак!!! а я то, огород городил...

Автор:  Иван Кузьмицкий [ Пятница, 06 Январь, 2012 14:50 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Не нашёл лучшего места, чем тут, чтобы зафиксировать концептуальный примерчик. Задача: необходимо рассылать уведомления по шине (здесь считаем, что это статическая RECORD без полей), зная только имя типа уведомления в виде цепочки символов.


Код:
MODULE TestMeta;

(**
   project   = "TestMeta"
   organization   = ""
   contributors   = ""
   version   = "System/Rsrc/About"
   copyright   = "Kuzmitskiy Ivan Alexandrovich"
   license   = "Docu/BB-License"
   purpose   = "Лабораторный образец, демонстрирующий один из способов создания переменной, тип который задан цепочкой символов"
   changes   = "
   - 20120106, kia, автогенерация заголовка
"
   issues   = ""
**)

   IMPORT
      Log := StdLog, Meta;

   TYPE
      Message* = ABSTRACT RECORD END;
      PMessage* = POINTER TO Message;
      NotifyMsg* = RECORD (Message) END;
      NotifyMsg1* = RECORD (Message) END;
      NotifyMsg2* = RECORD (Message) END;

   PROCEDURE Handle (VAR msg: Message);
   BEGIN
      WITH msg: NotifyMsg DO
         Log.String('TestMeta, обработка NotifyMsg'); Log.Ln
      | msg: NotifyMsg1 DO
         Log.String('TestMeta, обработка NotifyMsg1'); Log.Ln
      | msg: NotifyMsg2 DO
         Log.String('TestMeta, обработка NotifyMsg2'); Log.Ln
      ELSE
      END
   END Handle;

   PROCEDURE Do* (s: ARRAY OF CHAR);
      VAR this: Meta.Item; x: ANYPTR; pn: PMessage; n: NotifyMsg;
   BEGIN
      Meta.LookupPath(s, this);
      IF this.Valid() THEN
         x := this.New(); ASSERT(x # NIL, 60);
         Handle(x(PMessage))
      ELSE
         Log.String('TestMeta: Тип не найден!'); Log.Ln;
      END;
   END Do;

END TestMeta.

^Q"TestMeta.Do('')"
^Q"TestMeta.Do('TestMeta.NotifyMsg')"
^Q"TestMeta.Do('TestMeta.NotifyMsg1')"
^Q"TestMeta.Do('TestMeta.NotifyMsg2')"

Автор:  Илья Ермаков [ Понедельник, 09 Январь, 2012 10:13 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Хороший пример, но у него есть важный изъян: порождается динамический экземпляр.
Для прикладного программирования это можно стерпеть, но вообще, каждый раз, когда я такое вижу, мне обидно за то, что мы скатываемся к стилю Явы/Шарпов - плодить мусор там, где Оберон, с его полиморфизмом для записей на стеке, позволяет этого не делать...

В вашем случае, чтобы "не плодить", можно сделать модулёк, которая внутри решает эту проблему (подстановку пустого экземпляра записи по имени) через SYSTEM...
Самый простой способ - использовать THISREC.

Если без SYSTEM - то можно сделать такой модулёк, который хранит по одному экземпляру записей по их имени и отдаёт нужную.

Это как дополнение к Вашему примеру. В нагруженных приложениях это важно.

Автор:  Иван Кузьмицкий [ Понедельник, 09 Январь, 2012 14:53 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Я ещё не понял, как пользоваться SYSTEM.THISRECORD :(

Автор:  Александр Ильин [ Понедельник, 09 Январь, 2012 15:03 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Илья Ермаков писал(а):
Самый простой способ - использовать THISREC.
Чего?
В документации не нашёл такого.

Автор:  Евгений Темиргалеев [ Понедельник, 09 Январь, 2012 15:07 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

В документации её и нету. Была выкопана в исходниках компилятора и затем обнаружена в коде модуля Meta.

Автор:  Роман М. [ Вторник, 10 Январь, 2012 18:59 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Илья Ермаков писал(а):
Самый простой способ - использовать THISREC.

То есть использовать недокументированную функциональность?

Автор:  Info21 [ Среда, 11 Январь, 2012 12:53 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Заразная какая болезнь.

Автор:  Иван Кузьмицкий [ Среда, 11 Январь, 2012 13:35 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Это не болезнь, а поиск средств для обмена сообщениями между экземплярами BlackBox. Всем известно, что экземпляры BlackBox функционируют в чужеродной, "не-оберон"-среде :), поэтому просто так сообщение не передашь!

Надо его, это сообщение, обязательно завернуть в полиэтиленовый пакет в целях инфекционной безопасности, да сургучом залить, а при получении распечатать :)

Автор:  Info21 [ Среда, 11 Январь, 2012 15:33 ]
Заголовок сообщения:  Re: Как изменить тип записи во время исполнения программы?

Иван Кузьмицкий писал(а):
Это не болезнь, а поиск средств для обмена сообщениями между экземплярами BlackBox.
Ну, пусть.

Страница 1 из 1 Часовой пояс: UTC + 3 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/