• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

Bc9 Info

Started by James C. Fuller, January 14, 2013, 03:53:09 PM

Previous topic - Next topic

0 Members and 2 Guests are viewing this topic.

James C. Fuller

Ok here it is.

The attached file has Patrice's demo dll source translated as best I could. It was just too large to do any definitive "is it correct" type of testing.
Also included are tips to converting PowerBASIC to Bc9 along with a link to the latest bc9 package.

The actual bc9 package is compressed with 7zip: http://www.7-zip.org/

I was not really ready for a new update but hopefully it will work for the limited interest there is on this
site as downloads are restricted I believe.


James

Patrice Terrier

#1
James--

Thank you for the BC9 translation of this dummy DLL.

I think i shall go plain C, as i did for the C++ HUDplus project i have posted here.

I would avoid to convert my code twice, first to BC9, then to C.

I shall take already on the work i have done with C++ 32-bit, in the C++ programming (SDK style) section in 2011.

At this time, i think doing everything by "hand" is the only way to insure that the translation would be correct.

Thank you again for your help.

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

James C. Fuller

Patrice,
  You're welcome.
  In the conversion of PowerBASIC to c bc9 is just a tool to help, not to do the whole thing.
It will help take the drudgery out of typing all those stupid braces and semi-colons.
Personally I have no need for a 64bit PowerBASIC. It works fine as it is for me.
If I was still doing commercial work my first choice would be PowerBASIC.
Linux has lost the appeal it once had for me so a PBLinux is not a big wish. Linux is too much of a PIA. That's not to say I don't play
around with with it .... :)

James


James C. Fuller


Frederick J. Harris

#4
You've done a lot of work on this James, and I want to say I appreciate it too.  I'm rather certain that I'm going to become familiar with this work also, i.e. your BC9 fork, and the BCx 6 series work.  I've only just started thinking about this issue, and it is a terrible task to convert PowerBASIC to C syntax.  Just taking a rough guess, I doubt if I could average more than 100 lines/hour.  I have rather severe neck problems from a car accident many years ago, and I hate to spend my limited time at a computer doing 'busy' work as opposed to more enjoyable creative work.  For example, I'm sure I have 50000 lines of PowerBASIC code I wouldn't mind converting, so at the rate I mentioned above, that's easily 500 hours.  That's a lot!  Even if the tool could only translate three quarters of it, that's 375 hours saved.

I realize there are issues.  For example, multiple variable declarations per line are treacherous.  Another issue As Patrice mentioned or alludded to, there are in theory an infinite number of possible variable types in C/C++, as no self-respecting C/C++ library developer can see him/herself clear to accepting, for example, one kind of 32 bit unsigned integer; they all need to make up their own names and use #defines/typedefs of the fundamental types given by the compiler - and the compiler will enforce those new names with a vengence.  Bob Zale, on the other hand, was able to clearly see through this nonsense, and realized that one name for a 32 bit unsigned integral type was exactly what was needed.  In PowerBASIC I'll typically use Dwords for anything that's a handle (virtual memory pointer) like so ...


Function Foo(Byval hMain As Dword) As Long
  Local hWnd, hDC, hBrush As Dword
  ...
  ...
End Function


And of course, in C/C++ only this will work ...


int Foo(HWND hMain)
{
HWND hWnd;
HDC hDC;
HBRUSH hBrush;

...
}

   
Also, regarding that discussion between yourself and Patrice last week about statics verses locals, I'd have to side with Patrice on that.  I'd never use statics for locals in PowerBASIC or C.  This is what I found in the PowerBASIC 10 docs on that issue ...

Quote
Local

Local variables are only accessible within a single SUB, FUNCTION, METHOD, or PROPERTY. They are automatically created and initialized each time you enter the procedure. They are automatically destroyed when you exit the procedure. This is the default variable scope unless you declare otherwise.

Static

Static variables are only accessible within a single SUB, FUNCTION, METHOD, or PROPERTY. They are initialized when your program starts, but retain their value regardless of how many times the procedure is entered and exited. They are destroyed only when the program ends.

Additional Scope Considerations

LOCAL variables are stored on the stack frame of the procedure, so the address will change throughout the program.  Therefore, it may not be safe to rely upon a pointer to them.  Likewise, INSTANCE and THREADED variables exist only as long as the object or  is active, though their lifetime is generally somewhat longer.  STATIC and GLOBAL variables are stored in main memory, so their address remains constant for the entire program.

That's pretty much the way I remembered it too.  Basically, my conception was that statics are stored in the global data segment of the program.  I look at them as 'cheap' globals, and seldom if ever use them.  Now there's nothing wrong with either globals or statics; I just prefer for myself not to use them.  Rather, (and I love Chris Holbrooks's term here - 'Torture Variables') I store my data in Window Properties or .cbWndExtra bytes.

Having said that, I can still see the BC9 / BCx tools as being extremely valuable.  Thanks again for your work on this, as well as the original work of Kevin Diggins.


James C. Fuller

Fred,
  I also wondered about LOCAL so I sent an email to Kevin to explain his thought process (if he can remember as BCX started as  a PBDos app.)
I think by adding a command line parameter I can treat LOCAL the same as Raw.

I think I mentioned in the other thread that gcc will offer the actual type needed in a function call. If you are using a DWORD and it really wants a HWND it will tell you it wants a HWND.

James

Frederick J. Harris

Yes, to me its no big deal either way (statics vs. locals).  I didn't mean to make it seem bigger by harping on it.  Its just that it had been on my mind from Patrice's mention of it.  It could be easily fixed by Search And Replace in any case.

James C. Fuller

Fred,
  This is what Kevin said:
local variables dimensioned within SUB's & FUNCTION's are created on the stack and their allocated space is uninitialized.  Hence the need to zero their space.  In this context, DIM and LOCAL arre identical, at least, that's how it was when I was maintaining the code.
Meaning this Bcx code

LOCAL rv As integer


is translated to


static   int      rv;
  memset(&rv,0,sizeof(rv));


As my c is terrible my question to you is when would you ever NEED to zero out the memory on a local variable? I have never seen any "c" code where this is done?

James

Frederick J. Harris

#8
C never initializes anything as far as I know.  In fact, I don't believe C++ is any different whatsoever in that regard.  All they do is allocate the variables automatically on the stack (or in the data segment in the case of globals) as they are declared in the code.  So in other words, if you have this in C/C++ ...


void Foo(void)
{
int iNum1, iNum2, iNum3;
printf("%d\t%d\t%d\n", iNum1, iNum2, iNum3);
}


All you'll get are garbage values out.  In fact, you'll likely get piles or warning messages to the effect that iNum1, iNum2, and iNum3 have been used without being initialized.  So, all you have there is a case where the variables are completely valid, i.e., they've been properly declared and allocated, but the memory to which they access is indeterminate.  In other words, it will contain whatever was in memory when it was last used by this program or another.

Of course, no dialect of basic that I know of works like that.  This has bitten me many times in my switching back and forth between basic and C when I forget to set memory to 0.

So the way this works in C/C++ from a practical standpoint is that you don't absolutely have to always initialize a variable, but you have to be very careful about what you do.  Here would be a practical example.  Let's say you want to use a memory allocation function and you've declared a pointer for it like so...


void foo(void)
{
int* pMem;
pMem=(int*)GlobalAlloc(GPTR, 256);
}


Is that OK?  What we have there is a dangling pointer that actually may contain some number.  If you would print out pMem it might possibly have your birthday of the year in it; or maybe 12345.  Of course if you use it like that you'll crash.  But the GlobalAlloc() call will either give you a valid pointer or a zero return on failure.  So your're OK.  Of course, what one really should do is this ...


void foo(void)
{
int* pMem=(int*)GlobalAlloc(GPTR, 256);
...
}


And that nicely solves the problem.  However, let's look at another case (a bit more interesting and dangerous) that will involve all of our favorite subjects, i.e., COM.  Let's say we have this ...


void foo(void)
{
ISomeInterface* pInterface;
HRESULT hr;

QueryInterface(IID_SomeInterface, (void**)&pInterface);
if(pInterface)
{
    pInterface->MethodCall()
    pInterface->Release();
{
}


Any problems there?  You bet!  Note that we again have an auto stack allocated ISomeInterface* variable that, again, may contain some non-zero garbage value from whatever existed in memory there when the compiler allocated the variable.  And its used as an [out] param in the QueryInterface call where it may or may not be initialized.  And that's an interesting point there too.  QueryInterface() calls are implemented by folks like you and I who have good days and bad days.  Microsoft doesn't implement QueryInterface logic.  We do.  On a good day we'll likely remember to return E_NOINTERFACE on a IID the client passes in that we can't satisfy, but will we always remember to set the [out] pInterface** to NULL?    In that case the client will get a pInterface=0 which he can test, in addition to a FAILED HRESULT.  But on a bad day ... Who knows.  No guarantees!  See my point???  So in that case, let's say pInterface has a value of 123 when its created and that's just a garbage value that was in random memory.  And the QueryInterface call fails.  It'll still contain 123 (if the guy who wrote the code was having a bad day) and it'll crash the client right away, and that ridiculous if statement testing for zero won't help one little bit.  Interesting, huh?  So in that case I'd never consider creating a COM interface pointer without setting it to NULL on allocation like so ...


ISomeInterface* pInterface=NULL;


And the function using it should definitely test HRESULT values ...


void foo(void)
{
ISomeInterface* pInterface=NULL;
HRESULT hr;

hr=QueryInterface(IID_SomeInterface, (void**)&pInterface);
if(SUCCEEDED(hr))
{
    pInterface->MethodCall()
    pInterface->Release();
{
}


So getting back to translating PowerBASIC code to C/C++, or maybe even BC9 (I don't know all (or even any) of the rules there), if autos (automatic stack based variables) are created out of PowerBASIC locals, they need to be initialized to zero if the application's logic requires it.  And how can the translator know that?  It likely can't (or not easily).  So that would be my guess why Kevin seemed to pair up all allocations with memset()s to assure damage doesn't occur.  I know in my PowerBASIC coding I frequently rely on the compiler initializing a local to zero.  Only by looking close at a number of programming statements in the body of the procedure on a case by case basis would it be possible to determine whether it was/is necessary or not.  So I guess better safe than sorry.  Hope I've helped!

James C. Fuller

Fred,
  Thank you for the detailed reply.

  I understand the reasoning now and it is the same for other basic's I checked. Basic programmers expect var's to be
initialized to zero on start up. What I don't understand is why static is used for BCX?

LOCAL b As Integer

translates to this in "c" and "c++" using bcx 6.2.2

  static int      b;
  memset(&b,0,sizeof(b));


This  code compiles and runs just fine.

  int      b;
  memset(&b,0,sizeof(b));


I believe there is a bug in bc9 and later (after 6.2.2) versions of BCX when translated to cpp source for this construct.


Here is an interesting link on static.
http://gynvael.coldwind.pl/?id=406

James

Patrice Terrier

#10
I use very often this type of function to save properties, based on the used of STATIC variable, as an alternative use of GLOBAL.

'// Static function to save the main window handle.
FUNCTION zMainWindow(BYVAL hMain AS LONG) AS LONG
    STATIC WasMain AS LONG
    IF hMain THEN WasMain = hMain
    FUNCTION = WasMain
END FUNCTION


Using CALL zMainWindow(hMain) to save the handle the first time
then using hMain = zMainWindow(0) to retrieve the saved value.

Or other examples to save/retrieve DC/RC:
'//5.00
FUNCTION GetSetGLDC(BYVAL hDC AS LONG, BYVAL RW AS LONG) AS LONG
    STATIC WasDC AS LONG
    IF RW THEN WasDC = hDC
    FUNCTION = WasDC
END FUNCTION
FUNCTION GL_GLDC ALIAS "GL_GLDC" () EXPORT AS LONG
    FUNCTION = GetSetGLDC(0, 0)
END FUNCTION

'//5.00
FUNCTION GetSetGLRC(BYVAL hRC AS LONG, BYVAL RW AS LONG) AS LONG
    STATIC WasRC AS LONG
    IF RW THEN WasRC = hRC
    FUNCTION = WasRC
END FUNCTION
FUNCTION GL_GLRC ALIAS "GL_GLRC" () EXPORT AS LONG
    FUNCTION = GetSetGLRC(0, 0)
END FUNCTION
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Frederick J. Harris

Well I guess using memset() is a bit more generalized for a translater program than b=0;

But yes, using statics for locals seems exceedingly odd.  They are two pretty radically different kinds of beasts.  I mean, all pets are animals, but all animals don't make good pets. 

In terms of the static vs global vs local debate, I'll never forget when Jose asked me if I'd like a board on his forum.  That was in the summer of 2007.  At that time and previous for a couple years, I had been posting tutorial type material on SDK Api coding in the PowerBASIC Forums.  Since advanced coders such as those here already knew this material backwards and forwards and even in more detail than I, I saw my role as pretty much one where I'd attempt to bring other PowerBASIC coders not overly familiar with this material 'up to speed', so to say. 

In my Form2 tutorial (my 2nd one after the obligatory Hello, World! one, I wanted to discuss the Window Procedure, and I thought in that case a program that displayed mouse coordinates as one moved the mouse, likewise button clicks, key presses, window resizing, etc., would be a good tutorial idea.  Of course, this data comes in the WndProc() through the WndProc() parameters, and must be persisted across invocations of the Window Procedure so that the data can be displayed in a WM_PAINT handler.  Of course, Charles Petzold used C statics to do this throughout his famous books, and I never heard anyone run Petzold down because of it.  I did it in my first post of the Form2 tutorial here ...

Original Form2 Tutorial:
http://www.jose.it-berater.org/smfforum/index.php?topic=1254.0

The top couple lines of thw WndProc ...


Function WndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
  Static xCoord As Word, yCoord As Word, wWidth As Word, wHeight As Word, wCharHt As Word
  Static xPos As Word, yPos As Word
  Static szText As Asciiz*256
  ...
  ...


Upon reading that, Edwin Knoppert raked me over the coals rather severely for using global/static data, which he made no distinction between, regarding it as all evil and beneath good programmers....

http://www.jose.it-berater.org/smfforum/index.php?topic=1255.0

So upon reading that all I could think was that, "Fred, you're in the big leagues here, and folks in these circles take a dim view of global or static data, so you had better come into line and not use any."  And I do believe that might very well have been the last post I made here that had any program data persisted in my program's data segment.  But the thing is, I wanted my tutorials to help folks learn the material, and I didn't want to make it a prerequisite that they be experts with pointers and dynamic memory allocation.  I know there are a real lot of really experienced basic programmers out there who have been working with the language for a long time but weren't real familiar with pointers and allocating memory.  I didn't want to leave those behind.  So to satisfy Edwin I posted a redone Form2 I called Form2A and in that I created a UDT on the heap and stored that in the WNDCLASSEX::cbWndExtra bytes....

http://www.jose.it-berater.org/smfforum/index.php?topic=1252.0

I think it was in my Form6 tutorial I attempted to systematically discuss pointers to bring folks up to speed in their use of them to store program data in ways other than in the program's data segment. 

When I first began doing this myself (WNDCLASSEX:cbWndExtra bytes, Window Get/Set Properties) in the early 2000s I looked upon the practice as nothing more than a cute and tricky way to eliminate globals, which a lot of folks thought were bad.  Its really only been the last several years however when I was struck with the really big picture and significance of this practice, and that didn't really have anything to do with being cute or tricky to impress other programmers.  At the time I was giving a lot of thought to COM, OOP, C++, objects, and the Windows Api as designed by Microsoft.  And the realization came to me that all of us using the C based Windows Api were doing OOP; we just weren't recognizing it as such because we weren't familiar with what OOP looked like in C.  Most of us that had worked with other OOP based languages such as Visual Basic or C++ assummed it had to look exactly like that, and what we were seeing in the C based Api just didn't look quite 'right', whatever 'right' was - something like a C++ Class I guess. 

But the Windows Api was what OOP looked like in C.  CreateWindow() calls were basically a 'constructor' call on an object.  The OOP 'Class' that was being constructed was what was 'Registered' by RegisterClassEx().  That struct had primitive data members such as sizeof(), and also member function calls such as WNDCLASSES::lpfnWndProc().  All operations on the object that was the Window were carried out by member functions where the first parameter of the call was inevitably the hWnd, which is a virtual memory address and corresponds to the hidden 'this' pointer which every C++ member function of a class gets so as to be able to reference the object's memory instantiation and private data.  There is no difference here; this isn't a forced analogy.  All that the C++ new operator returns is a virtual memory handle to an object, and that is exactly what the CreateWindow() call returns too. 

So regarding storing a Window's data within the Window Class struct in .cbWndExtra bytes, or the alternate method of using Get/SetProp(), this wasn't just some tricky and convoluted way of coding; it was what OOP looked like in procedural/functional languages.  Chris Holbrook just recently termed these 'Torture Variables', because they make coding go slower.  Its harder.  Easiest thing is to make every variable a global like in GWBASIC.  Until your programs get to be 10,000 lines + of course! 

So having muddled my way through that sort of thought process, I began to see OOP implementations on top of the Windows Api as nothing more than one sort of OOP wrapper on a lower level OOP, that is, the C based OOP layer.  Everything was another layer of duplication/abstraction.  At that point it became clear to me that unless there was something a class framework could do that I didn't know how to do myself, or I didn't want to take the time to learn, I'd prefer to just use the Api exactly as it presented itself to me.  I wasn't ever satisfied to have the size of my executables quadruple in size just because I could now call their coding OOP based.

So that's my philosophy on locals, statics, globals, for what its worth.         

 

 

James C. Fuller

#12
Fred,
Ok I have updated bc9 to translate

LOCAL b As Integer


To


  int      b;
  memset(&b,0,sizeof(b));


Which seems to be the correct way.

It was suggested to drop memset and translate to:


int     b={0};


My c/c++ is not good; so what happens with types (structures) and char arrays?

bc9 basic

DIM xx$

I don't think these two do the same although they compile fine. I would assume only the first char in the array is 0
which is ok(?) for strings but would not be ok for types if only the first element is set to zero?

  char    xx[cSizeOfDefaultString];
  memset(&xx,0,sizeof(xx));

  char xx[cSizeOfDefaultString]={0};
 


I just did some experimentation and it appears char xx[cSizeOfDefaultString]={0} works fine!

James

James C. Fuller

Patrice,
  That seems like an awful lot of overhead for what gains?. As an encapsulation freak I admit I dabbled with that idea at one time but decided if I really needed a global I would just make sure to prefix the name with a "g_".

James

Frederick J. Harris

That all seems fine James.  I've been aware of this idiom for some time ...


#define SOME_SIZE     256
char szBuffer[SOME_SIZE]={};


... to zero out memory.  I've just never made any use of it, and just tried it out myself.  Where I had first seen it is on MSDN for a Hello, World! Sdk example, where they did this ...


WNDCLASSEX wc={};


... which zeros out the WNDCLASSEX struct.  So if you want to be real parsimonious with your initializations of that struct, you can come up with a real small Hello, World like so ...


#include <windows.h>

LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
if(msg==WM_DESTROY)
{
    PostQuitMessage(0);
    return 0;
}

return (DefWindowProc(hwnd, msg, wParam, lParam));
}


int WINAPI WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
char szClassName[]="Form1";
WNDCLASSEX wc={};
MSG messages;
HWND hWnd;

wc.lpszClassName   = szClassName;
wc.lpfnWndProc     = fnWndProc;
wc.cbSize          = sizeof (WNDCLASSEX);
wc.hInstance       = hIns;
RegisterClassEx(&wc);
hWnd=CreateWindowEx(0,szClassName,szClassName,WS_OVERLAPPEDWINDOW,100,100,350,300,HWND_DESKTOP,0,hIns,0);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
    TranslateMessage(&messages);
    DispatchMessage(&messages);
}

return messages.wParam;
}


That actually works!  I guess you could do the same in PowerBASIC, as PB zeros Types out (far as I know).

By the way, what is the corresponding syntax for zeroing out a Type in PowerBASIC?  I seem to recall its Type Set or something like that.  I was trying to remember that the other day, but couldn't.