Having problem understanding what is wrong with this project when compiled as it shows a WARNING?
QuoteCompiling...
Main.eba
File: C:\My EBasic Forum\DTP\Main.eba (35) Warning: Uninitialized variable: DemoDTP - CDTP
CDTP.eba
Thought I had done it 'by the book'.
The RECT does not draw either? DemoDTP.DrawObject()
Allan
The warning is normal and can be ignored.
The compiler generates warnings when it doesn't see a local variable being initialized before it has been used for the first time. Unfortunately it doesn't know that classes don't need to be initialized so it will always generate that warning.
If you were to create the class with NEW then your wouldn't get the warning.
SUB CDTP::SetWindow(window win)
win = win
ENDSUB
What did you thing that would do? Scope resolution is local->class->global. So all the statement win=win is doing is copying the local parameter over itself. If you really wanted to copy the whole window structure you could use the 'this' pointer:
SUB CDTP::SetWindow(window win)
*<CDTP>this.win = win
ENDSUB
And while that would make your class work, it would be poor practice. Name your class variables something a little more unique, such as using m_win, and that way you'll instantly recognize that it is a member variable of the class.
A better design would be to use a pointer, instead of a window UDT. Only requires a couple of changes:
CLASS CDTP
DECLARE CDTP()
DECLARE SetWindow(window win)
DECLARE DrawObject()
PROTECTED
WINRECT m_DRect
POINTER m_win
ENDCLASS
' CDTP.eba
$INCLUDE "CDTP.inc"
SUB CDTP::CDTP()
m_DRect.left = 10
m_DRect.top = 10
m_DRect.right = 180
m_DRect.bottom = 120
m_win = NULL
ENDSUB
SUB CDTP::SetWindow(window win)
m_win = win
ENDSUB
SUB CDTP::DrawObject()
IF m_win
RECT #<WINDOW>m_win, m_DRect.left, m_DRect.top, m_DRect.right, m_DRect.bottom, RGB(255,155,0), RGB(100,0,255)
ENDIF
ENDSUB
Keep cracking at it, and be sure to study the object oriented programming section of the users guide.
http://www.ionicwind.com/guides/emergence/language_topics_oop.htm
Thanks,
Paul.
Allan
You never told it to draw.
SUB MakeDemo(),INT
DEF DemoDTP as CDTP
DemoDTP.SetWindow(Main)
DemoDTP.DrawObject()
RETURN 0
ENDSUB
Larry
OK - Thanks. I thought I would pass Main in from the Main Window to the Class.
Also can you put another Class into a Class as a Member of the class.
BRIEFLY...
CLASS one
declare one()
PROTECTED
winrect a
ENDCLASS
CLASS two
declare two()
one xone ' other class
PROTECTED
winrect b
ENDCLASS
Allan
Quotecan you put another Class into a Class as a Member of the class.
I don't think so but I might be wrong
A class
member is a variable of some type and not a "function/subroutine"
A class
method is the "function/subroutine"
I know you can create multiple classes and use a class inside of another class.
Maybe someone else that understands it better can explain it better.
Larry
Yes - I have changed the code and it works as expected.
Though I have tried putting second class in as member.
Should it not hold the complete class and have access?
Attached the updated CDTP with second class.
I have used this in other langs (CBuilder) and thought it may work in EB?
Allan
A class member can be any variable type, including another class. As long as that class has been defined before use of course.
Have you read the users guide section?
Paul.
QuoteHave you read the users guide section?
More times than I can count! But that does not mean I get the message correct.
Originally I was hung up on the misunderstanding of this:
QuoteAccess protection
It is generally considered bad form to directly access a member variable from outside of a method implementation. In the above examples we have been setting the name of the employee directly using a dot operator.
To aid in limiting outside access to an objects data Emergence provides three keywords, PUBLIC, PRIVATE and PROTECTED that control how an objects methods and members may be used.
I was trhing to access the Methods out of SCOPE for them.
SCOPE was the problem I had first try and again this time.
Now I got it in my head that SCOPE has to be observed for the different Instances of a CLASS. The Circles Protected Members were not accessible from where I was trying to use them!
Have re-done the DTP again and attached it.
Please have a look at the code and any suggestions would be most welcome.
regards,
Allan
Of course I have suggestions. Which is why I asked if you had read the section on OOP programming. I could kind of see where you were heading when you fist posted your code.
Instead of one class that has member variables of other classes, you should experiment and learn about virtual methods and inheritance. I hesitate to write the code for you, as OOP is something that is best learned hands on.
Think of a design that uses a base class, perhaps called CDTPObject, which has virtual methods named SetParams and Draw, you then derive from the base class overriding those methods with the ones in the derived classes.
Class CDTPObject
Declare SetWindow(pointer win)
Declare Virtual SetParams(pointer obj)
Declare Virtual Draw(pointer win)
End Class
Class CDTPRect, CDTPObject
Declare Virtual SetParams(pointer obj)
Declare Virtual Draw(pointer win)
...
End Class
Class CDTPEllipse, CDTPObject
Declare Virtual SetParams(pointer obj)
Declare Virtual Draw(pointer win)
...
End Class
This is just to give you ideas and inspiration. Method overriding and inheritance are the primary reason to write an application using OOP instead of a straight procedural implementation. Objects that share similar properties should have an abstract base class that can be derived from. GUI libraries commonly employ this technique, using a base "window" class that all other objects are derived from. Which gives the advantage of not needing to know what kind of object is drawing, the application using the GUI objects call the virtual methods Draw, Move, Resize, etc and the correct method for the particular object gets called.
Objects like this work best when created dynamically. So for sake of discussion lets say we were using a GUI library whose base class was called GWindow, and there were derived classes called GButton, GMenu, and GToolbar. In our imaginary application we have a subroutine that draws all objects contained in a linked list, and those objects were created by another subroutine...
SUB CreateInterface
pointer obj
obj = NEW(GWindow,1)
#<GWindow>obj.Create(100,100,100,100)
ListAdd(gList,obj)
obj = NEW(GButton,1)
#<GButton>obj.Create(10,10,50,20,"Close")
ListAdd(gList,obj)
obj = NEW(GMenu,1)
#<GMenu>obj.AddTitle("File")
#<GMenu>obj.AddItem("Quit")
ListAdd(gList,obj)
ENDSUB
SUB DrawEverything(pointer list)
pointer obj
for obj = each list as GWindow
#obj.Draw()
next
ENDSUB
In the DrawEverything subroutine we don't need to know the class of the object in order to draw it, because all objects are derived from a common base class and 'Draw' is a virtual method.
Your current method will work, and there is nothing wrong with it of course, so if it makes you happy then go with it. When you get a bit more comfortable then try using base and inherited classes. One step at a time.
Paul.
QuoteOf course I have suggestions.....
Those suggestions are great and I will take them to heart and use them as shown.
There is just one more thing to get my head around in that project and it is saving to file.
I wish to use that technique of VIRTUAL Functions to Write and Read to a File from the LinkedList, the same way you have done the DRAW.
Thank you Paul.
DTP4 now has Derived Classes and Virtual Methods as suggested by Paul in above message...
This was a very educational task and helped me see more clearly into CLASS structure. I have been using Classes for years (pre-built ones that is) but have not been actually building many of my own Classes.
Actually doing the coding helps learn!
Experiment . . .
I tried to make the CDTPEllipse Class Derive from the CDTPRect Class so as to use the Members already defined in the CDRect Class. They are the same Members. Help File - EB would need to have more than one level of inheritance to handle that I guess?
After finishing the Program I realised that the m_win could have been handled in the OBJECTS that were passed to the SetParm Method instead of having its own Method [SetWindow(pointer win)] which is used as well in its Derived Classes (CDTPRect, CDTPEllipse, CDTPCircle).
But the way it is shows the Classes at work using Methods of a Base Class as well as the VIRTUAL Methods in the Derived Classes.
I can see ways to extend the program using LineStyle and Brush Style and other effects. It will keep me bizzy for a while!
EB Classes are very neat!!
Thanks for your help, Paul and Larry.
Please have a look at the code and any suggestions would be most welcome.
Allan
Been following what Allan is doing and trying to make sense of it all.
There's one part in particular that I'm just not getting an handle on.
Allan is creating objects from different classes and storing a pointer to each in a linked list.
Then there is thisSUB DrawEverything(pointer list)
pointer obj
for obj = EACH list AS CDTPObject
#obj.Draw()
NEXT
ENDSUB
He's going through the list with typecasting as the basec class CDTPObject
I don't understand what is triggering the calling of the CORRECT derived class.
Is it purely an internal windows thing that I should accept that that's the way it works 'just cause'?
Larry
Virtual functions ensure the correct method is called. That is the purpose of them, and it has to do with how OOP works. Nothing to do with Windows at all.
Every instance of a class object has a vtable (virtual function table), basically an array of addresses to subroutines. When you declare a method as virtual:
class SomeClass
declare virtual Draw()
end class
The vtable would have 1 address in it, the address of SomeClass::Draw()
When you derive a class and override the virtual function:
class AnotherClass, SomeClass
declare virtual Draw()
end class
The vtable element has the address of AnotherClass::Draw(), in other words it replaced the address of SomeClass::Draw in that array.
A pointer is generic when you think about it, it just contains an address. So if you have:
#<SomeClass>p.Draw()
And p actually contains a pointer to AnotherClass then the vtable ensures the correct method gets called. Much like a function pointer:
!<Draw>p.vtable[0]()
So a virtual method is called by the compiler by looking up the address in the vtable, while a non virtual method is called directly. If Allan had not used the "virtual" keyword when declaring the methods then the line #obj.Draw() would only call CDTPObject::Draw().
Emergence BASIC, and Aurora use a vtable mechanism identical to C++, which is why we can use Emergence Classes to create COM servers. So you could search the net for a better description of virtual functions and the vtable, but most are confusing to non C++ users.
In Emergence, like in C++ the address of the vtable is stored in the first member of the class instance, it is kind of like a hidden variable. Consider this code:
class SomeClass
declare virtual Draw()
declare virtual Erase()
end Class
sub SomeClass::Draw()
endsub
sub SomeClass::Erase()
endsub
def class1 as SomeClass
We create a class, implement two virtual methods, and define a variable of that class. If you look at the assembly output, towards the end, you will see the vtable:
SomeClass_vtable_
dd $SomeClass@Draw
dd $SomeClass@Erase
segment .bss use32 align=4
$class1 resb 4
Which is the array of address I mentioned. Now lets add a derived class:
class AnotherClass,SomeClass
declare virtual Draw()
end Class
sub AnotherClass::Draw()
endsub
def class2 as AnotherClass
When compiled and you examine the assembler output then you'll see the vtable of AnotherClass:
SomeClass_vtable_
dd $SomeClass@Draw
dd $SomeClass@Erase
AnotherClass_vtable_
dd $AnotherClass@Draw
dd $SomeClass@Erase
Since we only overrode the Draw method the AnotherClass vtable has AnotherClass::Draw as its first address and SomeClass::Erase as it's second address. Which means if you don't override a method then the base class version will be called.
Paul.
Paul
That's an excellent explanation of virtual methods.
But you know how I am.
To me, vtables are a "windows thing" ;) and the answer I really understand is:
QuoteIs it purely an internal windows thing that I should accept that that's the way it works 'just cause'
;D
Thanks
Larry
For anyone interested in this programs development I have now added File Save and File Load Virtual Methods to the Class.
Also have taken out the Method of Setting the Window m_win Member as it is only needed for Drawing the Graphics and can be passed through the Draw Virtual Method.
The SaveFile Method was easy and really only a copy of the Draw Virtual Method.
The ReadFile Method was not as simple so if anyone can see how to contain it more into the CLASS lets know please.
Any Suggestions are welcome?
The Code is Attached.
Allan
DTP6
Using CLASSES in EBasic has made it easy to simply add new Members and Methods to the existing project.
We have added two more Members to the Rectangle, Ellipse and Circle Classes. They are LineWidth and LineStyle.
LineWidth is only used for the SOLID LineStyle as other LineStyles only use LineWidth of 1.
The Two New members are added to the Derived Class Definitions. The necessary changes to the Methods have been done in the CDTP.eba file. The Constructors needed two additional members set.:-
m_LineWidth = 1
m_LineStyle = "@LSSOLID"
The SetParam Methods had two new Members set.:-
m_LineWidth = #obj.LineWidth
m_LineStyle = #obj.LineStyle
WriteFile Methods required two new code lines:
WRITE #gf, m_LineWidth
WRITE #gf, m_LineStyle
ReadFile Methods required two new code lines:
READ(#gf, m_LineWidth)
READ(#gf, m_LineStyle)
The Virtual Draw Methods also had to be changed so as to select the correct LineStyle and LineWidth to draw the Graphic with.
The UDT for each Graphic Object had to have the two new members put in them also. The UDT for the Objects is now in a file by itself, namely "udts.inc". The reason for doing that is they are needed in two other Graphic Input Dialogs now in the Program.
The two new dialog windows are - 1: For Rectangle and Ellipse Input data; 2. Circle Input data.
In the Main Program window there are now Vertical and Horizontal Scrollbars. The scrollbars are used to scroll a second window (gwin) which is a child of the Main window and is used to draw the Graphic Objects onto instead of drawing them onto the Main Window.
A Toolbar has been added with images for Text, Rectangle, Circle, Ellipse. Text is not yet set into the CLASS as a Derived Class. The other Buttons in the Toolbar are active and will display a Dialog allowing the entry of the parameters for the graphics.
These graphics created in the Dialogs are then stored into the Linked List and drawn on the new gwin Window.
The Save and Load File methods now use a Dialog Window to choose the File to Load or Save.
A lot of the code has been taken from EBasic Sample programs and other users contributions on the Forum.
Allan