OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Вторник, 19 Март, 2024 05:39

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 30 ]  На страницу 1, 2  След.
Автор Сообщение
 Заголовок сообщения: Про разработку компонентов..
СообщениеДобавлено: Понедельник, 24 Апрель, 2006 20:45 

Зарегистрирован: Суббота, 22 Апрель, 2006 21:30
Сообщения: 35
Буквально недавно я думал, что разобрался с тем как строятся компоненты.
Но реально начав писать их я понял, что ровным счетом ничего не понял и надо начинать с начала. Хэлп я уже читал, но по нему трудно понять как правильно строятся компоненты.

На данный момент много вопросов, но буду задавать постепенно. Надеюсь кто-нибудь ответит.

Назначение абстрактных записей? Как на их основе строить каркас? И как этот каркас можно использовать?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Понедельник, 24 Апрель, 2006 22:07 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Абстрактная запись - это базовый тип, который имеет только интерфейс, но не имеет реализации. Что это нам дает? Давайте на примере. Создаем модуль для работы с файлами в нашем каркасе, как это сделано в ББ:
Код:
MODULE Files;
(*Объявим асбтрактные типы, с которыми будут работать пользователи*)
TYPE
  File = POINTER TO ABSTRACT RECORD ... END; (* Файл *)
  Locator = POINTER TO ABSTRACT RECORD ... END; (* Описатель пути, НЕ сама директория - всего лишь удобная замена строковому представлению *)
  Reader = POINTER TO ABSTRACT RECORD ... END;
  Writer = POINTER TO ABSTRACT RECORD ... END;
  (* Курьеры для доступа к файлу *)

  (* Объявим методы для наших типов *)
  PROCEDURE (f: File) NewReader (old: Reader), NEW, ABSTRACT;
...

А где реализация? Реализации нет и в этом модуле и не будет. Клиентские модули будут работать, естественно, с переменными каких-то конкретных типов-потомков, но даже не будут подозревать об этом. Более того, в зависимости от ситуации можно им подсовывать экземляры разных типов. Но как клиентскому модулю получить экземпляр конкретного типа, ничего не зная о нем? Для этого используется известный в ООП механизм фабрик типа, которые в ББ называются директории.
Объявляется опять же асбтрактный тип, через интерфейс которого клиенты могут создавать экземпляры других типов модуля:
Код:
(* Продолжаем наш Files *)
TYPE Directory = POINTER TO ABSTRACT RECORD ... END
PROCEDURE (d: Directory) New (loc: Locator; ask: BOOLEAN): File, NEW, ABSTRACT;
...

И финальный штрих - объявим экспортированную переменную-указатель на фабрику:
Код:
VAR dir-: Directory;

Все, интерфейс для клиентов закончен, больше клиентам ничего знать не надо. Они могут создавать файлы через обращение Files.dir.New(...).
Теперь плавно переходим к реализации. Финальный шрих нашего модуля Files:
Код:
VAR stdDirectory: Directory;
PROCEDURE SetDirectory (d: Directory);
BEGIN
  dir := d;
  IF stdDirectory = NIL THEN
    stdDirectory := d
  END
END SetDirectory;


Все. Каркас для файлов готов.

Теперь пишем модуль реализации:
Код:
MODULE HostFiles;
IMPORT Files; (* Обратите внимание - системщики часто это не сразу понимают - в компонентных системах низкоуровневые модули импортируют высокоуровневые, а не наоборот *)

(* Здесь мы объявляем конкретные типы - потомки абстрактных и реализовываем их *)
TYPE ...
(* Чаще всего эти типы вообще НЕ ЭКСПОРТИРУЮТСЯ. В случае с HostFiles и HostWindows это все же сделано, чтобы дать выход на некоторые конкретные нюансы платформы. Но программист должен отдавать себе отчет в том, что использование таких нюансов надо ограничивать несколькими местами своей программы, закрывая их за новыми своими типами, которые в случае чего можно быстро переписать *)

TYPE Directory = POINTER TO RECORD (Files.Directory) ... END;

Наш модуль будет прилинкован сразу после Kernel и должен будет о себе "заявить", подставив при своей загрузке свою фабрику HostFiles.Directory в Files:
Код:
PROCEDURE Init;
  VAR d: Directory;
BEGIN
  NEW(d);
  FIles.SetDirectory(d)
END Init;

BEGIN
  Init
END.


Что мы имеем? HostFiles с момента своей загрузки в память обеспечивает всю реальную работу с файлами, при этом никто из пользовательских модулей о его существовании знать не обязан и даже не должен.

Дальше начинается самое интересное. Обычной файловой системы нам мало. В ББ есть возможность припаковывать файлы к EXE, создавая своего рода виртуальную файловую систему внутри приложения. Как нам обеспечить прозрачную работу с ней? Да проще пареной репы :-)

Делается еще один модуль HostPackedFiles, в котором реализуются File/Locator/Directory для работы с упакованными файлами.
В нем объявляется следующие процедуры:
Код:
PROCEDURE SetFilesDir;
BEGIN
(* здесь мы запоминаем текущую Files.dir, а потом подставляем новую *)
  Files.SetDirectory(d)
END SetFilesDir;

PROCEDURE RestoreFilesDir;
BEGIN
  Files.SetDirectory(запомненная старая директория)
END SetFilesDir;

Откуда-нибудь, например, из конфигурационного файла Config, вызываем HostPackedFiles.SetFilesDir. В Files инсталлируется новая директория.
Это директория сама решает, относится ли запрошенный файл к упакованной файловой системе или к реальной. В первом случае клиенту возвращается HostPackedFiles.File, во втором - HostFiles.File, c которыми клиент работает совершенно одинаково и даже не подозревает о хитрых манипуляциях. Потом можно отключить HostPackedFiles, если будет желание.

А захотим сделать прозрачный доступ к FTP через Files - да сколько угодно.
У меня, например, есть модуль, позволяющий работать с шифрованными файлами. Щелкаешь на коммандер - и все сохраняемые файлы начинают шифроваться, а при загрузке - расшифровываться. Щелкаешь на другой - работа с шифрованием отключается.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Понедельник, 24 Апрель, 2006 22:42 

Зарегистрирован: Суббота, 22 Апрель, 2006 21:30
Сообщения: 35
Спасибо. Кое что началось прояснятся.

1. А в чем смысл в таком определении:

TYPE MyRec = POINTER TO ABSTRACT RECORD END;

И в таком:

TYPE MyRec = ABSTRACT RECORD END;

То что первая, это указатель на запись это понятно.
Но не понятен смысл такого определения.

2. Когда описываешь метод записи TYPE MyRec = EXTENSIBLE RECORD END;
нада писать PROCEDURE (VAR m: MyRec) ......
А когда описываешь метод указателя на запись: TYPE MyRec = POINTER TO EXTENSIBLE RECORD END;
надо писать PROCEDURE (m: MyRec) ......
Иначе компилятор ругается.

3. И наконец
Чем такое:

TYPE MyRec = ABSTRACT RECORD
MyProp-:INTEGER;
END;
PROCEDURE (VAR m: MyRec) MyProc* (), NEW, ABSTRACT;

Отличается от такого:

TYPE MyRec = EXTENSIBLE RECORD
MyProp-:INTEGER;
END;
PROCEDURE (VAR m: MyRec) MyProc*(), NEW, EMPTY;

PS: Иногда кажется вот, наконец, понял компонентный паскаль, а потом оказывается, что ничего и не понял.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вторник, 25 Апрель, 2006 00:34 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Да, документация устроена дурно - найти нужные вещи трудно. Тем не менее эти нюансы КП описаны - можно посмотреть раздел "Что нового в языке?".
TYPE MyRec = POINTER TO RECORD END
это все равно, что объявить:
Код:
TYPE
  MyRec = POINTER TO MyRecDesc;
  MyRecDesc = RECORD ... END;

Однако тогда у пользователя останется возможность использовать тип и статически, что очень часто нежелательно. В частности, указатели мы можем выдавать пользователю "контролируемо", проводя нужную инициализацию. При создании через NEW память обнуляется, при создании в стеке VAR r: MyRecDesc будут обнулены только поля-указатели. В общем, есть веские причины делать некоторые типы только динамическими. Тогда не вводят статический тип вообще, а пишут единственное объявление POINTER TO ABSTRACT RECORD ...

Self-параметр метода должен передаваться либо по ссылке (VAR r: MyRecDesc), либо через указатель (r: MyRec) . Для динамических типов, ясно, можно объявлять методы только вторым способом. А вот для статических типов можно объявить как VAR-методы, так и POINTER-методы. Первые методы можно будет вызывать для любого экземпляра типа - как статического, так и динамического - ведь указатели, передаваемые параметром, неявно разыменовываются.
А вот вторые можно будет вызывать только через указатель, то есть, только для переменных в куче (в КП указатель может указывать только на объект в куче).

Теперь об ABSTRACT. Этот модификатор запрещает создавать любые экземпляры типа. Т.е. от типа можно только наследоваться. Единственное его назначение - описывать базовый интерфейс семейства типов.
Для ASBTRACT-записи некоторые процедуры можно реализовать, а можно оставить нереализованными, пометив их также ABSTRACT.

EXTENSIBLE же говорит, что тип может быть расширен в дальнейшем. Но он может использоваться и сам по себе. ABSTRACT-методов у не-ABSTRACT типа быть не может. А вот EMPTY - пожалуйста. Это будет заглушка, которая при вызове ничего не будет делать. Зато в расширенных типах ее можно переопределить, как и EXTENSIBLE.
EMPTY равносильно тому, что написать:
PROCEDURE (t: Type) Proc, EXTENSIBLE;
END;

А у абстрактных типов абстрактными помечают те процедуры, которые расширенный тип (если он тоже не абстрактный) обязан реализовать, иначе компилятор скажет об ошибке. Например, у Views.View метод Restore - абстрактный, и любое отображение обязано его реализовать. А все другие расширяемые методы - либо EXTENSIBLE, либо EMPTY. Мы можем их и не реализовывать.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вторник, 25 Апрель, 2006 08:49 

Зарегистрирован: Суббота, 22 Апрель, 2006 21:30
Сообщения: 35
Вопрос по предыдущему Вашему сообщению:

А как сделать что бы программа переключалась между низкоуровневыми модулями реализующими абстрактные типы в Files?
Например, между HostFiles и HostPackedFile или HostFTPFile?

Что бы пользователь, например, вводил только путь(может url), а программа определяла, что это и выбирала разными реализациями.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вторник, 25 Апрель, 2006 09:31 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Последняя из установленных Directory при запросе к ней, например, dir.New("ftp://..") сама решает, может (и хочет ли) она отработать такой запрос. Если нет, то передает вызов ниже, другой директории и возвращает File другого, не своего, типа.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вторник, 25 Апрель, 2006 09:54 

Зарегистрирован: Суббота, 22 Апрель, 2006 21:30
Сообщения: 35
Спасибо. Многое стало ясно.

И ещё.

Вы писали примерно такое:

Цитата:
MODULE HostFiles;
IMPORT Files;

PROCEDURE Init;
VAR d: Directory;
BEGIN
NEW(d);
FIles.SetDirectory(d)
END Init;

BEGIN
Init
END HostFiles.


Получается низкоуровневые модули иницилизируют модули верхнего уровня, а не наоборот?
BB вызывает все модули у которых есть:

BEGIN
Процедура инициализации();
END Модуль.

Так?

Если так, то например как BB поймет, когда я линкую EXE, где у меня находится ГЛАВНЫЙ МОДУЛЬ который тоже имеет:

BEGIN
Тело программы.
END ГлавныйМодуль.

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


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вторник, 25 Апрель, 2006 10:03 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 18:55
Сообщения: 2272
Откуда: Россия, Нижний Новгород
Grabli писал(а):
Если так, то например как BB поймет, когда я линкую EXE, где у меня находится ГЛАВНЫЙ МОДУЛЬ

Никак не поймёт, Вы это должны будете сами ему явно указать.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Вторник, 25 Апрель, 2006 11:54 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Еще раз посоветую Вам до конца прочесть тему:
http://blackbox.metasystems.ru/forum/vi ... .php?t=132

Там вопросы линковки/загрузки/инициализации подробно обсуждались.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 26 Апрель, 2006 12:21 

Зарегистрирован: Суббота, 22 Апрель, 2006 21:30
Сообщения: 35
Хотелось бы ещё узнать:

Вот такой, трах тибидох, будет работать?

В одном модуле создаем каркас.

TYPE Человек = POINTER TO ABSTRACT RECORD; END;
Мужчина = POINTER TO ABSTRACT RECORD(Человек); END;
Женщина = POINTER TO ABSTRACT RECORD(Человек); END;

PROCEDURE (h: Человек) Оболванить* (ch: Человек), NEW, ABSTRACT;

Ну соответственно человека, мужчину и женщину реализовываем в другом модуле. А потом в нем же:

VAR Чел: Человек;
Муж: Мужчина;
Жен: Женщина;

BEGIN
(* Куралесим с Муж и Жен. А потом *)
Чел.Оболванить(Муж);
Чел.Оболванить(Жен);
END;

Извиняйте, если ерунду спрашиваю.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 26 Апрель, 2006 14:26 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Да, работать будет. Будет вызываться метод того типа, на который фактически указывает переменная. Т.е. в Вашем коде можно переменной Чел присвоить указатель на мужчину/женщину/нечто другое. И будет вызываться тот метод, который для того типа реализован.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 26 Апрель, 2006 16:16 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 18:55
Сообщения: 2272
Откуда: Россия, Нижний Новгород
Grabli писал(а):
TYPE Человек = POINTER TO ABSTRACT RECORD; END;
Мужчина = POINTER TO ABSTRACT RECORD(Человек); END;
Женщина = POINTER TO ABSTRACT RECORD(Человек); END;

Кстати, современная медицина способна превратить мужчину в женщину, а женщину в мужчину. Так что типов "Мужчина" и "Женщина" как расширений типа "Человек" - нету, ведь тип уже созданного объекта поменять нельзя, а пол уже созданного человека - можно. :D :D :D


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 26 Апрель, 2006 17:05 

Зарегистрирован: Суббота, 22 Апрель, 2006 21:30
Сообщения: 35
Сергей Губанов писал(а):
Кстати, современная медицина способна превратить мужчину в женщину, а женщину в мужчину. Так что типов "Мужчина" и "Женщина" как расширений типа "Человек" - нету, ведь тип уже созданного объекта поменять нельзя, а пол уже созданного человека - можно. :D :D :D


Нуууу. Медицина много чего может.

А можно обратиться через тип человек к типу мужчина в методе Оболванить?

PROCEDURE (c: Человек) Оболванить* (ch: Человек);
BEGIN
ch(Мужчина).Поесть();
END Оболванить;

Или так нельзя?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения:
СообщениеДобавлено: Среда, 26 Апрель, 2006 19:00 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Да, можно.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Про разработку компонентов..
СообщениеДобавлено: Воскресенье, 08 Март, 2015 21:53 

Зарегистрирован: Пятница, 15 Апрель, 2011 22:41
Сообщения: 13
Все-таки я не очень понял, как осуществдяется "подмена". Только при загрузке модуля заменой фабрики?
А если нужно в каком-то модуле обеспечить независимую от конкретного типа обработку объектов? Т.е. этот модуль "знает" только абстрактные типы, а объекты к нему сыплются конкретные. Он вызывает метод абстрактного типа, а конкретный объект как сам должен определить, что именно ему нужно работать?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Про разработку компонентов..
СообщениеДобавлено: Воскресенье, 08 Март, 2015 21:59 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Не уверен, что понял Ваш вопрос,
но то, о чём Вы спрашиваете, называется виртуализация вызова (динамическое связывание вызова).
Это общий механизм всех объектно-ориентированных языков.

НЕвиртуальный вызов:
t.Do для случая, если переменная t имеет нерасширяемый тип (просто RECORD). Компилятор генерирует машинный код вида ВЫЗВАТЬ ПРОЦЕДУРУ (АДРЕС ПРОЦЕДУРЫ Do в модуле, где тип объявлен t)

Виртуальный вызов:
t.Do, если переменная t имеет EXTENSIBLE или ABSTRACT-тип.
Комплятор генерирует код вида:
- от объекта t пойти к описателю его типа;
- у описателя типа есть таблица процедур типа, взять там реальный адрес процедуры Do для реального типа объекта t;
- вызвать процедуру по этому полученному реальному адресу процедуры.

Так вызов и попадает сразу на нужную процедуру, в зависимости от фактического типа объекта, на который указывает t (и независимо от того, что сама переменная t имеет базовый, абстрактный тип).


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Про разработку компонентов..
СообщениеДобавлено: Понедельник, 09 Март, 2015 09:16 

Зарегистрирован: Пятница, 15 Апрель, 2011 22:41
Сообщения: 13
Спасибо! Да, я примерно так и представлял. Непонятно вот что: есть модуль, в котором описаны абстрактные типы, они экспортируются, в т.ч. их абстрактные методы:
TYPE Rec*=POINTER TO ABSTRACT RECORD END;
TYPE RecSet*=POINTER TO RECORD
r*:POINTER TO ARRAY OF Rec;
END;
PROCEDURE (r:Rec) Do* (), NEW, ABSTRACT;

В других модулях реализуем их, но не экспортируем.
Еще один модуль реализует работу (например, математику) со списком объектов, которые являются наследниками абстрактного типа, но они могут быть разными его реализациями (он импортирует первый модуль):
PROCEDURE DoMath* (rs:RecSet);
VAR i:INTEGER;
BEGIN
FOR i:=0 TO LEN(r)-1 DO
rs.r[i].Do;
END;
END DoMath;
(предполагаем, что rs где-то заполнился конкретными r разных типов)
Это будет работать? Или я что-то неправильно делаю, с т.з. архитектуры?


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Про разработку компонентов..
СообщениеДобавлено: Понедельник, 09 Март, 2015 10:48 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Если Вас смущает именно момент "в других модулях реализуем, но не экспортируем", то это всего лишь синтаксическая условность.

Понятно, что процедура, реализовавшая абстрактную, фактически экспортирована (будет доступна для вызова через виртуализацию).
И в ББ можно, кстати, спокойно поставить значок экспорта - вообще у любой процедуры даже неэкспортированного типа. Это ни на что не влияет.

Тут вопрос, на который я не готов ответить (некогда было уточнять) - эта возможность однозначно следует из описания языка или это неточность описания языка (соответственно, в другой реализации, нежели ББ, могут понять по-другому и не разрешить; хотя авторы КП - именно разработчики ББ, так что ББ можно рассматривать как отражение именно их понимания во всех не до конца понятных из описания местах).

Практический смысл от возможности поставить значок экспорта у процедуры неэкспортированного типа... Да никакого особо. Разве что чтобы подчеркнуть, что эта процедура - реализация абстрактной и на самом деле доступна для вызова извне.

У Вас, вроде, всё нормально. Архитектурно смущает открытость поля r (т.е. данные доступны кому угодно не через методы), но для каких-нибудь конкретных вычислительных задач, где не хочется громоздить и терять производительность на оборачивании всех-всех данных в методы, можно и так.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Про разработку компонентов..
СообщениеДобавлено: Понедельник, 09 Март, 2015 11:19 

Зарегистрирован: Пятница, 15 Апрель, 2011 22:41
Сообщения: 13
Цитата:
У Вас, вроде, всё нормально. Архитектурно смущает открытость поля r (т.е. данные доступны кому угодно не через методы), но для каких-нибудь конкретных вычислительных задач, где не хочется громоздить и терять производительность на оборачивании всех-всех данных в методы, можно и так.

Это просто абстрактный пример, я не стал его "окультуривать". На самом деле, у меня записи добавляются клонированием прототипа:
PROCEDURE (rs:RecSet) Add* (VAR prototype:Rec);
PROCEDURE (r:Rec) Clone* ():Rec;

Меня смущает, что модуль (назовем его Math) и его методы (DoMath) работают с указателями на абстрактный Rec, "не зная" о конкретных типах. Т.е. каким-то образом экземпляры Rec, на самом деле имеющие типы OneRec, TwoRec, и т.д. должны вызвать именно свой метод Do, и сделать это в момент выполнения. Есть сомнения, что я правильно организую работу. Что-то здесь не так...


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Про разработку компонентов..
СообщениеДобавлено: Понедельник, 09 Март, 2015 13:10 
Аватара пользователя

Зарегистрирован: Четверг, 08 Октябрь, 2009 15:00
Сообщения: 3774
С виду, все вы верно делаете. Работая с абстрактным объектами, ваш модуль будет знать конкретный тип объектов и вызывать соответствующие методы. Также вы можете еще уточнять алгоритм в зависимости от типа реализации с помощью WITH.

В BlackBox у каждой записи или массива есть тег, в котором содержится дополнительная информация, в том числе, по-видимому, и необходимая для реализации таких механизмов. Этот тег очень важен и для автоматической сборки мусора. А возможно в массиве абстрактных переменных реально уже будут хранится указатели на объекты из реализаций, а различные ограничения на соответствие типов проверяются в момент компиляции... Было бы интересно выяснить точно как это реализовано.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 30 ]  На страницу 1, 2  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
cron
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2024, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB