Обдумываю как могла бы выглядеть правильная трансляция Оберона в C#.
Трансляция используемая в GPCP абсолютно неправильная ибо в GPCP оберонистые value-типы эмулируются дотнетными ссылочными, что убивает производительность на корню.
С точки зрения C# язык Oberon ужасно-ужасно-ужасно ансэйфный.
Вот, например, простенький модуль на Обероне:
MODULE Test;
TYPE
Message* = RECORD END;
MouseMsg* = RECORD (Message)
x*, y*: INTEGER
END;
KeyboardMsg* = RECORD (Message)
ch*: CHAR
END;
Controller* = RECORD
handler*: PROCEDURE (VAR controller: Controller; VAR message: Message);
END;
MyController = RECORD (Controller)
...
END;
PROCEDURE MyHandler (VAR controller: Controller; VAR message: Message)
BEGIN
IF message IS MouseMsg THEN
...
ELSIF message IS KeyboardMsg THEN
...
END
END
PROCEDURE Smth (VAR c: Controller)
VAR mouseMsg: MouseMsg; keyboardMsg: KeyboardMsg;
BEGIN
mouseMsg.x := 10;
mouseMsg.y := 20;
c.handler(c, mouseMsg);
keyboardMsg.c := 'A';
c.handler(c, keyboardMsg)
END Smth;
PROCEDURE Do* ()
VAR c: MyController;
BEGIN
c.handler := MyHandler;
Smth(c)
END Do;
END Test.
Транслируем его в C#.
Сначала вспомогательный модуль на C#:
public static unsafe class SYSTEM
{
public struct TYPE
{
public TYPE* ext0;
public TYPE* ext1;
public TYPE* ext3;
public TYPE* ext4;
public TYPE* ext5;
public TYPE* ext6;
public TYPE* ext7;
}
public struct ANYREC
{
public TYPE* type;
}
public static System.IntPtr ALLOC (int size)
{
return System.Runtime.InteropServices.Marshal.AllocHGlobal(size);
}
public static void FREE (void* pointer)
{
System.Runtime.InteropServices.Marshal.FreeHGlobal((System.IntPtr)pointer);
}
public static System.Delegate DELEGATE (System.IntPtr p, System.Type t)
{
return System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(p, t);
}
public static System.IntPtr PROCEDURE (System.Delegate d)
{
return System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(d);
}
}
А теперь модуль Test:
public static unsafe class Test
{
public struct Message // : SYSTEM.ANYREC
{
public SYSTEM.TYPE* type;
public static readonly SYSTEM.TYPE* Type;
static Message ()
{
Type = (SYSTEM.TYPE*)SYSTEM.ALLOC(sizeof(SYSTEM.TYPE));
Type->ext0 = Type;
}
}
public struct MouseMsg // : Message
{
public SYSTEM.TYPE* type;
public int x;
public int y;
public static readonly SYSTEM.TYPE* Type;
static MouseMsg ()
{
Type = (SYSTEM.TYPE*)SYSTEM.ALLOC(sizeof(SYSTEM.TYPE));
Type->ext0 = Message.Type;
Type->ext1 = Type;
}
}
public struct KeyboardMsg // : Message
{
public SYSTEM.TYPE* type;
public char ch;
public static readonly SYSTEM.TYPE* Type;
static KeyboardMsg ()
{
Type = (SYSTEM.TYPE*)SYSTEM.ALLOC(sizeof(SYSTEM.TYPE));
Type->ext0 = Message.Type;
Type->ext1 = Type;
}
}
public struct Controller // : SYSTEM.ANYREC
{
public SYSTEM.TYPE* type;
public System.IntPtr handler;
public static readonly SYSTEM.TYPE* Type;
static Controller ()
{
Type = (SYSTEM.TYPE*)SYSTEM.ALLOC(sizeof(SYSTEM.TYPE));
Type->ext0 = Type;
}
}
public delegate void Handler (Controller* controller, Message* message);
public struct MyController // : Controller
{
public SYSTEM.TYPE* type;
public System.IntPtr handler;
public static readonly SYSTEM.TYPE* Type;
static MyController ()
{
Type = (SYSTEM.TYPE*)SYSTEM.ALLOC(sizeof(SYSTEM.TYPE));
Type->ext0 = Controller.Type;
Type->ext1 = Type;
}
}
public static Handler myControllerHandler = new Handler(MyHandler);
public static void MyHandler (Controller* controller, Message* message)
{
if (message->type->ext1 == MouseMsg.Type)
{
MouseMsg* m = (MouseMsg*)message;
System.Console.WriteLine("MyController.Handler() Receive MouseMsg x={0} y={1}", m->x, m->y);
}
else if (message->type->ext1 == KeyboardMsg.Type)
{
KeyboardMsg* m = (KeyboardMsg*)message;
System.Console.WriteLine("MyController.Handler() Receive KeyboardMsg ch={0}", m->ch);
}
}
public static void Do ()
{
MyController c;
c.type = MyController.Type;
c.handler = SYSTEM.PROCEDURE(myControllerHandler);
Smth((Controller*)&c);
}
private static void Smth (Controller* c)
{
MouseMsg mouseMsg;
mouseMsg.type = MouseMsg.Type;
mouseMsg.x = 10;
mouseMsg.y = 20;
((Handler)SYSTEM.DELEGATE(c->handler, typeof(Handler)))(c, (Message*)&mouseMsg);
KeyboardMsg keyboardMsg;
keyboardMsg.type = KeyboardMsg.Type;
keyboardMsg.ch = 'A';
((Handler)SYSTEM.DELEGATE(c->handler, typeof(Handler)))(c, (Message*)&keyboardMsg);
}
}
По производительности реализация на C# будет медленнее из-за того что в C# вместо указателей на процедуры используются делегаты (которые суть динамические объекты). Тормозить будет конвертация из указателя в делегат.
Лучше будет, видимо, вовсе не использовать GetDelegateForFunctionPointer/GetFunctionPointerForDelegate, а класть делегаты в глобально доступный статический массив и вместо указателя на функцию использовать индекс в этом массиве делегатов. Проигрыш по производительности всё равно будет, но не таким большим как при постоянном мусорении объектами делегатов.