Поля - процедурные переменные

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

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

Re: Поля - процедурные переменные

Сообщение Sergei I. Gorelkin » 23.06.2010 21:51:29

Возможность переопределять TObject.NewInstance в ее текущем виде совершенно бесполезна, потому что при этом нет возможности ни передать туда какой-либо аргумент, ни получить тип результата, отличный от TObject. Выделить память на стеке внутри NewInstance нельзя, потому что такая модификация стека сделает невозможным возврат из процедуры.
Единственный работающий вариант собственного распределения памяти для классов - фабрика, выделяющая нужные блоки памяти и вызывающая сначала InitInstance, потом конструктор. При этом запретить создавать те же объекты стандартным способом нельзя, потому что конструктор всегда public.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Поля - процедурные переменные

Сообщение MageSlayer » 23.06.2010 22:11:06

Sergei I. Gorelkin писал(а):Возможность переопределять TObject.NewInstance в ее текущем виде совершенно бесполезна, потому что при этом нет возможности ни передать туда какой-либо аргумент, ни получить тип результата, отличный от TObject.

Непонятно зачем ей что-то передавать? Отнаследовал новый тип, переопределил NewInstance и порядок. Функция-то классовая - считай глобальная. Пускай читает глобальные переменные.
Sergei I. Gorelkin писал(а):Выделить память на стеке внутри NewInstance нельзя, потому что такая модификация стека сделает невозможным возврат из процедуры.

Зачем же внутри NewInstance? Надо снаружи, в самой верхней по стеке функции. Выделить раз, и нарезать ее, при необходимости делая fallback на стандартный getmem.
Sergei I. Gorelkin писал(а):Единственный работающий вариант собственного распределения памяти для классов - фабрика, выделяющая нужные блоки памяти и вызывающая сначала InitInstance, потом конструктор. При этом запретить создавать те же объекты стандартным способом нельзя, потому что конструктор всегда public.

Как-то слишком категорично.
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: Поля - процедурные переменные

Сообщение Sergei I. Gorelkin » 24.06.2010 00:22:42

MageSlayer писал(а):Непонятно зачем ей что-то передавать? Отнаследовал новый тип, переопределил NewInstance и порядок. Функция-то классовая - считай глобальная. Пускай читает глобальные переменные.

С глобальными переменными очень тяжело создавать один и тот же тип объектов, беря память поочередно от разных менеджеров памяти (из разных блоков). Передать информацию о блоке в конструктор можно без проблем, а в NewInstance - никак.

MageSlayer писал(а):Как-то слишком категорично.

Решал я задачу управлением пулом объектов, получил массу удовольствия, потому и категорично.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Поля - процедурные переменные

Сообщение MageSlayer » 24.06.2010 21:17:17

Sergei I. Gorelkin писал(а):
MageSlayer писал(а):Непонятно зачем ей что-то передавать? Отнаследовал новый тип, переопределил NewInstance и порядок. Функция-то классовая - считай глобальная. Пускай читает глобальные переменные.

С глобальными переменными очень тяжело создавать один и тот же тип объектов, беря память поочередно от разных менеджеров памяти (из разных блоков). Передать информацию о блоке в конструктор можно без проблем, а в NewInstance - никак.

MageSlayer писал(а):Как-то слишком категорично.

Решал я задачу управлением пулом объектов, получил массу удовольствия, потому и категорично.


Э-э. Вроде задача решается даже в общем виде. Допускаю, что чего-то не понимаю.
Грубо, по типу следующего:
Код: Выделить всё
program project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes
  { you can add units after this };

{$R *.res}

type
  TFakeClass = class
    class function newinstance : tobject;override;
  end;

class function TFakeClass.newinstance : tobject;
begin
  //эмуляция нестандартной кучи
  getmem(Pointer(Result), InstanceSize);

  if Result <> nil then
     InitInstance(Result);
end;

function CloneClass(C:TClass;var Vmt:TVmt):TClass;
begin
  Move( (PPointer(C))^, Vmt, SizeOf(Vmt) );
  Vmt.vNewInstance:=@TFakeClass.newinstance;
  Result:=TClass(@Vmt);
end;

type
  { TClass1 }
  TClass1 = class
    private
      FStr:String;
    public
      constructor Create;
  end;
  TMetaClass1 = class of TClass1;

{ TClass1 }

constructor TClass1.Create;
begin
  FStr:='Test';
end;

var O:TClass1;
    OMeta:TMetaClass1;
    V:TVmt;
begin
  OMeta:=TMetaClass1(CloneClass(TClass1,V));

  O:=OMeta.Create;
  Writeln(O.FStr);
  O.Free;
end.

... ну, и собственно, нужно просто наделать несколько переменных метаклассов и реализаций TFakeClass.
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: Поля - процедурные переменные

Сообщение Sergei I. Gorelkin » 25.06.2010 00:04:19

Ага, TVmt - полезный тип :) Чтобы не умереть от скромности, скажу, что он появился в RTL как раз в результате моих изысканий :)
Только с ним есть небольшая проблема: TVmt является не структурой класса целиком, а только "заголовком", за которым следуют адреса виртуальных методов. А общий размер этой конструкции нигде не хранится. Так что предложенное "клонирование" сработает только для классов без виртуальных методов.

А вот что получается, если не лезть в NewInstance и создавать объекты с помощью фабрики:
Код: Выделить всё
procedure TFactory.Alloc(aClass: TSomeClass): TObject;
begin
  result := CustomGetMemory(aClass.InstanceSize);
  aClass.InitInstance(result);
  result.Create;
end;

...почувствуйте разницу.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Поля - процедурные переменные

Сообщение MageSlayer » 26.06.2010 11:12:32

Sergei I. Gorelkin писал(а):Только с ним есть небольшая проблема: TVmt является не структурой класса целиком, а только "заголовком", за которым следуют адреса виртуальных методов. А общий размер этой конструкции нигде не хранится. Так что предложенное "клонирование" сработает только для классов без виртуальных методов.

Жаль, очень жаль.

Sergei I. Gorelkin писал(а):А вот что получается, если не лезть в NewInstance и создавать объекты с помощью фабрики:
Код: Выделить всё
procedure TFactory.Alloc(aClass: TSomeClass): TObject;
begin
  result := CustomGetMemory(aClass.InstanceSize);
  aClass.InitInstance(result);
  result.Create;
end;

...почувствуйте разницу.

Ну, это слишком просто :)
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Пред.

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

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

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

Рейтинг@Mail.ru
cron