Recent

Author Topic: <Solved> Problem: class; virtual method; runtime Exception "External: SIGSEGV"  (Read 1033 times)

oog

  • Newbie
  • Posts: 5
I am implementing a simple microprocessor as a class and a computer as a derived (child) class of microprocessor.

The computer implements memory like ram/rom that should be read by the microprocessor, but I can't get it to work.

This simplified program shows the problem. I think I'm handling virtual methods in a wrong way.

Problem 1:
If the method readmem() is not virtual, i get the wrong memory content.

Problem 2:
If I declare the method readmem() as virtual / override, I get a runtime Exception "External: SIGSEGV".


The simple test program uses a form with Memo1, Butto1 und Button2.

This is the result pushing Button1 and Button2 without virtual methodes:

Memo1:
Expect same result for mem and cpu.
Processor: mem=0 cpu=0
Computer: mem=1 cpu=0

I expect this: Computer: mem=1 cpu=1



Here is the code of the complete unit:

Code: Pascal  [Select][+][-]
  1. unit unit7;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm7 }
  13.  
  14.   TForm7 = class(TForm)
  15.     Button1: TButton;
  16.     Button2: TButton;
  17.     Memo1: TMemo;
  18.     procedure Button1Click(Sender: TObject);
  19.     procedure Button2Click(Sender: TObject);
  20.     procedure FormCreate(Sender: TObject);
  21.   private
  22.  
  23.   public
  24.  
  25.   end;
  26.  
  27.  
  28.  
  29. // class Processor (base class)
  30.  
  31.  
  32. type
  33. TProcessor = class
  34. private
  35. public
  36.   constructor init;
  37.   //destructor kill;
  38.   function readmem(): integer;  virtual;
  39.   function runcpu: integer;
  40. end;
  41.  
  42.  
  43. // class Computer (child of processor)
  44.  
  45.  
  46. type
  47. TComputer = class(TProcessor)
  48. private
  49. public
  50.   constructor init;
  51.   //destructor kill;
  52.   function readmem(): integer;  override;
  53. end;
  54.  
  55.  
  56. var
  57.   Form7: TForm7;
  58.  
  59.   Processor: TProcessor;
  60.   Computer: TComputer;
  61.  
  62. implementation
  63.  
  64. {$R *.lfm}
  65.  
  66. { TForm7 }
  67.  
  68.  
  69. procedure TForm7.Button1Click(Sender: TObject);
  70. var
  71.   m,c: integer;
  72. begin
  73.   m:=Processor.readmem(); // this is the content of ram
  74.   c:=Processor.runcpu;    // this is read by the cpu (should be the same)
  75.   Memo1.Lines.Add('Processor:'
  76.     +' mem='+IntToStr(m)
  77.     +' cpu='+IntToStr(c)
  78.   );
  79. end;
  80.  
  81.  
  82. procedure TForm7.Button2Click(Sender: TObject);
  83. var
  84.   m,c: integer;
  85. begin
  86.   m:=Computer.readmem(); // this is the content of ram
  87.   c:=Computer.runcpu;    // this is read by the cpu (should be the same)
  88.   Memo1.Lines.Add('Computer:'
  89.     +' mem='+IntToStr(m)
  90.     +' cpu='+IntToStr(c)
  91.     );
  92. end;
  93.  
  94.  
  95. procedure TForm7.FormCreate(Sender: TObject);
  96. begin
  97.   Memo1.Clear;
  98.   Memo1.Lines.Add('Expect same result for mem and cpu.');
  99.   Processor.init;
  100.   Computer.init;
  101. end;
  102.  
  103.  
  104.  
  105. //////////////////////////////////////
  106. //
  107. // object Processor
  108. //
  109. //////////////////////////////////////
  110.  
  111.  
  112.  
  113. constructor TProcessor.init;
  114. begin
  115. end;
  116.  
  117.  
  118. //destructor TProcessor.kill;
  119. //begin
  120. //end;
  121.  
  122.  
  123. function TProcessor.readmem(): integer;
  124. begin
  125.   readmem:=0;
  126. end;
  127.  
  128.  
  129. function TProcessor.runcpu: integer;
  130. begin
  131.   runcpu:=readmem();
  132. end;
  133.  
  134.  
  135.  
  136. //////////////////////////////////////
  137. //
  138. // object Computer
  139. //
  140. //////////////////////////////////////
  141.  
  142.  
  143.  
  144. constructor TComputer.init;
  145. begin
  146.   inherited;
  147.   TProcessor.init;
  148. end;
  149.  
  150.  
  151. //destructor TComputer.kill;
  152. //begin
  153. //end;
  154.  
  155.  
  156. function TComputer.readmem(): integer;
  157. begin
  158.   readmem:=1;
  159. end;
  160.  
  161. end.
  162.  
  163.  

« Last Edit: June 10, 2024, 03:05:31 pm by oog »

Eugene Loza

  • Hero Member
  • *****
  • Posts: 729
    • My games in Pascal
Note that you are using obsolete OOP from Turbo Pascal. Note that class is not the same thing as object.

Classes need to operate on "instances". I.e. a specific self-contained object which in reality is a reference to memory volume (data) and methods on how to operate it.

In other words:

Code: Pascal  [Select][+][-]
  1. type TMyClass = class
  2.   public A; virtual;
  3. end;
  4.  
  5. var
  6.   MyClass: TMyClass;
  7. begin
  8.   MyClass.Init;
  9.   MyClass.A;
  10. end;

Is not correct. A is a method that needs to operate on an "instance", and therefore calling it on class "type" may and will result in unexected behavior. The fact that A is virtual makes things only worse - as virtual methods not just operate on some abstract memory volume, but operate on a specific instance of a class which you don't provide.

So, what happens above is MyClass (in your case Processor) variable is nil. Nil is a reference to "nowhere", meaning that the instance doesn't exist. Some methods may survive being called on a nil instance. But virtual methods that must know about what instance they are called on (to know what exact child of the method must be called) cannot do that and hence the error.

Again, the confusion comes from old objects which were rather like (advanced) records, i.e. were automatically allocated on heap memory and therefore didn't need any additional memory management to make sure the variable is pointing to a valid instance of a class.

So, the new way of handling objects is this:

Code: Pascal  [Select][+][-]
  1. type TMyClass = class
  2.   public A; virtual;
  3. end;
  4.  
  5. var
  6.   MyClass: TMyClass; // this is a reference to an instance of the class
  7. begin
  8.   MyClass := TMyClass.Create; // create an instance of the class and assign it to the variable pointing to the instance of the class
  9.   MyClass.A; // call method of this specific instance;
  10.   MyClass.Free; // finally release memory occupied by the class
  11. end;

I must admit that a few years ago I myself had been struggling with this specific problem :D And this short book helped me greatly to switch from Turbo Pascal to modern Pascal way of thinking https://castle-engine.io/modern_pascal
My FOSS games in FreePascal&CastleGameEngine: https://decoherence.itch.io/ (Sources: https://gitlab.com/EugeneLoza)

cdbc

  • Hero Member
  • *****
  • Posts: 1646
    • http://www.cdbc.dk
Hi
@Eugene Loza beat me to it...
His response is more thorough than mine... but I'll leave it, anyway...
First off, creation goes like this:
Code: Pascal  [Select][+][-]
  1. procedure TForm7.FormCreate(Sender: TObject);
  2. begin
  3.   Memo1.Clear;
  4.   Memo1.Lines.Add('Expect same result for mem and cpu.');
  5.   Processor:= TProcessor.init; // we usually call 'init' for 'Create'
  6.   Computer:= TComputer.init; // we usually call 'init' for 'Create'
  7. end;
...and you HAVE to Free / Destroy them again, when done, like this:
Code: Pascal  [Select][+][-]
  1. procedure TForm7.FormDestroy(Sender: TObject);
  2. begin
  3.   Processor.Free; // Free calls 'Destroy' internally,
  4.   Computer.Free; // after checking for NIL
  5. end;
As shown, the 'OnDestroy' event-handler is good for that
Your AV came from not creating your objects before use.
HTH
Regards Benny
« Last Edit: June 10, 2024, 12:57:07 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

oog

  • Newbie
  • Posts: 5
Thank you very much for this explanation.

This looks very helpful.
 :D :D :D

oog

  • Newbie
  • Posts: 5

Now it works, thank you all. :D

The result by pushing Button1 and Button2 is now, what I expected:

Memo1:
Expect same result for mem and cpu.
Processor: mem=0 cpu=0   (by puhing Button1)
Computer: mem=1 cpu=1    (by puhing Button2)


This small example program compiles and runs well as it should. Constructor and destructor are in the comments. The program uses a Form with Button1, Button2 and Memo1 Here is the code:

Code: Pascal  [Select][+][-]
  1. unit unit7;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm7 }
  13.  
  14.   TForm7 = class(TForm)
  15.     Button1: TButton;
  16.     Button2: TButton;
  17.     Memo1: TMemo;
  18.     procedure Button1Click(Sender: TObject);
  19.     procedure Button2Click(Sender: TObject);
  20.     procedure FormCreate(Sender: TObject);
  21.     procedure FormDestroy(Sender: TObject);
  22.   private
  23.  
  24.   public
  25.  
  26.   end;
  27.  
  28.  
  29.  
  30. // class Processor (base class)
  31.  
  32.  
  33. type
  34. TProcessor = class
  35. private
  36. public
  37.   //constructor init;
  38.   //destructor free;
  39.   function readmem(): integer; virtual;
  40.   function runcpu: integer;
  41. end;
  42.  
  43.  
  44. // class Computer (child of processor)
  45.  
  46.  
  47. type
  48. TComputer = class(TProcessor)
  49. private
  50. public
  51.   function readmem(): integer; override;
  52. end;
  53.  
  54.  
  55.  
  56. var
  57.   Form7: TForm7;
  58.  
  59.   Processor: TProcessor;
  60.   Computer: TComputer;
  61.  
  62.  
  63. implementation
  64.  
  65. {$R *.lfm}
  66.  
  67. { TForm7 }
  68.  
  69.  
  70. procedure TForm7.Button1Click(Sender: TObject);
  71. var
  72.   m,c: integer;
  73. begin
  74.   m:=Processor.readmem(); // this is the content of ram
  75.   c:=Processor.runcpu;    // this is read by the cpu (should be the same)
  76.   Memo1.Lines.Add('Processor:'
  77.     +' mem='+IntToStr(m)
  78.     +' cpu='+IntToStr(c)
  79.   );
  80. end;
  81.  
  82.  
  83. procedure TForm7.Button2Click(Sender: TObject);
  84. var
  85.   m,c: integer;
  86. begin
  87.   m:=Computer.readmem(); // this is the content of ram
  88.   c:=Computer.runcpu;    // this is read by the cpu (should be the same)
  89.   Memo1.Lines.Add('Computer:'
  90.     +' mem='+IntToStr(m)
  91.     +' cpu='+IntToStr(c)
  92.     );
  93. end;
  94.  
  95.  
  96. procedure TForm7.FormCreate(Sender: TObject);
  97. begin
  98.   Memo1.Clear;
  99.   Memo1.Lines.Add('Expect same result for mem and cpu.');
  100.  
  101.   Processor:=TProcessor.Create;
  102.   Computer:=TComputer.Create;
  103.  
  104.   //Processor.init;
  105.   //Computer.init;
  106. end;
  107.  
  108. procedure TForm7.FormDestroy(Sender: TObject);
  109. begin
  110.   //Processor.Free;
  111.   //Computer.Free;
  112. end;
  113.  
  114.  
  115.  
  116. //////////////////////////////////////
  117. //
  118. // class Processor
  119. //
  120. //////////////////////////////////////
  121.  
  122.  
  123.  
  124. //constructor TProcessor.init;
  125. //begin
  126. //end;
  127. //
  128. //
  129. //destructor TProcessor.free;
  130. //begin
  131. //end;
  132.  
  133.  
  134. function TProcessor.readmem(): integer;
  135. begin
  136.   readmem:=0;
  137. end;
  138.  
  139.  
  140. function TProcessor.runcpu: integer;
  141. begin
  142.   runcpu:=readmem();
  143. end;
  144.  
  145.  
  146.  
  147. //////////////////////////////////////
  148. //
  149. // class Computer
  150. //
  151. //////////////////////////////////////
  152.  
  153.  
  154.  
  155. //constructor TComputer.init;
  156. //begin
  157. //end;
  158. //
  159. //
  160. //destructor TComputer.free;
  161. //begin
  162. //end;
  163.  
  164.  
  165. function TComputer.readmem(): integer;
  166. begin
  167.   readmem:=1;
  168. end;
  169.  
  170. end.
  171.  
  172.  

Thaddy

  • Hero Member
  • *****
  • Posts: 16152
  • Censorship about opinions does not belong here.
You could have used your old code too, but that also contained many syntax errors for that type of code.
Just to show you an example how that old style coding should have been done.
It would have worked in your modern gui code.
I do NOT recommend it, I recommend the above approach, but here is what you meant to write, old school, using objects:
Code: Pascal  [Select][+][-]
  1. program tpstyle;
  2. {$mode objfpc}
  3. type
  4.   { object Processor (base object) }
  5.   PProcessor = ^TProcessor;
  6.   TProcessor = object
  7.   private
  8.   public
  9.     constructor init;
  10.     destructor done;
  11.     function readmem(): integer;  virtual;
  12.     function runcpu: integer;
  13.   end;
  14.  
  15. { object Computer (child of processor) }  
  16.   PComputer = ^TComputer;
  17.   TComputer = object(TProcessor)
  18.   private
  19.   public
  20.     constructor init;
  21.     destructor done;
  22.     { objects do not know override }
  23.     function readmem(): integer;virtual;
  24.   end;
  25.  
  26. { object Processor }
  27. constructor TProcessor.init;
  28. begin
  29.   writeln('processor init');
  30. end;
  31.  
  32.  
  33. destructor TProcessor.done;
  34. begin
  35.   writeln('processor done');
  36. end;
  37.  
  38. function TProcessor.readmem(): integer;
  39. begin
  40.   readmem:=0;
  41. end;
  42.  
  43. function TProcessor.runcpu: integer;
  44. begin
  45.   runcpu:=readmem();
  46. end;
  47.  
  48. { object Computer }
  49. constructor TComputer.init;
  50. begin
  51.   inherited init;
  52.   writeln('computer init');
  53. end;
  54.  
  55. destructor TComputer.done;
  56. begin
  57.   writeln('computer done');
  58.   inherited done;
  59. end;
  60.  
  61. function TComputer.readmem(): integer;
  62. begin
  63.   readmem:=1;
  64. end;
  65.  
  66. var
  67. { two ways of use }
  68.  a:TProcessor;
  69.  b:TComputer;
  70.  c:PProcessor;
  71.  d:PComputer;
  72. begin
  73.  writeln('As object instances, note done is illegal here');
  74.  a.init;
  75.  b.init;
  76.  writeln('As pointer to object instances');
  77.  writeln('New');
  78.  New(c,init);
  79.  New(d,Init);
  80.  writeln('dispose');
  81.  Dispose(c,Done);
  82.  Dispose(d,done);
  83. end.
« Last Edit: June 10, 2024, 09:39:45 pm by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

 

TinyPortal © 2005-2018