Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Meta-Class what is the purpose


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 3 - Last Post: Nov 29, 2014 11:34 AM Last Post By: Quentin Correll
tim crouse

Posts: 83
Registered: 2/11/02
Meta-Class what is the purpose  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 28, 2014 8:20 AM
The statement from Chris Rolliston's Delphi XE2 foundations on page 153
"When you define a class you implicitly define a meta-class".

This appears to be an automatic result of defining a class. He goes on to explain that a meta-class is static as compared to the class itself.

Can someone add some clarification as to what a meta-class is and is not, as well as why and where I should consider using one?

Thanks
Tim C.
Peter Below

Posts: 1,227
Registered: 12/16/99
Re: Meta-Class what is the purpose  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 29, 2014 2:02 AM   in response to: tim crouse in response to: tim crouse
tim crouse wrote:

The statement from Chris Rolliston's Delphi XE2 foundations on page
153 "When you define a class you implicitly define a meta-class".

This appears to be an automatic result of defining a class. He goes
on to explain that a meta-class is static as compared to the class
itself.

Can someone add some clarification as to what a meta-class is and is
not, as well as why and where I should consider using one?

{$LECTUREMODE ON}

Well, a class itself (as opposed to an object, an instance of a class)
is implemented by the compiler as a block of memory in the program's
static data section. It is basically like a typed constant of a record
type, the memory block starts with a fixed-size portion (the actual
layout depends on compiler version and is not documented outside the
System unit) that contains things like the class name, pointers that
refer to other bits of memory that belong to the class definition, like
the dynamic method table, RTTI, a pointer to the parent class "record",
if there is a parent class, and so on. Following that is the virtual
method table of the class, which is a list of addresses of the classes'
virtual methods (including the ones inherited from the base class and
not overriden). This part is of different sizes in different classes.

If you define a class reference type (a metaclass type)

type
TClassA = class(TOBject)
end;
TClassAClass = class of TClassA;

and a variable of this class reference type

Var
aClass: TClassAClass;

then you can assign not only TClassA as value to this variable, but
also any class that descends from TClassA. The value of the variable
(if you cast it to Pointer) is the address of the classes' virtual
method table. So the value of a class is not directly the address of
the start of the class record mentioned above, but the address of the
start of the VMT in this record. This is an optimization, since the
compiler uses this address a lot to code calls to virtual methods. All
the other fields of the class record can be accessed through negative
offsets relative to this address. The System unit defines a set of
constants for these offsets.

Some of the methods of TClass (and TObject) give you access to some of
the fields of the class record.

var
Obj: TClassA;

Obj := TClassA.Create;

Obj.Classname -> returns the class name from the class record
Obj.Classtype -> returns the class itself, the address of the VMT
in the class record
Obj.ClassParent -> returns the address of the parent classes' VMT
Obj.InstanceSize -> returns the number of bytes allocated for an
instances' memory block, also recorded in a field of the class record
Obj.GetInterfaceTable -> returns the address of the classes'
interface table, a pointer that is also stored in the class record.

An instance of a class (object) is represented by a memory block
allocated at run-time from the heap. The value of the object (the
content of the object variable) is a pointer to this memory block. The
first item in this block is the address of the class VMT as returned by
the Classtype property.

While interesting these implementation details are usually not that
useful in programming. What you need to know is that a class reference
type gives you an easy way to create instances of classes in a
hierarchy, where the actual type of class to create is not known at
compile time.

Let's go back to the example I used in the reply to your other question
on virtual methods:

type
TClassA = class(TObject)
public
constructor Create; virtual;
procedure DoSomething; virtual;
end;
TClassB = class(TClassA)
public
constructor Create; override;
procedure DoSomething; override;
end;
TClassC = class(TClassB)
public
constructor Create; override;
procedure DoSomething; override;
end;
TClassAClass = class of TClassA;

and a variable of this class reference type

Var
aClass: TClassAClass;

you can assign TClassA, TClassB, or TClassC to it. Now consider
this:

aClass := GetAClass;

where

function GetAClass: TClassAClass;

can return any of the three classes mentioned above,
depending on some inner logic that is not of interest at the moment.

You can now create an object using the aClass variable:

var
aObj: TClassA;

aObj := aClass.Create;

The instance you get will be of type TClassA, TClassB, or TClassC,
depending on what class the call to GetAClass returned, and the correct
constructor will be called since the class hierarchy has a virtual
constructor in the base class, which is overridden in the descendent
classes.

If the constructors are not virtual you get the same behaviour you get
with static methods: the type of the object created depends on the
class stored in aClass, but the constructor called would always be
TClassA.Create.

This may be a bit confusing since people usually think that it is the
constructor that allocates the memory for the object. That is actually
not the case in Delphi. If you code a constructor call on a class
reference (like the code above does) the compiler actually inserts
calls to the NewInstance and InitInstance methods the class inherits
from TClass. The first method allocates the memory block, depending on
the InstanceSize field of the class record, the second method
initializes the memory block. Only after that will the constructor
itself be called, with the address of the newly allocated memory block
as the hidden Self parameter. A constructor in Delphi is basically just
a method to do some custom initialization on the virgin object. You can
actually call it on an existing object reference (although that is not
recommended), and it will act like any other method call in this
context.

In the context above, a statement like

aObj.Create;

is perfectly legal code, but it cannot be stressed enough that *this
will not create an object!* It just calls the Create method like a
normal procedure, passing the content of aObj as the hidden Self
parameter. Which tends to blow up in your face if aObj is still nil or
contains the address of a previously freed object...

{$LECTUREMODE OFF}

--
Peter Below (TeamB)

tim crouse

Posts: 83
Registered: 2/11/02
Re: Meta-Class what is the purpose  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 29, 2014 5:00 AM   in response to: Peter Below in response to: Peter Below
Holy Cow :), a lot to take in.

Thank You for the detailed answer.

-Tim C.

Peter Below wrote:
tim crouse wrote:

The statement from Chris Rolliston's Delphi XE2 foundations on page
153 "When you define a class you implicitly define a meta-class".

This appears to be an automatic result of defining a class. He goes
on to explain that a meta-class is static as compared to the class
itself.

Can someone add some clarification as to what a meta-class is and is
not, as well as why and where I should consider using one?

{$LECTUREMODE ON}

Well, a class itself (as opposed to an object, an instance of a class)
is implemented by the compiler as a block of memory in the program's
static data section. It is basically like a typed constant of a record
type, the memory block starts with a fixed-size portion (the actual
layout depends on compiler version and is not documented outside the
System unit) that contains things like the class name, pointers that
refer to other bits of memory that belong to the class definition, like
the dynamic method table, RTTI, a pointer to the parent class "record",
if there is a parent class, and so on. Following that is the virtual
method table of the class, which is a list of addresses of the classes'
virtual methods (including the ones inherited from the base class and
not overriden). This part is of different sizes in different classes.

If you define a class reference type (a metaclass type)

type
TClassA = class(TOBject)
end;
TClassAClass = class of TClassA;

and a variable of this class reference type

Var
aClass: TClassAClass;

then you can assign not only TClassA as value to this variable, but
also any class that descends from TClassA. The value of the variable
(if you cast it to Pointer) is the address of the classes' virtual
method table. So the value of a class is not directly the address of
the start of the class record mentioned above, but the address of the
start of the VMT in this record. This is an optimization, since the
compiler uses this address a lot to code calls to virtual methods. All
the other fields of the class record can be accessed through negative
offsets relative to this address. The System unit defines a set of
constants for these offsets.

Some of the methods of TClass (and TObject) give you access to some of
the fields of the class record.

var
Obj: TClassA;

Obj := TClassA.Create;

Obj.Classname -> returns the class name from the class record
Obj.Classtype -> returns the class itself, the address of the VMT
in the class record
Obj.ClassParent -> returns the address of the parent classes' VMT
Obj.InstanceSize -> returns the number of bytes allocated for an
instances' memory block, also recorded in a field of the class record
Obj.GetInterfaceTable -> returns the address of the classes'
interface table, a pointer that is also stored in the class record.

An instance of a class (object) is represented by a memory block
allocated at run-time from the heap. The value of the object (the
content of the object variable) is a pointer to this memory block. The
first item in this block is the address of the class VMT as returned by
the Classtype property.

While interesting these implementation details are usually not that
useful in programming. What you need to know is that a class reference
type gives you an easy way to create instances of classes in a
hierarchy, where the actual type of class to create is not known at
compile time.

Let's go back to the example I used in the reply to your other question
on virtual methods:

type
TClassA = class(TObject)
public
constructor Create; virtual;
procedure DoSomething; virtual;
end;
TClassB = class(TClassA)
public
constructor Create; override;
procedure DoSomething; override;
end;
TClassC = class(TClassB)
public
constructor Create; override;
procedure DoSomething; override;
end;
TClassAClass = class of TClassA;

and a variable of this class reference type

Var
aClass: TClassAClass;

you can assign TClassA, TClassB, or TClassC to it. Now consider
this:

aClass := GetAClass;

where

function GetAClass: TClassAClass;

can return any of the three classes mentioned above,
depending on some inner logic that is not of interest at the moment.

You can now create an object using the aClass variable:

var
aObj: TClassA;

aObj := aClass.Create;

The instance you get will be of type TClassA, TClassB, or TClassC,
depending on what class the call to GetAClass returned, and the correct
constructor will be called since the class hierarchy has a virtual
constructor in the base class, which is overridden in the descendent
classes.

If the constructors are not virtual you get the same behaviour you get
with static methods: the type of the object created depends on the
class stored in aClass, but the constructor called would always be
TClassA.Create.

This may be a bit confusing since people usually think that it is the
constructor that allocates the memory for the object. That is actually
not the case in Delphi. If you code a constructor call on a class
reference (like the code above does) the compiler actually inserts
calls to the NewInstance and InitInstance methods the class inherits
from TClass. The first method allocates the memory block, depending on
the InstanceSize field of the class record, the second method
initializes the memory block. Only after that will the constructor
itself be called, with the address of the newly allocated memory block
as the hidden Self parameter. A constructor in Delphi is basically just
a method to do some custom initialization on the virgin object. You can
actually call it on an existing object reference (although that is not
recommended), and it will act like any other method call in this
context.

In the context above, a statement like

aObj.Create;

is perfectly legal code, but it cannot be stressed enough that *this
will not create an object!* It just calls the Create method like a
normal procedure, passing the content of aObj as the hidden Self
parameter. Which tends to blow up in your face if aObj is still nil or
contains the address of a previously freed object...

{$LECTUREMODE OFF}

--
Peter Below (TeamB)

Quentin Correll


Posts: 2,412
Registered: 12/1/99
Re: Meta-Class what is the purpose  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 29, 2014 11:34 AM   in response to: Peter Below in response to: Peter Below
Peter,

Thanks for sharing that!!!!!!!

--

Q

1.19.1.372 (Q's Broken Toolbar.)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02