Conversion of Bartosz Milewski's Winnie program to Purebasic

Everything else that doesn't fall into one of the other PB categories.
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Conversion of Bartosz Milewski's Winnie program to Purebasic

Post by GedB »

As part of getting to grips with Objects in PureBasic I've converted Bartosz Milewski's Winnie program to Purebasic. It is taken from his book 'C++ In Action'

Its a simple HelloWorld windows program, encapsulating the Api inside objects. Not very practical at the moment, but there is more to come.

You'll find discussion of the C++ original here:
http://www.relisoft.com/book/win/1hello.html

I'm working on developing some automatic error handling before progressing to the book's more complex windows example.


For those that might be interested here it is.

Code: Select all

Procedure Throw(Message.s)
  MessageRequester("Error", Message)
  End
EndProcedure

;- RootObject
Interface IRootObject
  Free()
EndInterface

Structure RootObject_vTable
  *Free.l
EndStructure

Structure RootObject_Internal
  *vTable.IRootObject_vTable
  MemoryID.l
EndStructure

Procedure RootObject_Free(*Self.RootObject_Internal)
  If *Self\MemoryID > 0
    FreeMemory(*Self\MemoryID)
  EndIf
EndProcedure

;- WinClassMaker
Interface IWinClassMaker Extends IRootObject
  Register()
EndInterface

Structure WinClassMaker_vTable Extends RootObject_vTable
  *Register.l
EndStructure

Structure WinClassMaker_Internal Extends RootObject_Internal
  _className.s
  _class.WNDCLASSEX
EndStructure

Procedure WinClassMaker_Register(*Self.WinClassMaker_Internal)
  If RegisterClassEx_(*Self\_class) = 0
    Throw("RegisterClass failed")
  EndIf
EndProcedure

Procedure WinClassMaker_Make(*WindowCallback, ClassName.s, hInst)
  ;Define the internal structre
  DefType.WinClassMaker_Internal *Internal
  
  ;Allocate the memory that will hold the internal structure
  *MemoryID = AllocateMemory(SizeOf(WinClassMaker_Internal))
  *Internal = *MemoryID
  *Internal\MemoryID = *MemoryID
  
  ;Set up the vTable.  
  ;Static so that all instances share the same one.
  
  Static vTable.WinClassMaker_vTable
  vTable\register = @WinClassMaker_Register()
  vTable\Free = @RootObject_Free()
  *Internal\vTable = vTable
  
  ;Set the className
  *Internal\_className = ClassName
  
  ;Populate _class, an instance WNDCLASSEX
  *Internal\_class\lpfnWndProc = *WindowCallback
	*Internal\_class\hInstance = hInst          
	*Internal\_class\lpszClassName = @*Internal\_className
	*Internal\_class\cbSize = SizeOf(WNDCLASSEX)
	*Internal\_class\hCursor = LoadCursor_(0, #IDC_ARROW)
	*Internal\_class\hbrBackground = #COLOR_WINDOW + 1
	*Internal\_class\style = 0
	*Internal\_class\cbClsExtra = 0
	*Internal\_class\cbWndExtra = 0
	*Internal\_class\hIcon = 0
	*Internal\_class\hIconSm = 0
	*Internal\_class\lpszMenuName = 0
  
  ;Return the address of our internal structure
  ProcedureReturn *Internal
EndProcedure


;- Window
Interface IWindow Extends IRootObject
  Display(cmdShow)
EndInterface

Structure Window_vTable Extends RootObject_vTable
  Display.l
EndStructure

Structure Window_Internal Extends RootObject_Internal
  _h.l
EndStructure

Procedure Window_Display(*Self.Window_Internal, cmdShow)
  If *Self\_h = 0
    Throw("Window_Display() called while the windows handle is null")
  Else
    ShowWindow_(*Self\_h, cmdShow)
    UpdateWindow_(*Self\_h)
  EndIf
EndProcedure

;Procedure Window_Make(hwnd)
;Window is created by WinMaker\Create()

;- WinMaker
Interface IWinMaker Extends IRootObject
  Create(Title.s)
EndInterface

Structure WinMaker_vTable Extends RootObject_vTable
  Create.l
EndStructure

Structure WinMaker_Internal Extends RootObject_Internal
  _className.s
  _exStyle.l
  _style.l
  _hInst.l
  _hMenu.l
  _hWndParent.l
  _height.l
  _width.l
  _x.l
  _y.l
  _data.l
EndStructure

Procedure WinMaker_Create(*Self.WinMaker_Internal, Title.s)
  hwnd = CreateWindowEx_(*Self\_exStyle, *Self\_className, Title, *Self\_style, *Self\_x, *Self\_y, *Self\_width, *Self\_height, *Self\_hWndParent, *Self\_hMenu, *Self\_hInst, *Self\_data)
  If hwnd = 0
    Throw("Window Creation Failed")
  EndIf
  ;wrap hwnd in a Window object
  ;Define the internal structure
  DefType.Window_Internal *Internal
  
  ;Allocate the memory that will hold the internal structure
  *MemoryID = AllocateMemory(SizeOf(*Internal))
  *Internal = *MemoryID
  *Internal\MemoryID = *MemoryID
  
  ;Set up the vTable.
  ;Static so that all instances share the same one.
  Static vTable.Window_vTable
  vTable\Free = @RootObject_Free()
  vTable\Display = @Window_Display()
  *Internal\vTable = @vTable
  
  ;Populate the private values
  *Internal\_h = hwnd
  
  ;Return the address of our internal structure
  ProcedureReturn *Internal
EndProcedure
  
Procedure WinMaker_Make(ClassName.s, hInst)
  ;Define the internal structre
  DefType.WinMaker_Internal *Internal
  
  ;Allocate the memory that will hold the internal structure
  *MemoryID = AllocateMemory(SizeOf(WinMaker_Internal))
  *Internal = *MemoryID
  *Internal\MemoryID = *MemoryID
  
  ;Set up the vTable.  
  ;Static so that all instances share the same one.
  Static vTable.WinMaker_vTable
  vTable\Create = @WinMaker_Create()
  vTable\Free = @RootObject_Free()
  *Internal\vTable = @vTable
  
  ;Populate the private values
  *Internal\_style = #WS_OVERLAPPEDWINDOW
  *Internal\_exStyle = 0
  *Internal\_className = ClassName
  *Internal\_x = #CW_USEDEFAULT
  *Internal\_y = 0
  *Internal\_width = #CW_USEDEFAULT
  *Internal\_height = 0
  *Internal\_hWndParent = 0
  *Internal\_hMenu = 0
  *Internal\_data = 0
  *Internal\_hInst = hInst
  
  ProcedureReturn *Internal
EndProcedure

;- WinProcedure
Procedure WinProcedure(hwnd, Message, wParam, lParam)
  Select Message
    Case #WM_Destroy
      PostQuitMessage_(0)
      ProcedureReturn 0
  EndSelect
  
  ProcedureReturn DefWindowProc_(hwnd, Message, wParam, lParam);
EndProcedure

;- WinMain

hInst = GetModuleHandle_(0)
ClassName.s = "Winnie"
winClass.IWinClassMaker = WinClassMaker_Make(@WinProcedure(), "Winnie", hInst)
winClass\Register()

maker.IWinMaker = WinMaker_Make(ClassName, hInst)
win.IWindow = maker\Create("Hello Windows!")

win\Display(#True)

;Message loop
DefType.msg Message

Repeat 
  status.l = GetMessage_(@Message, 0, 0, 0)
  If status > 0
    DispatchMessage_(@Message)
  EndIf
Until status = 0 Or status = -1

win\Free()
maker\Free()
winClass\Free()

If status = -1
  End -1
Else
  End Message\wParam
EndIf

MadMax
Enthusiast
Enthusiast
Posts: 237
Joined: Mon Oct 06, 2003 11:56 am

Post by MadMax »

My God!!!! :? :?

All that code just to open a window!!! :roll:

Probably all very interesting and necesary, but certainly not my cup of tea.

Sometimes I get the feeling that people that write c++ books, just want to scare newcomers; less people coding, less competition sort of thing. :twisted:

Of course I'll accept I'm totally wrong. :wink:
thefool
Always Here
Always Here
Posts: 5875
Joined: Sat Aug 30, 2003 5:58 pm
Location: Denmark

Post by thefool »

All that code just to open a window
yes- but he said
Not very practical at the moment
so maybe its more than a window :D
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

What the code does is wrap the Windows API in Objects. Quite light weight ones at that.

At the moment it is a work in progress. I need to sort out the resource manangement side of things before I can continue.

What I hope to have by the end is an easy to use object structure that provide very low level control over window classes which can be used for implementing custom classes.

Another point is that 80% of the code is boilder plate, a systematic wrapping of structures in unwieldy function calls. I'm wondering how much of it can be automated with a preprocessor.

I've posted it to show that non-trivial C++ can be converted into Purebasic.
eriansa
Enthusiast
Enthusiast
Posts: 277
Joined: Wed Mar 17, 2004 12:31 am
Contact:

Post by eriansa »

;- RootObject
Interface IRootObject
Free()
EndInterface

Structure RootObject_vTable
*Free.l
EndStructure

Structure RootObject_Internal
*vTable.IRootObject_vTable ----->???????
MemoryID.l
EndStructure
Shouldn't it be :

Structure RootObject_Internal
*vTable.RootObject_vTable
MemoryID.l
EndStructure

But hen : Why does the compiler not generate an error ?
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

You're exactly right, and that's what my copy on disk says.

I can't remember, but I must have done some last minute touching up.

Sorry.

Anyway, the internal structure should contain just a long pointer to the vtable, rather than the vtable embedded. ie.

Code: Select all

;- RootObject
Interface IRootObject
Free()
EndInterface

Structure RootObject_vTable
*Free.l
EndStructure

Structure RootObject_Internal
*vTable.l
MemoryID.l
EndStructure
At the moment It works, but space is being wasted since I'm reserving space for the whole vTable, but only using the first entry to store a pointer.

I'll post a fully fixed example when I get a chance.
Post Reply