Subclassing in XB++

Xbase++ forum for Visual FoxPro developers
Post Reply
Message
Author
LiddycoatA
Posts: 3
Joined: Mon Oct 10, 2016 12:20 pm

Subclassing in XB++

#1 Post by LiddycoatA »

TL:DR; When I create a subclass, and call the :new() clause. my new class's :init() method appears to interrupt the superclass setting default values to it's members. This breaks further execution of function calls on my new class. How can i continue the default behavior after :init() is called to set these defaults or do I have instantiate them in my subclass definition?

--------------------------------
Detailed explanation:

My company has decided after this years Devcon in Arizona to take the plunge and start converting apps from Foxpro to XB++. We have decided for the moment to forgo using Express while we learn the base language. I loved what I saw at the conference this year in regards to Express and we'll likely start using it once were more comfortable with the root concepts of XB++.

I'm working on a simple program of ours, it only uses one table and is a internal only management system. A great starter project.

XbpBrowse lacks a few things that I want to implement so, I figured to subclass it and create my own class. I'll call it MyBrow

MyBrow has a default method Init(). According to the Xbase documentation Init() executes immediately after :new() This is perfect because all I want to do is start by adding some variable properties to the class that I can easily access and modify at runtime.

Code: Select all

CLASS METHOD MyBrow:Init(oParent, oOwner, aPos, aSize, aPresParam, lVisible)
   //init
   ::aSize := aSize
   ::aPos  := aPos
   ::sType := "Browser"
RETURN self
So far everything was running swimmingly, until back in my main prg I hit:

Code: Select all

oBrowse:addColumn( oXbpColumn )
: oError:description : Parameter has a wrong data type

I double and triple checked through the debugger and i was successfully passing a XbpColumn object, I tried another method:

Code: Select all

oBrowse:addColumn( FieldBlock(cField),35,cField)
Again I found the same error. It was at this point that I noticed a lot of NIL values in MyBrow's members. I compared the members to a regular XbpBrowse object at runtime and noticed a large number of default values in MyBrow were missing. I was missing values like ColCount, ColPos, ControlState, CursorMode and many many more. My conclusion is that :init() executes between :new() and when these values are set to their defaults.

So my question becomes, how can finish creating the new class, with the default behaviors?

Thanks for any help I can garner.

User avatar
Auge_Ohr
Posts: 1405
Joined: Wed Feb 24, 2010 3:44 pm

Re: Subclassing in XB++

#2 Post by Auge_Ohr »

hi,

my Answer is using "pure" Xbase++.
LiddycoatA wrote:

Code: Select all

CLASS METHOD MyBrow:Init(oParent, oOwner, aPos, aSize, aPresParam, lVisible)
   //init
   ::aSize := aSize
   ::aPos  := aPos
   ::sType := "Browser"
RETURN self
your Class must inherit from XbpBrowse
o:aSize is PROTECT
o:aPos and o:sType does not exist so you have to declare it.

Code: Select all

CLASS MyBrow FROM XbpBrowse
EXPORTED:
     // your new iVAR
     VAR aPos
     VAR sType

INLINE METHOD init( oParent, oOwner, aPos, aSize, aPP, lVisible )
   ::XbpBrowse:init( oParent, oOwner, aPos, aSize, aPP, lVisible )

*  ::aSize := aSize  // PROTECT, use other iVAR Name
   ::aPos  := aPos   // try other (better) Name
   ::sType := "Browser"  
RETURN self

INLINE METHOD create( oParent, oOwner, aPos, aSize, aPP, lVisible )
   ::XbpBrowse:create( oParent, oOwner, aPos, aSize, aPP, lVisible )
RETURN self

ENDCLASS
LiddycoatA wrote:So far everything was running swimmingly, until back in my main prg I hit:

Code: Select all

oBrowse:addColumn( oXbpColumn )
: oError:description : Parameter has a wrong data type
a XbpBrowse() have XbpColumn() ... one or many
Method of XbpBrowse() are to "setup" ( Add/Del ... ) XbpColumn() or to navigate in Column.

Question : what do you want to enhance with your new Class ?

most want a new Column Class.
look into
v1.9x -> x:\ALASKA\XPPW32\SOURCE\samples\solution\xbpget\
v2.0x -> x:\USER\xxx\Documents\Xbase++\Source\samples\solution\xbpget\
greetings by OHR
Jimmy

skiman
Posts: 1183
Joined: Thu Jan 28, 2010 1:22 am
Location: Sijsele, Belgium
Contact:

Re: Subclassing in XB++

#3 Post by skiman »

Hi,

I can only give the advice to start with eXPress. This will make it a lot easier.
Best regards,

Chris.
www.aboservice.be

User avatar
rdonnay
Site Admin
Posts: 4722
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: Subclassing in XB++

#4 Post by rdonnay »

Can you give us the full source for your class (and your test program) ?
The eXpress train is coming - and it has more cars.

LiddycoatA
Posts: 3
Joined: Mon Oct 10, 2016 12:20 pm

Re: Subclassing in XB++

#5 Post by LiddycoatA »

The full code for my class is very simple.

Code: Select all

CLASS MyBrow FROM XbpBrowse
  //declare vairables
  PROTECTED:   //Visible only in class methods
  EXPORTED:    //globally visable
     CLASS VAR aSize, aPos, sType

     CLASS METHOD  Init        // class methods declaration

ENDCLASS



CLASS METHOD MyBrow:Init(oParent, oOwner, aPos, aSize, aPresParam, lVisible)
   //init
   ::aSize := aSize
   ::aPos  := aPos
   ::sType := "Browser"

RETURN self
I don't want to post my full code because there's a lot of stuff that doesn't matter. the relevant code is:

Code: Select all

PROCEDURE MAIN
   LOCAL nEvent, mp1, mp2, oXbp
   LOCAL oBtn_First, oBtn_Prev, oBtn_Next, oBtn_Last
   LOCAL oBrowser
   PRIVATE oApplication, oScreen    //private makes these viewable from any child function. Locals are released at end of declairing function.

   // Create Main Application Window
   //Object Screen    new dialog      parent obj, owner, position, size
   oApplication         := XbpDialog():new( )
   oApplication:title   := "Mytitle"
   oApplication:taskList := .T.
   oApplication:usevisualstyle :=.T.
   oApplication:minsize := {1150,500}

   //do not call create until after settings above are set. create is the final call to build the screen.
   oApplication:create( ,,, {1280,720} )
   CenterControl( oApplication )
   
   //Add screen objects
   //all ojects are added to the application's drawing area.
   oScreen      := oApplication:DrawingArea

   //Add Browser
   oBrowser = ScreenBrowser()

   // I believe this code is equevilant to READ EVENTS in foxpro
   nEvent := 0
   DO WHILE nEvent <> xbeP_Close
      nEvent := AppEvent( @mp1, @mp2, @oXbp )
      oXbp:handleEvent( nEvent, mp1, mp2 )
   ENDDO
RETURN


FUNCTION ScreenBrowser()

 //Add Table Browse
   //NOTE: XB++ calculates position based on a 0,0 bottom left position. By default items are anchored to the bottom of the screen.
   //   Positions are for the bottom left corner of objects.

   LOCAL nBrowseHeight  := 250
   LOCAL nBrowseWidth   := 1100
   LOCAL nMarginBuffer  := 20
   LOCAL aColNames, cnt :=1
   LOCAL oBrowse, cField, cField1, i, aColList, aBrowsePos
   aAppWindowSize := oApplication:ClientSize

   nBrowseLeftPos := aAppWindowSize[1]/2 - nBrowseWidth/2
   nBrowseBotPos  := aAppWindowSize[2] - nBrowseHeight - nMarginBuffer
   aBrowsePos     := {nBrowseLeftPos,nBrowseBotPos}

   //oBrowse := XbpBrowse():new(oScreen,,aBrowsePos,{nBrowseWidth,nBrowseHeight})         //this works
   oBrowse := MyBrow():new(oScreen,,aBrowsePos,{nBrowseWidth,nBrowseHeight})                //this doesn't
   oBrowse:layoutalign := XBPLAYOUT_TOP
   


   // Navigation code blocks for the browser
   oBrowse:skipBlock     := {|n| DbSkipper(n) }
   oBrowse:goTopBlock    := {| | DbGoTop()    }
   oBrowse:goBottomBlock := {| | DbGoBottom() }
   oBrowse:phyPosBlock   := {| | Recno()      }
   oBrowse:goPhyPosBlock := {|n| DbGoto(n)    }

   // Navigation code blocks for the vertical scroll bar
   oBrowse:posBlock      := {| | DbPosition()    }
   oBrowse:goPosBlock    := {|n| DbGoPosition(n) }
   oBrowse:lastPosBlock  := {| | 100             }
   oBrowse:firstPosBlock := {| | 0               }

   //This loads ALL fields into browser
   FOR i:=1 TO FCount()
      cField := FieldName( i )
      oBrowse:addColumn( FieldBlock(cField), , cField )    //error happens here when oBrowse is a MyBrow class object.
   NEXT
   oBrowse:hScroll := .F.
   oBrowse:create()
   oBrowse:goBottom()
   oBrowse:RefreshAll()

   //Add index select
   IndexBox(aColNames)

ENDIF
RETURN oBrowse



I appreciate your attempt Auge_Ohr, but your code throws further errors. INLINE METHOD has a reserved keyword, and adding the ::XbpBrowse:init( oParent, oOwner, aPos, aSize, aPresParam, lVisible ) line results in an error"Access to instance-variable not allowed within class-object"

For the question about, what I want the class to do, I want it to do exactly what a XbpBrowse does, with a couple additional members I can easily access that declares the objects size and position in the window. But it's much less about what I want it to DO and more about HOW do I manipulate classes in XB++. This is a learning project more than anything before we start to look at taking programs with a hundred PRG files and forms from foxpro to XB++.

User avatar
Auge_Ohr
Posts: 1405
Joined: Wed Feb 24, 2010 3:44 pm

Re: Subclassing in XB++

#6 Post by Auge_Ohr »

LiddycoatA wrote:The full code for my class is very simple.

Code: Select all

   //oBrowse := XbpBrowse():new(oScreen,,aBrowsePos,{nBrowseWidth,nBrowseHeight})         //this works
   oBrowse := MyBrow():new(oScreen,,aBrowsePos,{nBrowseWidth,nBrowseHeight})                //this doesn't
   oBrowse:layoutalign := XBPLAYOUT_TOP
you need to CREATE() before assign Codeblock for navigation etc.
I appreciate your attempt Auge_Ohr, but your code throws further errors. INLINE METHOD has a reserved keyword, and adding the ::XbpBrowse:init( oParent, oOwner, aPos, aSize, aPresParam, lVisible ) line results in an error"Access to instance-variable not allowed within class-object"
as i say "aSize" is PROTECT -> no Access from "outside"
greetings by OHR
Jimmy

User avatar
rdonnay
Site Admin
Posts: 4722
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: Subclassing in XB++

#7 Post by rdonnay »

You need to make sure that your INIT method calls the INIT method of the SUPER class.
Also, the Init method cannot be a CLASS METHOD in this case. If it is, you will get a runtime error when creating the browse.

Code: Select all

CLASS MyBrow FROM XbpBrowse
  //declare vairables
  PROTECTED:   //Visible only in class methods

  EXPORTED:    //globally visable
     CLASS VAR aSize, aPos, sType
     METHOD  Init        // class methods declaration

ENDCLASS

* --------

METHOD MyBrow:Init(oParent, oOwner, aPos, aSize, aPresParam, lVisible)
   //init
   ::aSize := aSize
   ::aPos  := aPos
   ::sType := "Browser"

RETURN SUPER:init(oParent,oOwner,aPos,aSize,aPresParam,lVisible)
What kind of new features would you like to add to the XbpBrowse class?
Perhaps I can help you with this.
The eXpress train is coming - and it has more cars.

LiddycoatA
Posts: 3
Joined: Mon Oct 10, 2016 12:20 pm

Re: Subclassing in XB++

#8 Post by LiddycoatA »

Fantasitc Roger, that did it. Thanks a ton!

If you don't mind, can you clarify or point me towards an article that explains the difference between calling a CLASS Method Myclass:MyMethod and calling it without the CLASS declaration?

User avatar
rdonnay
Site Admin
Posts: 4722
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: Subclassing in XB++

#9 Post by rdonnay »

Syntax

CLASS METHOD <MethodName,...>

or


CLASS METHOD <MessageName> [IS <MethodName>] [IN <SuperClass>]

Parameters

<MethodName,...>

<MethodName,...> is a comma separated list containing names of class methods of the class object being declared. The name for a method follows the same convention as function and variable names. It must begin with an underscore or a letter and must contain alpha numeric characters. The first 255 characters are significant.

<MessageName>

<MessageName> designates the message which must be sent to a class object so that it executes the class method with the name <MethodName> . <MessageName> is used when several superclasses are available in the class hierarchy which have methods with the same name.

<MethodName>

When the option IS is used, a list of method names cannot be used. In this case only a single method name is permitted in the CLASS METHOD declaration. The method with the name <MethodName> is executed by the class object when it receives the message <MessageName> .

<SuperClass>

When multiple superclasses have the same class method <MethodName> it can be specified explicitly from which superclass a class method is to be called when an object receives the message <MessageName> . Only class methods of a superclass which are visible (which are not declared with the attribute HIDDEN: within the superclass declaration) can be referenced.

Description

CLASS METHOD declares a method as a class method within the class declaration (CLASS...ENDCLASS). Class methods are executed only from the class object. When an instance object receives the message for a class method, it forwards the message on to the class object.
The source code for a class method is generally implemented after the statement ENDCLASS. The code for the class method is prefaced by the declaration CLASS METHOD <MethodName> . If the class method is already implemented in a superclass or is assigned to a specific superclass by the option IN, the class method does not need to be implemented within this class. Instead, an object of this class executes the corresponding method in the superclass. When the class method <MethodName> is implemented in a superclass, it is automatically available for use in a subclass. When a class method of the same name is also declared in the subclass, the object executes the class method within the subclass and not the class method of the same name which exists in a superclass.

Within the source code for the class method the class object is always available via the variable self . This occurs whether the message to call the class method was sent to an instance object or directly to the class object.
When using CLASS METHOD outside the class declaration to preface the method implementation, the name of the class method <MethodName> is always used. When the method is declared as <MessageName> IS <MethodName> , <MessageName> determines the message which must be sent to an object, for the method <MethodName> to be executed.

<MessageName> is a mechanism for providing an alternative name for a class method. It can be used to declare a different name in this class than that used in the superclass. This is especially useful when two or more superclasses contain class methods with the same name. When this occurs, declaring alternative message names (unique within this class) for calling the class method in each of the superclasses is recommended. This clearly indicates which superclass class method will be used. To accomplish this, the declaration is used in the form CLASS METHOD..IS..IN.

The visibility of a class method is determined by the visibility attribute, which is set with one of the keywords EXPORTED:, PROTECTED:, or HIDDEN: (see the sections covering each attribute for additional information).
In general, class methods are only needed for accessing class variables. Class variables are member variables of the class object, and exist only once within a class. The value or contents of a class variable is the same for all instances of a class.
Reserved names: The object model of Xbase++ uses the following identifiers for class methods which are reserved and cannot be used in naming class methods. These are:

Reserved identifiers for class methods

Method Description

:new() Creates instance
:className() Returns name of class
:classObject() Returns class object
:isDerivedFrom() Determines whether or not an object is derived
from a particular class
:isDerivedFrom(<cClassName> | <oClassObject>) --> .T.|.F.

Example

// Declaring a CLASS METHOD
//
// Class declaration with class variables and methods
//
CLASS Cursor
EXPORTED: // globally visible
CLASS VAR nMaxRow, nMaxCol

CLASS METHOD initClass // class methods
CLASS METHOD SetMode, Hide // declaration

VAR nRow, nCol, nShape
METHOD init, Show, UpdatePos
ENDCLASS

CLASS METHOD Cursor:initClass() // class method

::nMaxRow := MaxRow() // implementations
::nMaxCol := MaxCol()
RETURN self

CLASS METHOD Cursor:SetMode( nMaxRow, nMaxCol )
IF SETMODE( nMaxRow, nMaxCol )
::initClass()
ENDIF
RETURN self

CLASS METHOD Cursor:Hide
SetCursor( 0 )

RETURN self
The eXpress train is coming - and it has more cars.

Post Reply