Inheritance

Public support forum for peer to peer support with related to the Visual Objects and Vulcan.NET products
Post Reply
ThomasWYale
Posts: 43
Joined: Thu Jun 20, 2019 5:19 pm

Inheritance

Post by ThomasWYale »

In my system, there are the following classes:

CLASS Frame
CLASS FE
CLASS FrameEx INHERIT Frame
CLASS FEEx INHERIT FE (which XSharp VOXPorter converted to PARTIAL CLASS FEEx INHERIT FE)

Each of these four classes has it's own init method (or rather CONSTRUCTOR method). I anticipated that whatever class is instantiated, it would run that CONSTRUCTOR code. But when I step through through the debugger where an instantiation of FrameEx occurs, and look at the call stack, it appears to be executing the CONSTRUCTOR for FrameEx twice, despite that at the top of the call stack, it's actually executing the CONSTRUCTOR for the inherited class Frame instead (please see screenshot below).
inheritance_problem.jpg
inheritance_problem.jpg (124.11 KiB) Viewed 451 times
).

Why would the CONSTRUCTOR Frame execute instead of the one for FrameEx? Is it possible that while X# is displaying "FrameEx", X# is ignoring the "Ex" in "FrameEx" and just executing the one with the inherited class with the shortest name? Should the inheriting classes be given a more distinctive name to prevent this from happening?
User avatar
robert
Posts: 4225
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Inheritance

Post by robert »

Thomas,
Can you share the code?
Your expectations are correct.
I can't read the screen shots. Are the "Ex" constructors calling the SUPER() constructors ?

Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
Chris
Posts: 4562
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Inheritance

Post by Chris »

Hi Thomas,

It's the correct constructor that it's being called every time, as you can see the line specified in the 3rd column is different in the two lines.

The "class" specified in the first column is the actual class name of the active SELF object, which is indeed the name of the last class in the inheritance, not the name of the class where the debugger is pointing at right now in the editor. It is unfortunately a bit complicated to make it show the class name in the inheritance tree, but will give it a try.

Is there some other problem that you were trying to debug and found this?

.
Chris Pyrgas

XSharp Development Team test
chris(at)xsharp.eu
ThomasWYale
Posts: 43
Joined: Thu Jun 20, 2019 5:19 pm

Inheritance

Post by ThomasWYale »

Yes, here it is. Another class instantiates a FrameEx object:

Code: Select all

AAdd(oExSyntaxRef:aFrameEx,(oFrameEx:=FrameEx{nVerbLoc,oVerb,TRUE}))
These are the class definitions of Frame and FrameEx:

Code: Select all

CLASS FrameEx INHERIT Frame
EXPORT lPositive AS LOGIC
EXPORT aFEsLiteral AS ARRAY
EXPORT aFEsObj AS ARRAY
EXPORT aRelations AS ARRAY
EXPORT aRelatedFrames AS ARRAY
EXPORT nVerbRef AS BYTE
EXPORT nScore AS BYTE
EXPORT aEmbeddingFEs AS ARRAY
EXPORT lFEsConform AS LOGIC
EXPORT aUnassignedObj AS ARRAY
EXPORT lViaVN AS LOGIC

CLASS Frame
EXPORT nRec AS LONG
EXPORT ccName AS STRING
EXPORT cDef AS STRING
EXPORT aFEs AS ARRAY
EXPORT aGramObjs AS ARRAY
EXPORT cRelation AS STRING
EXPORT aEmbedded AS ARRAY
And the CONSTRUCTORs:

Code: Select all

CONSTRUCTOR(lInitialize) // for Frame
LOCAL y AS BYTE
LOCAL cRec AS STRING
LOCAL aSFE[0],aOFE[0],aOtherFE[0] AS ARRAY
LOCAL oFE AS FE
SELF:aGramObjs:={}
SELF:cRelation:=""
IF lInitialize
 SELF:nRec:=0
 SELF:ccName:=""
 SELF:cDef:=""
 SELF:aFEs:={}
ELSE
 SELF:nRec:=RecNo()
 SELF:ccName:=Trim(cName)
 SELF:cDef:=Def
 SELF:aFEs:={}
 cRec:=base16(RecNo(),2)
 DbSelectArea("FE")
 DbSeek(cRec)
 AAdd(SELF:aFEs,FE{TRUE})
 WHILE cRec==nFrame
  oFE:=FE{FALSE}
  IF oFE:nGramSlot=0
   AAdd(aSFE,oFE)
  ELSEIF oFE:nGramSlot=1
   AAdd(aOFE,oFE)
  ELSE
   AAdd(aOtherFE,oFE)
  ENDIF
  DbSkip()
 ENDDO
 FOR y:=1 UPTO ALen(aSFE)
  AAdd(SELF:aFEs,aSFE[y])
 NEXT
 FOR y:=1 UPTO ALen(aOFE)
  AAdd(SELF:aFEs,aOFE[y])
 NEXT
 FOR y:=1 UPTO ALen(aOtherFE)
  AAdd(SELF:aFEs,aOtherFE[y])
 NEXT
 SELF:aEmbedded:=ArrayNew(ALen(SELF:aFEs))
 AFill(SELF:aEmbedded,FALSE)
ENDIF
RETURN SELF

CONSTRUCTOR(nVerbRef,oWord,lViaVN) // for FrameEx
LOCAL nFE1Loc,nFE2Loc AS BYTE
LOCAL nFE1Rec,nFE2Rec AS LONG 
LOCAL cRec AS STRING
LOCAL cElements AS STRING
SELF:nRec:=RecNo()
SELF:ccName:=Trim(cName)
IF SELF:ccName=="Change_position_on_a_scale"
 nFE1Loc:=1
ENDIF
SELF:cDef:=Def
SELF:aFEs:={}
cRec:=base16(RecNo(),2)
cElements:="FrameEx:init: "+NTrim(RecNo())+" = "+cRec+CRLF
cElements+=SELF:ccName+CRLF
DbSelectArea("FE")
DbSeek(cRec)
cElements+="In FE.dbf:"+CRLF+NTrim(RecNo())+CRLF
WHILE cRec==nFrame
 cElements+=NTrim(RecNo())+CRLF
 DbSkip()
ENDDO
DbSelectArea("FE")
DbSeek(cRec)
AAdd(SELF:aFEs,FEEx{TRUE})
WHILE cRec==nFrame
 AAdd(SELF:aFEs,FEEx{FALSE})
 DbSkip()
ENDDO
SELF:aEmbedded:=ArrayNew(ALen(SELF:aFEs))
AFill(SELF:aEmbedded,FALSE)
DbSelectArea("FEFESAFR")
DbSeek(cRec)
WHILE Frme==cRec
 IF RelateType="1"
  nFE1Rec:=base10(FE1)
  nFE1Loc:=AScan(SELF:aFEs,{|x|x:nCNameRec=nFE1Rec})
  nFE2Rec:=base10(FE2)
  nFE2Loc:=AScan(SELF:aFEs,{|x|x:nCNameRec=nFE2Rec})
  IF SELF:aFEs[nFE2Loc]:aSubFEs==NULL_ARRAY 
   SELF:aFEs[nFE2Loc]:aSubFEs:={}
  ENDIF
  AAdd(SELF:aFEs[nFE2Loc]:aSubFEs,SELF:aFEs[nFE1Loc])
  SELF:aEmbedded[nFE1Loc]:=TRUE
 ENDIF
 DbSkip()
ENDDO
SELF:aGramObjs:={}
SELF:cRelation:=""
SELF:lPositive:=TRUE
IF!IsNil(oWord)
 SELF:aFEsLiteral:={oWord:cLiteral}
 SELF:aFEsObj:={oWord}
ENDIF
SELF:aRelations:={}
SELF:aRelatedFrames:={}
SELF:nVerbRef:=nVerbRef
SELF:nScore:=0
SELF:aEmbeddingFEs:={}
SELF:lFEsConform:=TRUE
SELF:aUnassignedObj:={}
SELF:lViaVN:=lViaVN
RETURN SELF
In VO, only one of these inits is executed, depending on the CLASS. In X#, according to the callstack, both CONSTRUCTORs are executed for some reason. In that case, to answer your question, yes, instantiating FrameEx calls the SUPER() CONSTRUCTOR, which would be Frame:init(), which in X# would be Frame.ctor().

I circumvented the problem by recoding Frame and FrameEx as two separate CLASSes, as well as encapsulating CLASSes FE and FEex, with no inheritance, and it works fine. I was a little concerned that future developers, however, may require inherited or inheriting CLASSes and face the same problem.
ThomasWYale
Posts: 43
Joined: Thu Jun 20, 2019 5:19 pm

Inheritance

Post by ThomasWYale »

Yes, the inheritance problem here was separate from another problem that I posted on another forum topic, which dealt with using "RESULT" as a dbf field name. "RESULT" conflicted with an ACCESS attribute for the window that was using it. You suggested inserting "FIELD->(RESULT)" to refer to the field name, and I replaced all occurrences of that in the code specifically for the window. Your solution was excellent, thank you! After that, it worked fine.
User avatar
robert
Posts: 4225
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Inheritance

Post by robert »

Thomas,
In .Net if you have classes and subclasses, then the subclass is expected to call the constructor of the parent class.
Not calling the parent constructor is a "deadly sin".
What you can do is to create 2 (or more) constructors in the parent class. The default parameterless constructor can do "nothing" and can be called from the child class.
If you choose to do it that way then you will have to strongly type the parameters of the parameters of the constructor. You cannot use overloading when one of the constructors has untyped parameters (clipper calling convention).
Robert
XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
Chris
Posts: 4562
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Inheritance

Post by Chris »

Hi Thomas,

There's a trick you can use to avoid this, add the following code anywhere in your FrameEx constructor:

Code: Select all

IF FALSE
  SUPER()
ENDIF
this will trick the compiler into thinking you are handling calling the parent constructor yourself, and will not add implicitly code to do it, so the parent will never get called.

I am sure Robert will call me naughty for this ;)

.
Chris Pyrgas

XSharp Development Team test
chris(at)xsharp.eu
User avatar
robert
Posts: 4225
Joined: Fri Aug 21, 2015 10:57 am
Location: Netherlands

Inheritance

Post by robert »

XSharp Development Team
The Netherlands
robert@xsharp.eu
User avatar
Chris
Posts: 4562
Joined: Thu Oct 08, 2015 7:48 am
Location: Greece

Inheritance

Post by Chris »

:D
Chris Pyrgas

XSharp Development Team test
chris(at)xsharp.eu
ThomasWYale
Posts: 43
Joined: Thu Jun 20, 2019 5:19 pm

Inheritance

Post by ThomasWYale »

LOL
Post Reply