Recent

Author Topic: A Generic Inheriting a Generic in a Generic Way - 2  (Read 2934 times)

IndigoBoy83

  • New member
  • *
  • Posts: 9
A Generic Inheriting a Generic in a Generic Way - 2
« on: February 20, 2015, 10:12:01 pm »
- THIS IS A CONTINUATION OF THE PREVIOUS POST -
A Generic Inheriting a Generic in a Generic Way - 1

------
Now, back to the problem that started this.  I could very well be on the wrong track in how I am approaching my problem.

The problem I had was that I wanted a generic of data_type to inherit a generic of data_type.  i.e.:

Code: [Select]
generic TMyListBase<data_type> = class
  Procedure VirtualProc( data : data_type ); virtual; abstract;
  Procedure InternalProc; // <-- which uses VirtualProc
end;

generic TMyList<data_type> = class (specialize TMyListBase<data_type>)
  .. whatever I need goes here ..
end; 

Then, I can make

Code: [Select]
TIntegerList = specialize TMyList<integer>
TBooleanList = specialize TMyList<boolean>
TStringList = specialize TMyList<string>
etc..

and have them implement their own VirtualProc and I only had to define InternalProc once!

Now, I *do* actually have a working workaround to this problem, but ideally I'd like it to be solved as stated above.

Also, my problem is in fact not wholly mine!  I share it with another programmer:  http://forum.lazarus.freepascal.org/index.php?topic=9933.0

My solution using interjections follows, with example usage:

----START OF CODE a generic inheriting a generic in a generic way--
Code: [Select]
{ In this unit we will construct the classes:
  TMyListBoolean, TMyListByte, TMyListInteger
  TMyListBaseBoolean, TMyListBaseByte, TMyListBaseInteger }

unit TMyLists;
 
interface
 
uses
  Classes;
 
type

Interjection GTMyListBase_interface;
start
  GTMyListBase = class
    Procedure VirtualProc( data : data_type ); virtual; abstract;
    Procedure InternalProc;
  end;
stop;

Interjection GTMyList_interface;
start
  generic GTMyList<data_type> = class (GTMyListBase)
    Procedure VirtualProc; override;
  end; 
stop;

Interjection Interface_Code( TypePtr : pointer );
start
  {$extdef 'data_type' := NameThatType(TypePtr)}
  {$extdef 'GTMyListBase' := 'TMyListBase' + NameThatType(TypePtr)}
  {$extdef 'GTMyList' := 'TMyList' + NameThatType(TypePtr)}
    {$Interject GTMyListBase_interface}
    {$Interject GTMyList_interface}
  {$unextdef 'data_type'}
  {$unextdef 'GTMyListBase'}
  {$unextdef 'GTMyList'}   
stop;

{$Interject Interface_Code( boolean )}
{$Interject Interface_Code( byte )}
{$Interject Interface_Code( integer )}

implementation

Interjection GTMyListBase_implementation( TypePtr : pointer );
start
  Procedure GTMyListBase.InternalProc( data : data_type );
  begin
    writeln( 'InternalProc was just invoked from inside TMyListBase!' );
    writeln( 'Now calling VirtualProc with data = ', data );   
    VirtualProc( data ); 
  end;
stop;

Interjection Implementation_Code( TypePtr : pointer );
start
  {$extdef 'data_type' := NameThatType(TypePtr)}
  {$extdef 'GTMyListBase' := 'TMyListBase' + NameThatType(TypePtr)}
    {$Interject GTMyListBase_implementation}
  {$unextdef 'data_type'}
  {$unextdef 'GTMyListBase'}
stop;

{$Interject Implementation_Code( boolean )}
{$Interject Implementation_Code( byte )}
{$Interject Implementation_Code( integer )}

Procedure TMyListBoolean.VirtualProc( data : data_type );
begin
  writeln( 'VirtualProc was just invoked from inside TMyListBoolean with data = ', data );
end;

Procedure TMyListByte.VirtualProc( data : data_type );
begin
  writeln( 'VirtualProc was just invoked from inside TMyListByte with data = ', data );
end;

Procedure TMyListInteger.VirtualProc( data : data_type );
begin
  writeln( 'VirtualProc was just invoked from inside TMyListInteger with data = ', data );
end;

var
   booleanlist : TMyListBoolean;
   bytelist : TMyListByte;
   integerlist : TMyListInteger;

begin
  booleanlist := TMyListBoolean.Create;
  bytelist := TMyListByte.Create;
  integerlist := TMyListInteger.Create;
 
  writeln( 'Now calling TMyListBoolean''s InternalProc with data = ', true );   
  booleanlist.InternalProc( true );
  writeln; 
 
  writeln( 'Now calling TMyListByte''s InternalProc with data = ', 250 );   
  bytelist.InternalProc( 250 );
  writeln;
 
  writeln( 'Now calling TMyListInteger''s InternalProc with data = ', -10 );   
  integerlist.InternalProc( -10 );
  writeln;
 
  FreeAndNil( booleanlist );
  FreeAndNil( bytelist );
  FreeAndNil( integerlist );
end.


THE OUTPUT OF THE ABOVE PROGRAM IS:

Now calling TMyListBoolean's InternalProc with data = TRUE
InternalProc was just invoked from inside TMyListBase!
Now calling VirtualProc with data = TRUE
VirtualProc was just invoked from inside TMyListBoolean with data = TRUE

Now calling TMyListByte's InternalProc with data = 250
InternalProc was just invoked from inside TMyListBase!
Now calling VirtualProc with data = 250
VirtualProc was just invoked from inside TMyListByte with data = 250

Now calling TMyListInteger's InternalProc with data = -10
InternalProc was just invoked from inside TMyListBase!
Now calling VirtualProc with data = -10
VirtualProc was just invoked from inside TMyListInteger with data = -10

----END OF CODE a generic inheriting a generic in a generic way----

What are the advantages of interjections over templates?

1) -We can have all required code in one file
2) -Thus, it is more readable
3) -Perhaps we could make the debugger step through the interjections just like we do normal procedures and also convert the code using the macros *on* *the* *fly*!  That would be cool!
4) -Also, it would be great if the debugger was able to go to the line of code that is problematic when compiling, and show appropriate identifier names

Problems encountered, disadvantages:

1) -It is not as clean as actually having a generic inherit a generic; maybe the compiler can use interjections and "hide" all of the above so the programmer does not have to bother with the nitty-gritty
2) -It should be possible to use interjections defined outside your unit if the unit is used.  In doing so, however, sometimes you may be required to share certain variables between units that you would otherwise not want to.

------
Now, can this be of any help to anyone?  I hope so!  Personally, I'd be able to use interjections to simplify my work.

Now, I've probably made lots and lots of mistakes above..  so please take it all with a heaping cup of salt (plus I originally wrote most of it at night, couldn't sleep again..).

And if you can tell me how to make a generic class inherit a generic class in a generic way - without interjections - then please tell me, before I write this.

Hope you dream wonderful programs in the future!
IndigoBoy83

taazz

  • Hero Member
  • *****
  • Posts: 5363
Re: A Generic Inheriting a Generic in a Generic Way - 2
« Reply #1 on: February 20, 2015, 10:21:26 pm »
merge them in one thread please and delete the second.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

IndigoBoy83

  • New member
  • *
  • Posts: 9
Re: A Generic Inheriting a Generic in a Generic Way - 2
« Reply #2 on: February 20, 2015, 11:32:28 pm »
merge them in one thread please and delete the second.

I'm sorry.  It says:

"The following error or errors occurred while posting this message:
The message exceeds the maximum allowed length (20000 characters)."

What to do?

taazz

  • Hero Member
  • *****
  • Posts: 5363
Re: A Generic Inheriting a Generic in a Generic Way - 2
« Reply #3 on: February 20, 2015, 11:51:26 pm »
reply to your first message with the rest of the second part
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64