Recent

Author Topic: [SOLVED] Help with generics  (Read 8193 times)

anonymousstranger

  • New Member
  • *
  • Posts: 49
[SOLVED] Help with generics
« on: August 04, 2018, 04:28:22 am »
I am trying to use generics to build a doubly linked list of nodes that can accept any single uniform  datatype- including other nodes and chains. I would also like that if the field "value" is a node or a chain that the code will automatically create the applicable object. In order to to do this, I am implementing a conditional to check the type of "value" and compare it to the type "gptr" or "schain", since I would like the "value" feild to also accept base types "integer" and "set". Problem is though, when I try to compile, the debugger ignores the type conditional and evaluates the create statements using a base type which is of course incompatible. Is there a way around this?

Code: Pascal  [Select][+][-]
  1. {$m+}
  2. generic datatype<dtype>=class (tpersistent)
  3. type
  4.   datum=specialize datatype<dtype>;
  5. public
  6. var
  7.   value:dtype;
  8.  
  9. procedure valinit;
  10. constructor create; overload;
  11. destructor destroy; override;
  12. end;
  13.  
  14. generic node<dtype > = packed class
  15. type
  16.   datum=specialize datatype<dtype>;
  17.   gptr=specialize node<dtype>;
  18. var
  19.   fwd,bck:gptr;
  20.   data:datum;
  21. public
  22.   constructor create; overload;
  23.   destructor destroy; override;
  24. end;
  25.  
  26. generic chain<gptr,dtype>=packed class
  27. type
  28.   schain=specialize chain<gptr,dtype>;
  29. public
  30. var
  31.   first,last:gptr;
  32.   constructor create; overload;
  33.   destructor destroy; override;
  34. end;
  35.  
  36. procedure datatype.valinit;
  37. type
  38.   gptr=specialize node<dtype>;
  39.   schain=specialize chain<gptr,dtype>;
  40.  
  41. begin
  42.    if (ptypeinfo(typeinfo(self.value))^.kind<>tkset) and
  43.       (ptypeinfo(typeinfo(self.value))^.kind<>tkinteger) then
  44.       begin
  45.         with gettypedata(typeinfo(self.value))^.classtype do
  46.            begin
  47.                if classnameis('gptr') then
  48.                   self.value:=gptr.create             {debugger registers incompatible type defaulting to base type}
  49.                else if classnameis('schain') then
  50.                   self.value:=schain.create;         {debugger registers incompatible type defaulting to base type}  
  51.            end;
  52.       end;
  53. end;
  54.  
« Last Edit: August 21, 2018, 07:52:45 am by anonymousstranger »

soerensen3

  • Full Member
  • ***
  • Posts: 213
Re: Help with generics
« Reply #1 on: August 04, 2018, 11:17:46 am »
I'm not sure if I understood the question correctly but maybe this helps:

Code: Pascal  [Select][+][-]
  1. program Test;
  2. type
  3.   { TTestClass }
  4.  
  5.   TTestClass = class
  6.     constructor Create; virtual;
  7.   end;
  8.  
  9.   { TTestClass2 }
  10.  
  11.   TTestClass2 = class ( TTestClass )
  12.     constructor Create; override;
  13.   end;
  14.   TTestClassType = class of TTestClass;
  15.  
  16. { TTestClass2 }
  17.  
  18. constructor TTestClass2.Create;
  19. begin
  20.   inherited Create;
  21.   WriteLn( 'TTestClass.Create2' );
  22. end;
  23.  
  24. { TTestClass }
  25.  
  26. constructor TTestClass.Create;
  27. begin
  28.   WriteLn( 'TTestClass.Create' );
  29. end;
  30.  
  31. var
  32.   ClassType: TTestClassType;
  33.   obj1, obj2: TTestClass;
  34. begin
  35.   ClassType:= TTestClass;
  36.   obj1:= ClassType.Create;
  37.   ClassType:= TTestClass2;
  38.   obj2:= ClassType.Create;
  39.   obj1.Free;
  40.   obj2.Free;
  41. end.
  42.  
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

Blaazen

  • Hero Member
  • *****
  • Posts: 3237
  • POKE 54296,15
    • Eye-Candy Controls
Re: Help with generics
« Reply #2 on: August 04, 2018, 12:33:50 pm »
What compiler version you have? 3.0.4 or trunk?

Also, some compilable demo would be fine.

BTW, you can write:
Code: Pascal  [Select][+][-]
  1. if not (ptypeinfo(typeinfo(self.value))^.kind in [tkset, tkinteger]) then
Lazarus 2.3.0 (rev main-2_3-2863...) FPC 3.3.1 x86_64-linux-qt Chakra, Qt 4.8.7/5.13.2, Plasma 5.17.3
Lazarus 1.8.2 r57369 FPC 3.0.4 i386-win32-win32/win64 Wine 3.21

Try Eye-Candy Controls: https://sourceforge.net/projects/eccontrols/files/

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Help with generics
« Reply #3 on: August 04, 2018, 12:36:33 pm »
Note that trunk afaik has gettypekind to make such generic discussions on specialization.

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: Help with generics
« Reply #4 on: August 04, 2018, 01:02:26 pm »
Note that trunk afaik has gettypekind to make such generic discussions on specialization.
Yes,indeed. And in trunk you do not need to include the typinfo unit for that. As opposes to Blaazen's example. But that works in 3.0.4. too, provide unit typinfo IS in the uses clause.
Specialize a type, not a var.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Help with generics
« Reply #5 on: August 04, 2018, 04:24:19 pm »
I am trying to use generics to build a doubly linked list of nodes that can accept any single uniform  datatype- including other nodes and chains. I would also like that if the field "value" is a node or a chain that the code will automatically create the applicable object. In order to to do this, I am implementing a conditional to check the type of "value" and compare it to the type "gptr" or "schain", since I would like the "value" feild to also accept base types "integer" and "set". Problem is though, when I try to compile, the debugger ignores the type conditional and evaluates the create statements using a base type which is of course incompatible. Is there a way around this?

Code: Pascal  [Select][+][-]
  1. {$m+}
  2. generic datatype<dtype>=class (tpersistent)
  3. type
  4.   datum=specialize datatype<dtype>;
  5. public
  6. var
  7.   value:dtype;
  8.  
  9. procedure valinit;
  10. constructor create; overload;
  11. destructor destroy; override;
  12. end;
  13.  
  14. generic node<dtype > = packed class
  15. type
  16.   datum=specialize datatype<dtype>;
  17.   gptr=specialize node<dtype>;
  18. var
  19.   fwd,bck:gptr;
  20.   data:datum;
  21. public
  22.   constructor create; overload;
  23.   destructor destroy; override;
  24. end;
  25.  
  26. generic chain<gptr,dtype>=packed class
  27. type
  28.   schain=specialize chain<gptr,dtype>;
  29. public
  30. var
  31.   first,last:gptr;
  32.   constructor create; overload;
  33.   destructor destroy; override;
  34. end;
  35.  
  36. procedure datatype.valinit;
  37. type
  38.   gptr=specialize node<dtype>;
  39.   schain=specialize chain<gptr,dtype>;
  40.  
  41. begin
  42.    if (ptypeinfo(typeinfo(self.value))^.kind<>tkset) and
  43.       (ptypeinfo(typeinfo(self.value))^.kind<>tkinteger) then
  44.       begin
  45.         with gettypedata(typeinfo(self.value))^.classtype do
  46.            begin
  47.                if classnameis('gptr') then
  48.                   self.value:=gptr.create             {debugger registers incompatible type defaulting to base type}
  49.                else if classnameis('schain') then
  50.                   self.value:=schain.create;         {debugger registers incompatible type defaulting to base type}  
  51.            end;
  52.       end;
  53. end;
  54.  

First you should check whether the type kind is tkClass and not if it's not a set or integer. Otherwise your code will fail if one uses, e.g. a String.
Then you are not using the correct name as the name of the specialization is neither gptr nor schain, instead its the name of the generic together with its type parameters. You might be better of to check whether ClassType inherits from gptr or schain, though considering that we're talking about generics there is nevertheless the potential that this could fail...

anonymousstranger

  • New Member
  • *
  • Posts: 49
Re: Help with generics
« Reply #6 on: August 04, 2018, 08:52:46 pm »
Thanks everybody! I'm new to OOP and especially generics. I think I understand it conceptually but the devil is in the details, as they say. I'm using Lazarus 1.8.4 with FreePascal 3.0.4 and I'll try to work up a compilable example in a bit. For now...

First you should check whether the type kind is tkClass and not if it's not a set or integer. Otherwise your code will fail if one uses, e.g. a String.
Then you are not using the correct name as the name of the specialization is neither gptr nor schain, instead its the name of the generic together with its type parameters. You might be better of to check whether ClassType inherits from gptr or schain, though considering that we're talking about generics there is nevertheless the potential that this could fail...

I don't know how to check inheritance, could you elaborate? So, based on what I understand, I should do something like this(?):

Code: Pascal  [Select][+][-]
  1. begin
  2.    if  (ptypeinfo(typeinfo(self.value))^.kind=tkclass) then
  3.      if (ptypeinfo(typeinfo(self.value))^.kind<>tkset) and
  4.         (ptypeinfo(typeinfo(self.value))^.kind<>tkinteger) then
  5.         begin
  6.           with gettypedata(typeinfo(self.value))^.classtype do
  7.             begin
  8.                if classnameis('node<dtype>') then
  9.                   self.value:=gptr.create            
  10.                else if classnameis('chain<gptr,dtype>') then
  11.                   self.value:=schain.create;        
  12.             end;
  13.       end;
  14. end;
  15.  

Note that trunk afaik has gettypekind to make such generic discussions on specialization.
Yes,indeed. And in trunk you do not need to include the typinfo unit for that. As opposes to Blaazen's example. But that works in 3.0.4. too, provide unit typinfo IS in the uses clause.

where can I find information on this function (assuming it is a function)?

What compiler version you have? 3.0.4 or trunk?

Also, some compilable demo would be fine.

BTW, you can write:
Code: Pascal  [Select][+][-]
  1. if not (ptypeinfo(typeinfo(self.value))^.kind in [tkset, tkinteger]) then

nice! I'll try that.

I'm not sure if I understood the question correctly but maybe this helps:

Code: Pascal  [Select][+][-]
  1. program Test;
  2. type
  3.   { TTestClass }
  4.  
  5.   TTestClass = class
  6.     constructor Create; virtual;
  7.   end;
  8.  
  9.   { TTestClass2 }
  10.  
  11.   TTestClass2 = class ( TTestClass )
  12.     constructor Create; override;
  13.   end;
  14.   TTestClassType = class of TTestClass;
  15.  
  16. { TTestClass2 }
  17.  
  18. constructor TTestClass2.Create;
  19. begin
  20.   inherited Create;
  21.   WriteLn( 'TTestClass.Create2' );
  22. end;
  23.  
  24. { TTestClass }
  25.  
  26. constructor TTestClass.Create;
  27. begin
  28.   WriteLn( 'TTestClass.Create' );
  29. end;
  30.  
  31. var
  32.   ClassType: TTestClassType;
  33.   obj1, obj2: TTestClass;
  34. begin
  35.   ClassType:= TTestClass;
  36.   obj1:= ClassType.Create;
  37.   ClassType:= TTestClass2;
  38.   obj2:= ClassType.Create;
  39.   obj1.Free;
  40.   obj2.Free;
  41. end.
  42.  

I don't understand what this code does, could you explain?



soerensen3

  • Full Member
  • ***
  • Posts: 213
Re: Help with generics
« Reply #7 on: August 05, 2018, 11:58:07 am »
Quote
I don't understand what this code does, could you explain?
Are you familiar with virtual methods? If not read this: https://www.freepascal.org/docs-html/ref/refsu27.html
You can declare a constructor in the same way. Just have a look at the output of my code sample. There is a variable with the base type where you can store the class type (not an instance of the class). It has the base type of my class but I can assign any descendant type of my class. If I call create it calls the constructor of the descendant class because it overrides it.
I don't know if it helps in your case but you seem to be using only 2 basic class types so in your base classes you could define define a virtual constructor. In you descendants you have to override your constructor. And now your class types should work.
But I'm not sure if this is the problem. Your code there looks a bit inefficient. You have a lot of function calls only to determine if it is a class type. If you want a class that is always created then why not use a record or an object.
Lazarus 1.9 with FPC 3.0.4
Target: Manjaro Linux 64 Bit (4.9.68-1-MANJARO)

anonymousstranger

  • New Member
  • *
  • Posts: 49
Re: Help with generics
« Reply #8 on: August 06, 2018, 06:33:40 am »
Are you familiar with virtual methods? If not read this: https://www.freepascal.org/docs-html/ref/refsu27.html
You can declare a constructor in the same way. Just have a look at the output of my code sample. There is a variable with the base type where you can store the class type (not an instance of the class). It has the base type of my class but I can assign any descendant type of my class. If I call create it calls the constructor of the descendant class because it overrides it.
I don't know if it helps in your case but you seem to be using only 2 basic class types so in your base classes you could define define a virtual constructor. In you descendants you have to override your constructor. And now your class types should work.
But I'm not sure if this is the problem. Your code there looks a bit inefficient. You have a lot of function calls only to determine if it is a class type. If you want a class that is always created then why not use a record or an object.

I am not terribly familiar with virtual methods. My biggest problem right now with OOP is that while I know some of these constructs/subroutines exist, I don't really understand when or why they should be used a lot of times. That being said though, now that I understand (I think) what this does, it could help with another unmentioned problem in my code. I'm using classes because a class is basically a pointer to an object, and I'm using that fact to try to hopefully construct a safer, more efficient linked list. With records and objects I would have to define a pointer seperate of the record or object to point to instances of the record/object. With classes it's built in. Also, the ability to use "self" is something I really want to exploit for better code.

Basically my problem is that the base form of my chains have the following form: chainID.nodeID.data.value  . When "value" is a base type, it functions as you would expect with a linked list. This is complicated by the fact that "node" and "chain" have specializations that require "value" to be either a set, an integer, a node, or a chain.  But if "value" is a chain or node type, I have to access "value" with a (pointer) variable of the relevant class and then create it manually when "value" must be accessed. It would be better to do this at the same time the chain itself is created, so that I don't run the risk of accessing a null pointer (which has plagued my code). However, in order to do that I have to differentiate between a base type and the class types "node" and "chain", so that if value=node or value=chain I can create an initial object set to an initial value. This would also have a cascading effect so that if chain1ID.node1ID.data.value = chain2 then the code will excecute the initial building of Chain2 automatically, as it will if chain2 contains chain3, and so on, finally terminating with chainN in which chainNID.NodeNID.data.value= base type.
« Last Edit: August 06, 2018, 06:46:28 am by anonymousstranger »

egsuh

  • Hero Member
  • *****
  • Posts: 1273
Re: Help with generics
« Reply #9 on: August 06, 2018, 08:18:46 am »
I'm not sure I exactly understand you, but it seems that you want to do something like:


Code: Pascal  [Select][+][-]
  1. procedure DoThis(StartN, CurrentN: Node);
  2. begin
  3.     case CurrentN.ValueType of
  4.          Node : begin
  5.              CurrentN := GetNodeFromValue;
  6.              DoThis(CurrentN, CurrentN);
  7.         end;
  8.         GenuineValue: begin
  9.               DoSomethingWithValue;
  10.               CurrentN := GetNextNode;
  11.               if CurrentN = StartN
  12.                  then Exit
  13.                  else DoThis(StartN, CurrentN);
  14.         end;
  15.    end;
  16. end;
  17.          
  18.  
  19. begin
  20.     N0 := FirstNode;
  21.     DoThis(N0, N0);
  22. end;        
  23.              

If this is the case, I'm not sure generics are applicable here. Using polymorthism of objects would be better.  For example,

Code: Pascal  [Select][+][-]
  1. type
  2.  
  3. TNode = class
  4.      PrevNode, NextNode: TNode;  
  5.      function DoSomethingWithValue: TNode; virtual; abstract;
  6. end;
  7.  
  8. TIntegerNode = class(TNode)
  9.      Value: integer;
  10.      constructor Create(i: integer);
  11.      function DoSomethingWithValue: TNode; override;
  12. end;
  13.  
  14.    // TStringNode, TSetNode, etc. in the same way
  15.  
  16. TNodeNode = class(TNode)
  17.       Value: TNode;
  18.       constructor Create(n: TNode=nil);
  19.       function DoSomethingWithValue:TNode; override;
  20. end;
  21.  
  22. implementation
  23.  
  24. constructor TIntegerNode.Create(i:integer);
  25. begin
  26.      Value := i;
  27. end;
  28.  
  29. function TIntegerNode.DoSomethingWithValue: TNode;
  30. begin
  31.       ShowMessage(IntToStr(Data));
  32.       Result := NextNode;
  33. end;
  34.  
  35. constructor TNodeNode.Create(n:TNode);
  36. begin
  37.      Value := n;
  38. end;
  39.  
  40. function TNodeNode.DoSomethingWithValue:TNode;
  41. begin
  42.       Result := Value;
  43. end;
  44.  
  45.  

with this setup, you can write:

Code: Pascal  [Select][+][-]
  1. procedure DoThis(StartN, CurrentN: TNode);
  2. begin
  3.      if CurrentN is TNodeNode then begin
  4.              CurrentN := CurrentN.DoSomethingWithValue;
  5.              DoThis(CurrentN, CurrentN);
  6.       end
  7.       else begin
  8.               CurrentN := CurrentN.DoSomethingWithValue;
  9.               if CurrentN = StartN  
  10.                  then Exit
  11.                  else DoThis(StartN, CurrentN);
  12.       end;
  13. end;
  14.  
  15. begin
  16.      DoThis(ChainStartNode);
  17. end;
  18.  

The key of polymorphism if in the "virtual" and "override" of class methods. You don't have to know the type of descendant object.

Sorry that I haven't tested this algorithm. As I have to create chains first, and Dothis is a recursion, I can't test this.  Or I might have written a really stupid post :D...  Sorry if this is the case.
               

anonymousstranger

  • New Member
  • *
  • Posts: 49
Re: Help with generics
« Reply #10 on: August 09, 2018, 08:21:06 pm »

Quote
...I'm not sure generics are applicable here. Using polymorthism of objects would be better.  For example,

Code: Pascal  [Select][+][-]
  1. type
  2.  
  3. TNode = class
  4.      PrevNode, NextNode: TNode;  
  5.      function DoSomethingWithValue: TNode; virtual; abstract;
  6. end;
  7.  
  8. TIntegerNode = class(TNode)
  9.      Value: integer;
  10.      constructor Create(i: integer);
  11.      function DoSomethingWithValue: TNode; override;
  12. end;
  13.  
  14.    // TStringNode, TSetNode, etc. in the same way
  15.  
  16. TNodeNode = class(TNode)
  17.       Value: TNode;
  18.       constructor Create(n: TNode=nil);
  19.       function DoSomethingWithValue:TNode; override;
  20. end;
  21.  
  22. implementation
  23.  
  24. constructor TIntegerNode.Create(i:integer);
  25. begin
  26.      Value := i;
  27. end;
  28.  
  29. function TIntegerNode.DoSomethingWithValue: TNode;
  30. begin
  31.       ShowMessage(IntToStr(Data));
  32.       Result := NextNode;
  33. end;
  34.  
  35. constructor TNodeNode.Create(n:TNode);
  36. begin
  37.      Value := n;
  38. end;
  39.  
  40. function TNodeNode.DoSomethingWithValue:TNode;
  41. begin
  42.       Result := Value;
  43. end;
  44.  
  45.  

with this setup, you can write:

Code: Pascal  [Select][+][-]
  1. procedure DoThis(StartN, CurrentN: TNode);
  2. begin
  3.      if CurrentN is TNodeNode then begin
  4.              CurrentN := CurrentN.DoSomethingWithValue;
  5.              DoThis(CurrentN, CurrentN);
  6.       end
  7.       else begin
  8.               CurrentN := CurrentN.DoSomethingWithValue;
  9.               if CurrentN = StartN  
  10.                  then Exit
  11.                  else DoThis(StartN, CurrentN);
  12.       end;
  13. end;
  14.  
  15. begin
  16.      DoThis(ChainStartNode);
  17. end;
  18.  

The key of polymorphism if in the "virtual" and "override" of class methods. You don't have to know the type of descendant object.

Sorry that I haven't tested this algorithm. As I have to create chains first, and Do this is a recursion, I can't test this.  Or I might have written a really stupid post :D...  Sorry if this is the case.
               

Sorry for the late reply. This is an incredibly busy part of the year for me. What you demonstrated is pretty close to what I'm doing, the key difference being that the nodes for the linked list are contained in another class called "chain" which also holds pointers that reference the first and last node(s). I guess a big reason (perhaps the biggest) I am using generics is because I am wanting to decouple the linked-list processes from those processes which directly transform the information contained by the linked list. I am however very interested in what you did here. Not a lot is said in the documentation about the override, overload, virtual, and abstract keywords. Could I bother you to explain them (ie what they do, what they are for, etc.) and their relationship to class polymorphism in some detail? I would be incredibly grateful.  8)

anonymousstranger

  • New Member
  • *
  • Posts: 49
Re: Help with generics
« Reply #11 on: August 11, 2018, 10:11:22 pm »
Alternately, I would be interested in some good source material on the subject.

anonymousstranger

  • New Member
  • *
  • Posts: 49
Re: Help with generics
« Reply #12 on: August 13, 2018, 01:20:16 am »
a question:

1) Why is did you tag "dosomethingwithvalue" in "TNode" both virtual and abstract? from what I have gathered thusfar, virtual means it can have an implementation and may be overridden, while abstract means that it cannot have an implementation and MUST be overridden. Furthermore, the class becomes abstract and cannot be instantiated- only its decedents. Seems like abstract is more imperative than virtual, making the virtual tag unnecessary. Is there a nuance that I missed?

egsuh

  • Hero Member
  • *****
  • Posts: 1273
Re: Help with generics
« Reply #13 on: August 13, 2018, 07:52:54 am »
If you do are not familiar with object oriented programming(OOP), it may not be easy to fully understand the concepts. Anyway,

In class definition, there are inheritance.  In my example,

Code: Pascal  [Select][+][-]
  1. TNode = class
  2.      procedure DoSomethingWithValue; virtual; abstract;
  3. end;
  4.  
  5. TIntegerNode = class(TNode)
  6.      procedure DoSomethingWithValue; override;
  7. end;  
  8.  
  9. TStringNode = class(TNode)
  10.      procedure DoSomethingWithValue; override;
  11. end;  
  12.  

Here, TNode is parent class of TIntegerNode and TStringNode, and TIntegerNode and TStringNode are descendant (children) classes of TNode.

The key in OOP is you can assign like:

   parent := child;   (not vice versa).

So, you can write
Code: Pascal  [Select][+][-]
  1. var
  2.     ANode: TNode;
  3.     AnIntegerNode: TIntegerNode;
  4. begin
  5.     ANode := TIntegerNode.Create;
   

And in my example, ANode.DoSomethingWithValue (DSV in later) will run TIntegerNode.DSV, because TNode.DSV is defined as 'virtual' and TIntegerNode.DSV "override" it. If there are no virtual and override, ANode.DSV will run TNode.DSV even though it is created with ANode := TIntegerNode.Create because ANode is a variable with TNode anyway.

"abstract" is to denote that this method (function or procedure) itself does not have implementation. Only descendants' methods have physical? implementation. 


Practically polymorphism means nothing but the same function / procedure name can do different things. But this is quite cool, if you have well-designed inheritance hierarchy.

Sources:

https://castle-engine.io/modern_pascal_introduction.html
http://www.webtechcorp.co.uk/web-developer-training-delphi-article-oop.htm
http://www.cd.inf.br/prog/oop/oop%20-%203%20parte.pdf

anonymousstranger

  • New Member
  • *
  • Posts: 49
Re: Help with generics
« Reply #14 on: August 16, 2018, 07:02:32 am »
back to my original question for a moment, here's a compilable example:
Code: Pascal  [Select][+][-]
  1. program example;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes,typinfo
  7.   { you can add units after this };
  8.  
  9. type
  10.  
  11. {$m+}
  12. generic datatype<dtype> = class (tpersistent)
  13. public
  14.   var
  15.     value:dtype;
  16.   procedure valinit;
  17.   constructor create; overload;
  18.   destructor destroy; override;
  19. end;
  20.  
  21. generic node<dtype> = packed class
  22.   type
  23.     datum=specialize datatype<dtype>;
  24.     gptr=specialize node<dtype>;
  25.   var
  26.     fwd:gptr;
  27.     bck:gptr;
  28.     data:datum;
  29. public
  30.     constructor create; overload;
  31.     destructor destroy; override;
  32. end;
  33.  
  34. generic chain<gptr, dtype> = packed class
  35. public
  36.     var
  37.      first:gptr;
  38.      last:gptr;
  39.   constructor create; overload;
  40.   destructor destroy; override;
  41. end;
  42.  
  43. ennexample=(a,b,c,d);
  44. setexample=set of ennexample;
  45.  
  46. cardnode=specialize node<cardinal>;
  47. cardchain=specialize chain<cardnode,cardinal>;
  48.  
  49. setnode=specialize node<setexample>;
  50. setchain=specialize chain<setnode,setexample>;
  51.  
  52. nodeCschain=specialize node<setchain>;
  53. chainCnodeCschain=specialize chain<nodeCschain,setchain>;
  54.  
  55. {**************************Datatype*********************************************}
  56.  
  57. procedure datatype.valinit;
  58. type
  59.   gptr=specialize node<dtype>;
  60.   schain=specialize chain<gptr,dtype>;
  61. var
  62.   nvar:gptr;
  63.   svar:schain;
  64.  
  65. begin
  66.   if (ptypeinfo(typeinfo(self.value))^.kind = tkset) then
  67.     begin
  68.       writeln('set');
  69.       {self.value:=[];}
  70.     end
  71.   else if (ptypeinfo(typeinfo(self.value))^.kind = tkinteger) then
  72.     begin
  73.       writeln('cardinal');
  74.       {self.value:=0;}
  75.     end
  76.   else if (ptypeinfo(typeinfo(self.value))^.kind = tkclass) then
  77.     begin
  78.       if (gettypedata(typeinfo(self.value))^.ClassType.ClassName =
  79.           gettypedata(typeinfo(svar.first.data.value))^.ClassType.ClassName) then
  80.         begin
  81.           writeln(gettypedata(typeinfo(self.value))^.ClassType.ClassName);
  82.           {self.value:=schain.create;}                                           {create value as chain}
  83.         end
  84.       else if (gettypedata(typeinfo(self.value))^.ClassType.ClassName =
  85.                gettypedata(typeinfo(nvar.data.value))^.ClassType.ClassName) then
  86.         begin
  87.           writeln(gettypedata(typeinfo(self.value))^.ClassType.ClassName);
  88.           {self.value:=gptr.create;}                                             {create value as node}
  89.         end;
  90.     end;
  91. end;
  92.  
  93. constructor datatype.create; overload;                          {create datatype}
  94.  
  95.  begin
  96.   inherited;                                                                   {inhereted from root}
  97.   self.valinit;
  98.  end;
  99.  
  100. destructor datatype.destroy;                                   {destroy datatype}
  101.  
  102.   begin
  103.     inherited;                                                                 {inhereted from root}
  104.   end;
  105.  
  106. {**************************Node*************************************************}
  107.  
  108. constructor node.create; overload;                                  {create node}
  109.  
  110.   begin
  111.     inherited;                                                                 {inhereted from root}
  112.     data:=datum.create;                                                        {create data}
  113.   end;
  114.  
  115. destructor node.destroy;                                           {destroy node}
  116.  
  117.   begin
  118.     inherited;                                                                 {inhereted from root}
  119.   end;
  120.  
  121. {**************************Chain************************************************}
  122.  
  123. constructor chain.create; overload;                  {creates instances of chain}
  124.  
  125.   begin
  126.     inherited;                                                                 {inhereted from root}
  127.     self.first:=gptr.create;
  128.     self.last:=self.first;
  129.   end;
  130.  
  131. destructor chain.destroy;                           {releases instances of chain}
  132.  
  133.   begin
  134.     inherited;                                                                 {inhereted from root}
  135.   end;
  136.  
  137. {**************************Variables********************************************}
  138.  
  139. var
  140.   cnode:cardnode;
  141.   snode:setnode;
  142.   nCchain:nodeCschain;
  143.   cCnodeCchain:chainCnodeCschain;
  144.  
  145. {**************************Main*************************************************}
  146.  
  147. begin
  148.  cnode:=cardnode.create;
  149.  snode:=setnode.create;
  150.  nCchain:=nodeCschain.create;
  151.  cCnodeCchain:=chainCnodeCschain.create;
  152.  readln;
  153. end.
  154.  

if you run with the create/assignment statements in the procedure "valinit" commented out, you will notice that the conditionals work perfectly. However, if you uncomment those create statements, you will see that the compiler refuses to compile, complaining about type mismatches when otherwise in runtime the type mismatches it is complaining about will never happen. The compiler seems to be ignoring logic. Is there a way to reassure the compiler that what it thinks is a problem is not really a problem?
« Last Edit: August 16, 2018, 07:13:19 am by anonymousstranger »

 

TinyPortal © 2005-2018