Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2012-11

How to make class constructor body calls descendant's overriden method/property?

6 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

leledumbo

Cross posted from stackoverflow:
http://stackoverflow.com/questions/13431079/how-to-make-class-constructor-body-calls-descendants-overriden-method-property

I'm writing an ORM framework and got stuck in a way to automatically determine table name from class name. In my base object for the ORM to work, I have:

TghModel = class
...

  class var FTableName: String;
  class constructor Create;

...

whose implementation is:

class constructor TghModel.Create;
begin
  FTableName := ClassName;
  Delete(FTableName,1,1); // Delete 'T'
end;

My assumption was that ClassName method will return the real class name. e.g. if I have:

TUsers = class(TghModel)

then FTableName will be initialized to TUsers instead of TghModel, which is wrong. I want to avoid users to make class constructor for each classes inheriting from TghModel, especially because the content would be totally the same as in TghModel.Create. Is there any way to implement it?

JC Chu
to leledumbo

A CLASS CONSTRUCTOR is a special CLASS STATIC method executed during unit initialization. Because it is CLASS STATIC, Self is undefined (and thus (1) what you write for ClassName in TghModel.Create really stands for TghModel.ClassName, and (2) if TUsers inherits it, the same code will be executed).

What you want amounts to getting the list of classes with a common ancestor. To my knowledge is no way to do that automatically.

leledumbo wrote 39 lines
Cross posted from stackoverflow: http://stackoverflow.com/questions/13431079/how-to-make-class-constructor-body-calls-descendants-overriden-method-property I'm writing an ORM framework and got stuck in a way to automatically determine table name from class name. In my base object for the ORM to work, I have: TghModel = class ... class var FTableName: String; class constructor Create; ... whose implementation is: class constructor TghModel.Create; begin FTableName := ClassName; Delete(FTableName,1,1); // Delete 'T' end; My assumption was that ClassName method will return the real class name. e.g. if I have: TUsers = class(TghModel) then FTableName will be initialized to TUsers instead of TghModel, which is wrong. I want to avoid users to make class constructor for each classes inheriting from TghModel, especially because the content would be totally the same as in TghModel.Create. Is there any way to implement it? -- View this message in context: http://free-pascal-general.1045716.n5.nabble.com/How-to-make-class-constructor-body-calls-descendant-s-overriden-method-property-tp5711994.html Sent from the Free Pascal - General mailing list . _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Best regards, JC Chu
Joao Morais
to leledumbo: Cross posted from stackoverflow: http://stackoverflow.com/questions/13431079/how-to-make-c…
leledumbo wrote 30 lines
Cross posted from stackoverflow: http://stackoverflow.com/questions/13431079/how-to-make-class-constructor-body-calls-descendants-overriden-method-property I'm writing an ORM framework and got stuck in a way to automatically determine table name from class name. In my base object for the ORM to work, I have: TghModel = class ... class var FTableName: String; class constructor Create; ... whose implementation is: class constructor TghModel.Create; begin FTableName := ClassName; Delete(FTableName,1,1); // Delete 'T' end; My assumption was that ClassName method will return the real class name. e.g. if I have: TUsers = class(TghModel) then FTableName will be initialized to TUsers instead of TghModel, which is wrong. I want to avoid users to make class constructor for each classes inheriting from TghModel, especially because the content would be totally the same as in TghModel.Create. Is there any way to implement it?

Tell the user of the framework to do something like this for every class of your model:

  initialization
    TUser.RegisterClass;
    TProduct.RegisterClass;
    ...

or

  initialization
    RegisterClasses([TUser, TProduct, ...]);

Btw I've tried the following code:

  program Project1;
  {$mode objfpc}{$H+}
  type
    TBase = class(TObject)
    public
      class var name: string;
    end;
    TConcrete = class(TBase);
  begin
    TBase.name := 'base';
    TConcrete.name := 'concrete';
    writeln('TBase.name: ', TBase.name);
  end.

whose output is:

TBase.name: concrete

which means, if I'm not mistaken, the class var "name" is shared between all decendants, so perhaps your approach won't work very well. Please correct me if I'm wrong.

Joao Morais

leledumbo
to Joao Morais
Show quoted text 12 lines
Tell the user of the framework to do something like this for every class of your model: initialization TUser.RegisterClass; TProduct.RegisterClass; ... or initialization RegisterClasses([TUser, TProduct, ...]);

Seems like this is the most feasible way, there should be an internal data structure that maps registered classes with their respective table name.

Joao Morais
to leledumbo
leledumbo wrote 15 lines
> Tell the user of the framework to do something like this for every > class of your model: > > initialization > TUser.RegisterClass; > TProduct.RegisterClass; > ... > > or > > initialization > RegisterClasses([TUser, TProduct, ...]); Seems like this is the most feasible way, there should be an internal data structure that maps registered classes with their respective table name.

Both RegisterClass procedures I pointed out are methods of the framework, eg:

// The framework
  type
    TBaseModelClass = class of TBaseModel;
   TBaseModel = class(TObject)
    public
      class procedure RegisterClass;
    end;
  procedure FWRegisterClass(AClasses: array of TBaseModelClass);
  ...
  class procedure TBaseModel.RegisterClass;
  begin
    // Initialize the class metadata. "ClassName" works.
    writeln('Registering ', ClassName);
  end;
  procedure FWRegisterClass(AClasses: array of TBaseModelClass);
  var
    I: Integer;
  begin
    for I := 0 to Pred(Length(AClasses)) do
      AClasses[I].RegisterClass;
  end;
// The application
  type
    TUser = class(TBaseModel);
    TContact = class(TBaseModel);
  implementation
  initialization
    FWRegisterClass([TUser, TContact]);

Joao Morais

leledumbo
to Joao Morais
Both RegisterClass procedures I pointed out are methods of the framework,

eg:

Yes, I know. I implement it this way now:

procedure RegisterClass(AClass: TghModelClass);
procedure RegisterClass(AClass: TghModelClass; const AName: String);

...

procedure RegisterClass(AClass: TghModelClass);
var
  ClassName: String;
begin
  ClassName := AClass.ClassName;
  Delete(ClassName,1,1);
  RegisterClass(AClass,ClassName);
end;
procedure RegisterClass(AClass: TghModelClass; const AName: String);
begin
  ClassMap[AClass] := AName;
end;

...