Параметризовать конструктор

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Re: Параметризовать конструктор

Сообщение Дож » 31.07.2014 12:56:43

А в этом случае вам весь этот механизм не нужен, поскольку прекрасно будет работать тупое жёсткое преобразование типа объекта к нужном классу/объекту.

Мне нужны виртуальные методы, потому что я не знаю какого конкретного типа у меня объект.

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

У меня есть виртуальные методы в исходном родительском классе, которые переопределяют наследники и потому их условно можно назвать «старыми» виртуальными методами, если смотреть с позиции потомков.

И, если честно, я вообще не понимаю, зачем вам тогда ООП?

Чтобы использовать по назначению — определить в родителе виртуальные методы и поменять поведение в наследниках.

Каким образом вы собираетесь менять поведение ваших объектов, если не используете виртуальных функций?

Я использую виртуальные методы, заданные в родительском классе.

Если у вас в коде функции компилятор будет считать, что у вас объект класса TList, всегда будет вызываться невиртуальный метод Add для класса TList, даже если вы исхитритесь и подсуните ему во время исполнения вместо TList свой новый наследник TIntegerList.

Я не понимаю что должен прояснить этот пример. Если Add в классе TList невиртуален, то да, разумеется, что компилятор сделает статический вызов и наследники на это не влияют.

Если же имелось в виду, что когда Add объявлен виртуально, но переменная явно имеет тип TList, то компилятор всё равно вызовет статически, — то нет, это не верно. Тут я вынужден опять обратить Ваше внимание на то, что Вы невнимательно читали тему, и снова дать ссылку на всё тот же пост
viewtopic.php?f=1&t=9904#p81947
в котором следует посмотреть на программу и на результат её работы. Такое поведение задумано для объектов изначально
http://wiki.freepascal.org/Programming_ ... estructors
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Параметризовать конструктор

Сообщение MylnikovDm » 31.07.2014 13:09:46

Всё, ясно, я не правильно понял вашу первую фразу про "нет новых виртуальных методов".
MylnikovDm
постоялец
 
Сообщения: 103
Зарегистрирован: 15.02.2007 21:26:10
Откуда: Челябинск

Re: Параметризовать конструктор

Сообщение Дож » 31.07.2014 13:16:55

Раз уж обсуждение продолжается, то сообщаю, что по мотивам полученных в этом топике знаний и чтения исходников я научился менять поведение объектов на лету :) Сразу скажу, что считаю функцию SwitchState довольно грязной и опасной, но с поправкой на то, что вместо SizeOf(PtrInt) там должно быть vmtpos, функция гарантированно работоспособна.

Собственно, код, протестированный в Windows/Linux/FreeBSD на 32 битных и 64 битных архитектурах:
Код: Выделить всё
[doj@korica ~/temp]$ cat switch.pas
type
TAnimal = object
  FName: AnsiString;
  constructor Init(const Name: AnsiString);
  procedure SwitchState(VMT: Pointer);
  procedure TellAboutYourSelf; virtual; abstract;
end;

TCat = object(TAnimal)
  procedure TellAboutYourSelf; virtual;
end;

TDog = object(TAnimal)
  procedure TellAboutYourSelf; virtual;
end;

constructor TAnimal.Init(const Name: AnsiString);
begin
  FName := Name;
end;

procedure TAnimal.SwitchState(VMT: Pointer);
begin
  PPointer(PByte(@Self) + SizeOf(PtrInt))^ := VMT;
end;

procedure TCat.TellAboutYourSelf;
begin
  Writeln('Я кот по имени ', FName, '.');
end;

procedure TDog.TellAboutYourSelf;
begin
  Writeln('Я ', FName, ', гав-гав!');
end;

var
  Pet: TAnimal;

begin
  Pet.Init('Бусик');
  Pet.SwitchState(TypeOf(TCat));
  Pet.TellAboutYourSelf;
  Pet.SwitchState(TypeOf(TDog));
  Pet.TellAboutYourSelf;
end.
[doj@korica ~/temp]$ fpc -Mobjfpc switch.pas && ./switch
Я кот по имени Бусик.
Я Бусик, гав-гав!
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Параметризовать конструктор

Сообщение Vapaamies » 31.07.2014 13:19:09

MylnikovDm писал(а):Да и если уж на то пошло, то смещение полей в теле объекта вообще нет смысла хранить, поскольку при наследовании и добавлении новых полей в наследниках смещение существовавших до этого полей не изменяется. Вопрос не в смещениях, а в том, сколько выделять памяти под размещение данных объекта.

Противоречия в своей логике не видите? Смещения потому и изменятся, если выделить под объект столько памяти, сколько нужно. Его и растягивать-сжимать во время выполнения тоже можно будет, стоит только захотеть.

MylnikovDm писал(а):Кроме того, есть RTTI, коорая раньше создавалась только для published полей, а сейчас, если мне не изменяет память, после введения механизмов связывания, в последних Delphi она создаётся для всех полей. Там и смещение, и размер, и тип, и много ещё чего есть. Вот только рамзещать объекты на стеке или в коллекциях и массивах это никак не помогает.
Так правильно, они же отказались развивать свой компилятор, на LLVM теперь переходят. Было бы желание -- давно бы свою же RTTI и использовали. А они ограничились покупкой FastMM и успокоились. Мол, процы сейчас мощные, частоту памяти уже гигарерцами меряют, и так сойдет.
Аватара пользователя
Vapaamies
постоялец
 
Сообщения: 291
Зарегистрирован: 24.07.2012 22:37:59
Откуда: Санкт-Петербург

Re: Параметризовать конструктор

Сообщение MylnikovDm » 31.07.2014 21:11:45

Vapaamies писал(а):Противоречия в своей логике не видите? Смещения потому и изменятся, если выделить под объект столько памяти, сколько нужно. Его и растягивать-сжимать во время выполнения тоже можно будет, стоит только захотеть.

Я не вижу противоречия в своей логике, поскольку прекрасно знаю как работает компилятор, мне много раз приходилось работать с отладчиком и ассемблерным кодом. Поля размещаются компилятором согласно их порядку в описании класса. При объявлении классов наследников смещения полей, объявленных в предке не меняются. Новые поля из наследника добавляются в конец.
Также я не совсем понимаю для чего экземпляр объекта "сжимать-растягивать во время выполнения"? Вы хотите, чтобы у вас количество полей в классе менялось уже во время исполнения программы? Ну так это уже вообще из другой оперы. Причём даже в этом случае можно реализовать этот механизм так, чтобы статически объявленные поля в классе не меняли своего смещения.

Vapaamies писал(а):Так правильно, они же отказались развивать свой компилятор, на LLVM теперь переходят. Было бы желание -- давно бы свою же RTTI и использовали. А они ограничились покупкой FastMM и успокоились. Мол, процы сейчас мощные, частоту памяти уже гигарерцами меряют, и так сойдет.

У меня такое ощущение, что у вас какая-то каша в голове. FastMM - это просто быстрый менеджер памяти, который к объектной модели вообще никакого отношения не имеет, причём он был интегрирван в Delphi ещё в 2006 версии. RTTI вовсю используется в новом механизме LiveBindings, который как раз и содержит информацию о полях класса. А LLVM вообще виртуальная машина, которая на данный моент используется для мобильных платформ. К нативным компиляторам для платформы x86/x64 она никакого отношения не имеет. Редакция LLVM для Windows существует, но пратически используется мало. При этом нативные программы по определению будут более эфективны, чем при использовании LLVM. Фактически LLVM это очередная попытка создать байт-кодовую виртуальную машину типа Java от Oracle или .NET от Microsoft, но на этот раз от клана производителей мобильных устройств при яростной поддержке Apple и Google.
MylnikovDm
постоялец
 
Сообщения: 103
Зарегистрирован: 15.02.2007 21:26:10
Откуда: Челябинск

Re: Параметризовать конструктор

Сообщение Дож » 31.07.2014 21:23:37

А LLVM вообще виртуальная машина, которая на данный моент используется для мобильных платформ.

Нет, LLVM — это (1) абстрактный платформо-независимый ассемблер (2) оптимизирующий компилятор из этого ассемблера в конечный машинный код.

Проект LLVM используется в clang — кроссплатформенный компилятор C++, который сейчас уже теснит и в чём-то даже обходит старичка gcc (по оптимизациям в том числе). Насколько понимаю, последние версии FreeBSD сейчас по умолчанию собираются с помощью clang, а не gcc.

LLVM — правильная и прорывная технология, а не какая-то там очередная попытка чего-нибудь. К сожалению, компиляцию из FreePascal в LLVM пока что ждать не приходится.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Параметризовать конструктор

Сообщение MylnikovDm » 31.07.2014 22:34:24

Дож писал(а):Нет, LLVM — это (1) абстрактный платформо-независимый ассемблер (2) оптимизирующий компилятор из этого ассемблера в конечный машинный код.


Да, действительно, почитал подробнее. С одной стороны интересно, а с другой, принципиально ничего не меняет для обсуждаемой темы. LLVM является промежуточным представлением кода. Упрощает перенос компиляторов на разные платфомры и всяческую оптимизацию конечного кода. Но там нет никаких объектов и логичеких структур, с которыми мы работаем на уровне Object pascal или C++.
Опять же, с точки зрения развития компиляторов переход к использованию LLVM в качестве промежуточного слоя вполне оправдан, поскольку упрощает дальнейшее использование на разных платформах. что в последнее время стало весьма актуально. Так что вряд ли это стоит считать минусом. С другой стороны, объектная модель и все её механизмы всё равно остаются на уровне исходного компилятора, то бишь Object Pascal. Закончат перевод компилятора на использование LLVM, будут дальше заниматься его развитием. Я никакого криминала в этом не вижу, скорее одни плюсы.

Кстати, первые компиляторы, с которыми мне приходилось работать в конце 80-х, начале 90-х как раз генерировали текстовый файл на языке ассемблера, который уже потом компилмировался и линковался штаными средствами ОС. При желании можно было провести дополнительную оптимизацию сгенерированного ассемблерного кода, чем мы иногда и занимались. Всё возвращается на круги своя. :) Разве что еперь ассемблер платформонезависимый.
MylnikovDm
постоялец
 
Сообщения: 103
Зарегистрирован: 15.02.2007 21:26:10
Откуда: Челябинск

Re: Параметризовать конструктор

Сообщение Дож » 31.07.2014 22:43:53

Я никакого криминала в этом не вижу, скорее одни плюсы.

Безусловно, LLVM — это хорошая штука.

Кстати, первые компиляторы, с которыми мне приходилось работать в конце 80-х, начале 90-х как раз генерировали текстовый файл на языке ассемблера, который уже потом компилмировался и линковался штаными средствами ОС. При желании можно было провести дополнительную оптимизацию сгенерированного ассемблерного кода, чем мы иногда и занимались. Всё возвращается на круги своя.

LLVM предоставляет API для написания на C++ собственных шагов оптимизации. Говорят, удобное.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Параметризовать конструктор

Сообщение MylnikovDm » 01.08.2014 06:48:46

Дож писал(а):LLVM предоставляет API для написания на C++ собственных шагов оптимизации. Говорят, удобное.

??? С++ сам по себе имеет несколько уровней трансляции, поэтому если уж говорить об оптимизации, то только на уровне ассемблера. Тем более, что сами С++ компиляторы сейчас тоже, насколько я понял, пытаются перевести на LLVM.
Разве что речь может идти не об оптимизации кода транслируемой программы, а о написании шагов оптимизации уже для кода псевдоассемблера LLVM, который сам написан на С++. Ну так это речь о другом.
Опять же, когда речь заходит о низкоуровневой оптимизации, то она обычно делается с учётом конкретной архитектуры процессора и его особенностей. Я помню, что мы сидели со справочниками по командам процессора и высчитывали до каждого такта, какой вариант кода будет работать быстрее. Хотя, тогда это имело какой-то смысл, а сейчас, с учётом многоуровневых конвееров, всяческих предсказателей переходов, и т.п. наворотов это уже вряд даёт такой же эффект.
Другими словами, мы можем разработать некий дополнительный модуль на С++, который будет оптимизировать трансляцию кодов LLVM на конкретное железо, но применительно ко всем программам на LLVM, а не для конкретного алгоритма. Тогда это будет иметь какой-то смысл, особенно для всяческого редкого железа.
Тут ведь смысл в чём. LLVM написана на С++, соответственно, если под целевую платформу имеется компилятор С, то дальше вы уже автоматически получаете на своей платформе все прелести LLVM, включая работу всех языков программирования, которые LLVM используют. А поскольку практически для всех платформ сейчас стараются в первую очередь сделать именно компилятор С, то мы получаем весьма эффективную технологию как для разработчиков железа, так и для разработчиков всяческих компиляторов и сред разработки.
MylnikovDm
постоялец
 
Сообщения: 103
Зарегистрирован: 15.02.2007 21:26:10
Откуда: Челябинск

Re: Параметризовать конструктор

Сообщение Vapaamies » 01.08.2014 20:49:17

MylnikovDm писал(а):При объявлении классов наследников смещения полей, объявленных в предке не меняются.

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

MylnikovDm писал(а):У меня такое ощущение, что у вас какая-то каша в голове. FastMM - это просто быстрый менеджер памяти, который к объектной модели вообще никакого отношения не имеет

Имеет самое прямое в рамках рассматриваемого вопроса. Предположим, есть следующее описание:
Код: Выделить всё
type
  TDetail = class
    IntValue: Integer;
  end;

  TMain = class
    SomeValue: Integer;
    Detail1, Detail2: TDetail;
  end;

При конструировании TMain процедура GetMem будет вызвана три раза: для самого TMain и по разу для Detail1 и Detail2. Все вызовы лягут на менеджер памяти, и общая скорость выполнения программы будет зависеть от его эффективности. Если же Detail1 и Detai2 были бы вложены в тело TMain и обозначены смещениями, вызов GetMem был бы один.
Аватара пользователя
Vapaamies
постоялец
 
Сообщения: 291
Зарегистрирован: 24.07.2012 22:37:59
Откуда: Санкт-Петербург

Re: Параметризовать конструктор

Сообщение MylnikovDm » 01.08.2014 23:20:40

Уважаемый Vapaamies, то, что вы хотите получить, это уже не обычная компиляция, а некая динамическая система управления данными, которая будет перестраиваться на лету. Это в принципе невозможно откомпилировать, это можно только интерпретировать по ходу работы. При этом одним вызовом GetMem при создании TMain в серёзной системе вы не обойдётесь, поскольку на этапе создания TMain вы ещё можете не знать, какой в конечном итоге класс будет у Detail1 и Detail2, может быть TDetail, а может быть и любой из классов наследников, причём у Detail1 и Detail2 они могут быть разные. В сегодняшней модели с этим проблем нет, поскольку все классы являются указателями на некую область памяти с данными конкретного экземпляра класса. А вот в случае с вашей моделью придётся перерасперделять память заново, "раздвигая" поля данных.

Но самое главное даже не в этом. Сейчас для обращения к любому полю данных компилятор считывет из переменной Detail1 указатель на область данных, добавляет к нему смещение, и может уже читать или писать значение. В вашей же модели придётся сначала получить указатель на VMT (предположим, что там же хранятся смещения полей данных), добавить к нему смещение для считывания нуной ячейки, откуда мы считаем другое смещение, уже данных в области объекта. Дальше это смещение нужно прибавить к указателю на объект, и только после этого мы сможем считывать или записывать значение. Итого, при каждом обращении к любому полю данных у вас добавилось два дополнительных чтения данных из памяти (VMT и смещения поля данных), и дополнительное сложение, плюс один из регистров будет дополнительно занят для хранения указателя на сам объект, пока мы будем проделывать все эти операции по вычислению реального адреса поля данных.

Упс! Пока писал, понял, что работать эта схема не будет, поскольку у разных экземпляров объекта TMain могут быть разные классы наследники для Detail1 и Detail2. А это означает, что вы не сможете хранить одну общую таблицу смещений полей для всего класса TMain, как это делается для виртуальных процедур и функций. Вам придётся хранить такую таблицу смещений для каждого экземпляра объекта! Иначе никакого полиморфизма вам не будет.
А это значит, что накладные расходы для вашей модели ещё увеличились, причём существенно.

Итого, я готов биться об заклад, что три вызова GetMem будут в итоге работать намного быстрее и эффективнее, чем дополнительные затраты на каждое считывание и запись данных, которые увеличивают время обращения к каждому полю данных фактически в три раза, да ещё и дополнительный раход памяти для каждого экземпляра объекта. Память под объекты вы в любом случае выделяете намного реже, чем обращаетесь к их полям данных. Если в ваших программах это не так, то значит вы не умеете писать программы.
MylnikovDm
постоялец
 
Сообщения: 103
Зарегистрирован: 15.02.2007 21:26:10
Откуда: Челябинск

Re: Параметризовать конструктор

Сообщение Vapaamies » 02.08.2014 00:06:02

MylnikovDm писал(а):Итого, я готов биться об заклад, что три вызова GetMem будут в итоге работать намного быстрее и эффективнее, чем дополнительные затраты на каждое считывание и запись данных, которые увеличивают время обращения к каждому полю данных фактически в три раза, да ещё и дополнительный раход памяти для каждого экземпляра объекта.

На самом деле тут скорей будет похоже на повторение функциональности GetMem, только размазанной по объектам. И накладные расходы тоже размазанные. Менеджер кучи ведь тоже хранит много служебной инфы, поэтому еще неизвестно, где расход памяти будет больше. У GetMem расходы на каждый экземпляр, а не на класс в целом.

Будут ли вложенные поля создавать дополнительную нагрузку на процессор и/или его кэш, нужно еще обдумать. Чисто по прикидкам, обращение к объектным полям в обязательном порядке требует Self в регистре, куда он кладется из тела владельца, так что обращение к памяти задействуется и блок с куском объекта в кэш попадает, и промах вряд ли случится... Нужно еще обдумать и код примерный написать.

MylnikovDm писал(а):Память под объекты вы в любом случае выделяете намного реже, чем обращаетесь к их полям данных. Если в ваших программах это не так, то значит вы не умеете писать программы.

Гм, гм... а если взять код любой процедуры SaveToFile, ее авторы тоже не умеют писать программы? Подобный код встречается довольно часто, так что аргумент о частых обращениях к полям нужно подкреплять статистикой.
Аватара пользователя
Vapaamies
постоялец
 
Сообщения: 291
Зарегистрирован: 24.07.2012 22:37:59
Откуда: Санкт-Петербург

Re: Параметризовать конструктор

Сообщение Максим » 02.08.2014 02:46:57

Дож писал(а):К сожалению, компиляцию из FreePascal в LLVM пока что ждать не приходится.

Кому не приходится, а кому - наоборот. :mrgreen:
Аватара пользователя
Максим
энтузиаст
 
Сообщения: 597
Зарегистрирован: 27.07.2007 01:51:43
Откуда: Москва

Re: Параметризовать конструктор

Сообщение MylnikovDm » 02.08.2014 11:44:21

Vapaamies писал(а):На самом деле тут скорей будет похоже на повторение функциональности GetMem, только размазанной по объектам. И накладные расходы тоже размазанные. Менеджер кучи ведь тоже хранит много служебной инфы, поэтому еще неизвестно, где расход памяти будет больше. У GetMem расходы на каждый экземпляр, а не на класс в целом.

Во-первых, если вы собираетесь "раздвигать" поля данных в объекте, то ни о каком размещении на стеке или в коллекциях речи уже не идёт. Всё равно придётся разещать их на куче.
Во-вторых, операция realloc, то есть перевыделение памяти большего объёма, требует больше времени и ресурсов, чем обычная GetMem, поскольку далеко не факт, что можно увеличить имеющийся блок памяти сзади, поскольку там уже могут быть другие данные. На практике выделяется новый блок памяти большего размера, после чего туда копируются данные из старого блока памяти. И, что самое главное, в итоге вы получаете новый указатель на область данных. Так что если вы уже наплодили ссылок на ваш объект, то вам либо придётся их все скорректировать, либо городть особую структуру для обращения к объекту, когда реальный указатель хранится в специальной структуре связи, а все ссылки ведут на эту структуру. Тогда да, тогда можно менять указаетль на область памяти "на лету", но в этом случае накладные расходы, опять же, возрастают, поскольку нужно будет считывать два указателя, а не один, причём при каждом обращении к объекту через указатель.

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

Будут ли вложенные поля создавать дополнительную нагрузку на процессор и/или его кэш, нужно еще обдумать. Чисто по прикидкам, обращение к объектным полям в обязательном порядке требует Self в регистре, куда он кладется из тела владельца, так что обращение к памяти задействуется и блок с куском объекта в кэш попадает, и промах вряд ли случится... Нужно еще обдумать и код примерный написать.

Сейчас, когда вы обращаетесь к объекту, вы в любом случае считываете указатель на этот объект. Соответсвтенно, во все вызовы, где требуется Self, будет складываться он же. То есть, никаких ополнительных обращений к памяти не потребуется.
В предлагаемом вами механизме, прежде чем получить доступ к полю данных в объекте Detail1, вы дожны будете в начале считать смещение к его области данных из объекта Main, чтобы узнать откуда начинается таблица полей объекта Detail1. Потом считать смещеие к полю данных Detail1 из его таблицы смещений, и только после этого вы сможете определить адрес поля данных и осуществить к нему доступ. Итого, две дополнительные операции чтения данных и три сложения для вычисления адреса.
Кстати, мысль "написать примерный код" весьма здравая, попробуйте, а потом будем спорить.

Vapaamies писал(а):Гм, гм... а если взять код любой процедуры SaveToFile, ее авторы тоже не умеют писать программы? Подобный код встречается довольно часто, так что аргумент о частых обращениях к полям нужно подкреплять статистикой.


Я так понимаю, что вы имели в виду код процедуры LoadFromFile? Да, подобный код будет практчиески в каждом объекте в большой и сложной прикладной системе, поскольку нам нужно сохранять и загружать данные, с которыми мы работаем, иначе нет смысла в наших программах. Но вы явно путаете наличие кода и частоту его использования в программе. Постоянное чтение и запись данных будет происходить только в каких-либо конверторах данных, которые преобразуют файлы одного формата в другой. В остальных прикладных программах соотношение времени загрузки данных к остальной обработке не более 5%, а во многих расчётных программах или программах моделирования, где данные загружаются сравнительно редко, может быть и менее 1%. У нас вон архитекторы при работе могут утром файл один раз открыть и целый день с ним работать.
MylnikovDm
постоялец
 
Сообщения: 103
Зарегистрирован: 15.02.2007 21:26:10
Откуда: Челябинск

Re: Параметризовать конструктор

Сообщение Vapaamies » 04.08.2014 12:55:38

MylnikovDm писал(а):На практике выделяется новый блок памяти большего размера, после чего туда копируются данные из старого блока памяти. И, что самое главное, в итоге вы получаете новый указатель на область данных.

Ну вот, в нашей дискуссии уже есть полезный результат. Момент с новым указателем я упустил. Не обсуждал ни с кем, не приходилось. До реализации тоже не дошел, иначе бы вылезло.

MylnikovDm писал(а):Кстати, мысль "написать примерный код" весьма здравая, попробуйте, а потом будем спорить.

Примерный код будет после окончания жары.

MylnikovDm писал(а):У нас вон архитекторы при работе могут утром файл один раз открыть и целый день с ним работать.

Не используем запрещенные приемы, не передергиваем! Я ясно написал про SaveToFile, а не LoadFromFile, хоть их код различается несильно. Ваши архитекторы что, так сильно не уважают свой труд, что не сохраняют файлы и отключают автоматическое сохранение, если оно есть?
Аватара пользователя
Vapaamies
постоялец
 
Сообщения: 291
Зарегистрирован: 24.07.2012 22:37:59
Откуда: Санкт-Петербург

Пред.След.

Вернуться в Free Pascal Compiler

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

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

Рейтинг@Mail.ru