?
Solved

Using a "Class of object" and Virtualization

Posted on 2008-11-19
23
Medium Priority
?
320 Views
Last Modified: 2013-11-23
I have several descendants based on "TMyObject" (which is in unit1 -see below.  Descendants are in different units).  Each descendant implements its own "Execute" method.

In each case I want to create an instance of the descendant, run its execute method and destroy it. I am trying to have one common function in unit 1 that does this (in order to avoid duplication).  However I am unable to properly instantiate the child object by passing its classtype as an argument to the "VirtualisedRun" function.   If virtualization is working OK, I'd expect DescendentClass.Create(Ptr, result) to call the create function in unit 2.  That seems to work Ok. However the virtual "Execute" doesn't seem to get called.  See pseudo code below.  

Basically, I want a working sample that can I can pass a derived class as an argument to a function defined in the parent unit, and have the function properly instantiate an object of that class.


unit Unit1;
 
interface
  TMyObject=class(TObject)
  public
    constructor Create(Ptr:Pointer; var result:LongInt);virtual;
    function Execute:longint;virtual;abstract;  //must be defined in the decendent class.
    Destructor Destroy;override;
  end;
 
  TMyObjectClass = Class of TMyObject;
 
function VirtualisedRun(DescendentClass:TMyObjectClass; Ptr: Pointer): LongInt; stdcall;
 
implementation
 
function VirtualisedRun(DescendentClass:TMyObjectClass; Ptr: Pointer): LongInt; stdcall;
 
begin;
  with DescendentClass.Create(Ptr, result) do
  try
    if result <> 0 then result := Execute;
  finally
    Destroy;
  end;
end;
 
 ============================================================
 
 unit Unit2;
 
 interface
  TMyChildObject1=class(TMyObject)
  public
    constructor Create(Ptr:Pointer; var result:LongInt);virtual;
    Destructor Destroy;override;
    function Execute:longint;virtual;
  end;
 
  function DoExecute(Ptr:Pointer):longint;
  
 implementation
 
  function DoExecute(Ptr:Pointer):longint;
  
  begin;
    result := VirtualisedRun(TMyChildObject, Ptr);
  end;
  
 
constructor TMyChildObject1.Create(Ptr:Pointer; var result:LongInt);virtual;
 
begin;
  inherited create(Ptr, result);
  FOtherObject := TOtherObject.Create;
end;
 
destructor TMyChildObject1.Destroy;
 
begin;
  FOtherObject.Free;
  inherited;
end;
 
constructor TMyChildObject1.Execute:longint;
 
begin;
  result := blah...
end;

Open in new window

0
Comment
Question by:ahalya
  • 9
  • 8
  • 4
  • +1
23 Comments
 
LVL 7

Author Comment

by:ahalya
ID: 22994384
Please ignore this question.  I'll reword it and post it again.

The "virtual" create works. The problem seems to be in abstract "Execute"
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 22994440
hehe, i was gonna post a possible solution ...
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 22994442
still interested ?
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 7

Author Comment

by:ahalya
ID: 22994479
Sure, why not.

Go ahead. please.  Thanks.


 



0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 1000 total points
ID: 22994506
You should be overriding in the child class...

---- Base ----
unit Base;

interface

type
  TMyObject         =  class(TObject)
  public
     constructor    Create(Ptr: Pointer; var result: LongInt); virtual;
     function       Execute: LongInt; virtual; abstract;
     destructor     Destroy; override;
  end;

type
  TMyObjectClass    =  class of TMyObject;

function   VirtualisedRun(DescendentClass: TMyObjectClass; Ptr: Pointer): LongInt; stdcall;

implementation

function VirtualisedRun(DescendentClass: TMyObjectClass; Ptr: Pointer): LongInt; stdcall;
begin;

  with DescendentClass.Create(Ptr, result) do
  try
     if (result <> 0) then result:=Execute;
  finally
     free;
  end;

end;

constructor TMyObject.Create(Ptr: Pointer; var result: LongInt);
begin

  inherited Create;
  result:=0;

end;

destructor TMyObject.Destroy;
begin

  inherited Destroy;

end;

end.

---- Child1 ----

unit Child1;

interface

uses
  Windows, SysUtils, Classes, Base;

type
  TMyChildObject1   =  class(TMyObject)
  private
     // Private declarations
     FOtherObject:  TStringList;
  public
     constructor    Create(Ptr: Pointer; var result: LongInt); override;
     destructor     Destroy; override;
     function       Execute: LongInt; override;
  end;

function   DoExecute(Ptr: Pointer): LongInt;

implementation

function DoExecute(Ptr: Pointer): LongInt;
begin;

  result:=VirtualisedRun(TMyChildObject1, Ptr);

end;

function TMyChildObject1.Execute: LongInt;
begin

  // Example
  result:=FOtherObject.Count;

end;

constructor TMyChildObject1.Create(Ptr: Pointer; var result: LongInt);
begin;

  // Perform inherited
  inherited Create(Ptr, result);

  // Example
  Inc(result);

  // Startup
  FOtherObject:=TStringList.Create;

end;

destructor TMyChildObject1.Destroy;
begin;

  // Resource protection
  try
     // Cleanup
     FOtherObject.Free;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

end.

----------

Regards,
Russell
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 22994511
i believe what your looking for is this:
i use this to populate for example items of a combobox
i left only the relevant code for you:

the thing you're looking for is :

var
  iRoot: TRoot;
  ClassRef: TRootClass;
begin
    ClassRef := Self;
    iRoot := ClassRef.Create;

call it like
  TChildObject.DoExecute;

type
  TRoot = class(TObject)
  protected
    procedure Execute; virtual;
  public
    class procedure DoExecute;  
  end;
 
  TRootClass = class of TRoot;
 
class procedure TRoot.DoExecute;
var iRoot: TRoot;
  ClassRef: TRootClass;
begin
  Items.Clear;
  if InheritsFrom(TRoot) then
  begin
    ClassRef := Self;
    iRoot := ClassRef.Create;
    try
      with iRoot do
      begin
        // here you can call any method of your child object
        iRoot.Execute;
      end;
    finally
      FreeAndNil(iRoot);
    end;
  end;
end;
 
procedure TRoot.Execute; 
begin
  Raise Exception.Create('procedure Execute must be overriden in child !');
end;

Open in new window

0
 
LVL 7

Author Comment

by:ahalya
ID: 22994549
Geert,

For your info:
When stepping in the CPU window, the call to "Execute" takes me to @AbstractError and not to the execute function on the derived class.
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 22994563
i'm still adapting to your code, just a minute :


0
 
LVL 7

Author Comment

by:ahalya
ID: 22994664
rllibby:

Your comment about missing 'override' takes me to the derived function, but still seem to have problems accessing the private variables of the derived class.  I'll check this and get back to you.

This is the first time I am using an abstract method, and am a little puzzled.  Shouldn't "abstract" imply override.  (What other option is there ?)

Geert:
Need time to study your code.
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 22994684
You call descendants of my object like
 
type
  TChildOfRoot = class(TRoot)
  protected
    procedure Execute; override;
  end;

TChildOfRoot.DoExecute;

note the override as Russel did ...

0
 
LVL 7

Author Comment

by:ahalya
ID: 22994718
Russell,

OK, got your point.  You meant overriding the constructor as well.




0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22994735
>> Your comment about missing 'override' takes me to the derived function, but still seem to have problems accessing the private variables of the derived class.  I'll check this and get back to you. <<

Please check my example again. My derived class created, accessed, and then freed the string list object which is declared as private in the class.

Russell


0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22994782
Yep.
 Keep in mind that when you declare virtual methods in a parent class, you should be overriding these methods in the children. Otherwise, you end up calling the VMT method (which is still loaded with the parent's method). Overriding allows you to extend the functionality PLUS be able to call the parent's virtual declaration by calling "inherited"

Russell

0
 
LVL 38

Assisted Solution

by:Geert Gruwez
Geert Gruwez earned 1000 total points
ID: 22994846
this is a combination with your virtualisedrun and a class function

you can call it like
Result := TMyChildObject.Run(Ptr);

or
Result := VirtualisedRun(TMyChildObject, Ptr);
unit UnitMyObject;
 
interface
 
type
  TMyObject = class(TObject)
  public
    constructor Create(Ptr: Pointer; var result: LongInt); virtual;
    function Execute: Longint; virtual;
    destructor Destroy; override;
 
    class function Run(Ptr: Pointer): Longint;
  end;
 
  TMyObjectClass = class of TMyObject;
 
  TMyChildObject = class(TMyObject)
  private
    fOtherObject: TStrings;
  public
    constructor Create(Ptr: Pointer; var result: LongInt); override;
    destructor Destroy; override;
    function Execute: longint; override;
  end;
 
function VirtualisedRun(DescendentClass: TMyObjectClass; Ptr: Pointer): LongInt; stdcall;
 
implementation
 
function VirtualisedRun(DescendentClass: TMyObjectClass; Ptr: Pointer): LongInt; stdcall;
begin
  Result := DescendentClass.Run(Ptr);
end;
 
{ TMyObject }
 
constructor TMyObject.Create(Ptr: Pointer; var result: Integer);
begin
  inherited Create(Ptr, Result);
  // create objects
end;
 
destructor TMyObject.Destroy;
begin
  // free objects
  inherited Destroy;
end;
 
class function TMyObject.Run(Ptr: Pointer): Longint;
var iObject: TMyObject;
  ClassRef: TMyObjectClass;
begin
  Result := 0;
  if InheritsFrom(TRoot) then
  begin
    ClassRef := Self;
    iRoot := ClassRef.Create(Ptr, Result);
    try
      if Result <> 0 then
        Result := iRoot.Execute;
    finally
      FreeAndNil(iRoot);
    end;
  end;
end;
 
{ TMyChildObject }
 
constructor TMyChildObject.Create(Ptr: Pointer; var Result: LongInt);
begin;
  inherited Create(Ptr, Result);
  FOtherObject := TStringList.Create;
end;
 
destructor TMyChildObject.Destroy;
begin;
  FOtherObject.Free;
  inherited Destroy;
end;
 
function TMyChildObject.Execute: Longint;
begin;
  Result := FOtherObject.Count;
end;
 
end.

Open in new window

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22995310
You posted in the C++ TA too?

Does that mean you are interested in a C++ solution as well?

0
 
LVL 7

Author Comment

by:ahalya
ID: 22995344
Thanks to both of you.  I'll double the points, and then split between the two.

Russell you suggestions fixed my problem. But, I am going to adapt Geert's idea because it looks like that will simplify the code further (by adding some complexity :-).

Geert, I learnt something new from your code.  Never thought of your "Run" trick.  Using your approach, I  I don't even need a centralised "VirtualizedRun" function. I can simply call DescendentClass.Run and let it do all the work.  (in my DoExecute function in the derived unit).

Now, what is the difference between a "class function" and a "function" ? (your Run method)
0
 
LVL 7

Author Comment

by:ahalya
ID: 22995371
Hey its "you" andnobodyelse:

I wouldn't not have minded whether the solution is in Delphi or C++. But already have Delphi solutions, so would not need cpp solution. (But would presume same approach works in cpp). Thans
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 22995373
with a class function you don't need to create an instance of an object

but you don't have access to any variables of the object
as they don't exist yet (no memory reserved yet)
0
 
LVL 7

Author Closing Comment

by:ahalya
ID: 31518237
Thanks Folks.

I learnt some basics, and a neat trick
0
 
LVL 7

Author Comment

by:ahalya
ID: 22995573
Geert,

I simplified your Run method to the following.  Do you see any potential pitfalls in doing a self.create ?

Am I right in assuming that there is no need to check for InheritsFrom(TFeedbackControl) because "Run" is defined as a method of TFeedbackControl ?

Thanks.



class function TFeedBackControl.Run(Ptr: Pointer): Longint;
 
begin
  with Self.Create(Ptr, Result) do
  try
    if result = ecNormal then
      Result := Execute;
  finally
    Free;
  end;
end;

Open in new window

0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22995677
from the docs...
>>
In the defining declaration of a class method, the identifier Self represents the class where the method is called (which could be a descendant of the class in which it is defined). If the method is called in the class C, then Self is of the type class of C. Thus you cannot use Self to access fields, properties, and normal (object) methods, but you can use it to call constructors and other class methods. A class method can be called through a class reference or an object reference. When it is called through an object reference, the class of the object becomes the value of Self.
<<

Used the way you have it is fine. And there is no need to check for InheritsFrom, as a class function can only be called from a compatible type class refererence. (Base or child)

Regards,
Russell
0
 
LVL 38

Expert Comment

by:Geert Gruwez
ID: 22995717
lol,
do you know how many hours i pained my brain on this ?
now i learned something too :)
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22997417
>>>> (But would presume same approach works in cpp)
Hmm. Actually I have problems to fully understand PASCAL code ...

But in C++ for your issue there is a well-known design pattern called 'factory'. The clue is to have something like a 'virtual' constructor without the baseclass have to know all derived classes. In C++ the pattern can be solved by 'registering' function pointers for each class which create a new instance of the class together with a key, e. g. the class name into a container managed by the baseclass (or some singleton Factory manager class). That way the baseclass could create instances of any derived (and registered) class by only knowing the name of the class. For example, you could write a textfile with lines preceeded with a classname followed by varying attributes separated by semicolon. Then, the baseclass could read the file line by line, parse the classname, retrieve a function pointer from the container by name, create an instance of the class using that function pointer, and pass the rest of the line to that instance to let it load its attributes. Instead of a function pointer you also could store a baseclass pointer of a sample instance of the derived class. Then, instead of a function call you would use a virtual call using the sample instance to create new instances. Each derived class would provide the function (or the object) to create new instances by means of a static initialization:

// derived.h

   class Derived : public Base
   {
        static bool registered;
        static Base* createDerived() { return new Derived(); }

        ....
   };

// derived.cpp

...
bool Derived::registered = Base::registerForFactory("Derived", Base::createDerived);


// base.h

// forward declaration
class Base;

// function pointer
typedef Base* (*CreateFunc)();
...
class Base
{
     std::map<std::string, CreateFunc> factory;
public:
     static bool registerForFactory(const std::string classname,
                                                    CreateFunc func)
     {
          factory[classname] = func;
          return true;
     }

      ...
};

Is it the same as the above solution in Delphi?
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The viewer will learn how to use and create new code templates in NetBeans IDE 8.0 for Windows.
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
Suggested Courses

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question