• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

Get prepared to move on.

Started by Patrice Terrier, January 11, 2013, 02:44:59 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Charles Pegge


The genius of the OLEstring is that is has both a null terminator and has a hidden length field (offset-4), so in many situations it can be used precisely as a null-terminated string - passing string data to the API, for instance.

Only two calls are essential:
SysAllocStringByteLen
SysFreeString

James Klutho

C's weakness in strings is the main reason I switched from C to Powerbasic around the year 2001.  I love dynamic strings and Powerbasic's trunk full of operations on them.  If C had better string handling I would probably still be there.

Frederick J. Harris

Quote
The genius of the OLEstring is that ...

And of course, the bad news is that, even so,  its still not very smart.  You can't concatenate them, call Parse() or InStr() on them, or do anything whatsoever in C or C++ like we do with PowerBASIC dynamic strings (whose underlying type is the OLEString). 

I know there has been a massive effort on this by C++ coders everywhere, so it would likely be a worthwhile goal to investigate some of that work out there.  On the other hand, I already have a decent string class of my own that I might be able to modify so as to incorporate BSTRs instead of my new[] allocated buffers.     

Frederick J. Harris

Discussion of this (my favorite topic, other than COM - Strings!) caused me to lookup a fun old program of mine.  Its a little GUI program that exercises my C++ string class using many of the customary PowerBASIC coding calls we so love such as InStr(), Left(), Right(), Mid(), ParseCount(), Parse, concatenation, etc.  Of course, its in C++.  The header for the string class looks like this ...


//Strings.h
#if !defined(STRINGS_H)
#define STRINGS_H
#define EXPANSION_FACTOR      2
#define MINIMUM_ALLOCATION   16

class String
{
public:
friend String operator+(TCHAR*, String&);
String();                                    //Uninitialized Constructor
String(const TCHAR);                         //Constructor Initializes With A TCHAR.
String(const TCHAR*);                        //Constructor Initializes String With TCHAR*
String(const String&);                       //Constructor Initializes String With Another String (Copy Constructor)
String(const int, bool);                     //Constructor Creates String With User Specified Capacity and optionally nulls out
String(const int, const TCHAR);              //Constructor initializes String with int # of TCHARs
String(int);                                 //Constructor initializes String with int converted to String
String(unsigned int);                        //Constructor initializes String with unsigned int converted to String
String(double);                              //Constructor initializes String with double converted to String
String& operator=(const TCHAR);              //Assign A TCHAR To A String
String& operator=(const TCHAR*);             //Assign A Null Terminated TCHARacter Array To A String
String& operator=(const String&);            //Assigns Another String To this One
String& operator=(int iNum);                 //Assigns an unsigned int to a String
String& operator=(unsigned int iNum);        //Assigns an unsigned int to a String
String& operator=(double dblNum);            //Assign a double to a String
String operator+(const TCHAR);               //For adding TCHAR to String
String operator+(const TCHAR*);              //Adds a TCHAR* to this
String operator+(String&);                   //Adds another String to this
String& operator+=(const TCHAR ch);          //Add TCHAR to this
String& operator+=(const String&);           //Adds a String to this and assigns it to left of equal sign
String& operator+=(const TCHAR*);            //Adds a TCHAR*to this and assigns it to left of equal sign
bool operator==(String&);                    //Compares Strings For Case Sensitive Equality
bool operator==(const TCHAR*);               //Compares String Against TCHAR* For Case Sensitive Equality
String& Make(const TCHAR ch, int iCount);    //Returns reference to this with iCount ch TCHARs in it
String Left(int);                            //Returns String of iNum Left Most TTCHARs of this
String Right(int);                           //Returns String of iNum Right Most TTCHARs of this
String Mid(int, int);                        //Returns String consisting of number of TTCHARs from some offset
String Replace(TCHAR*, TCHAR*);              //Returns String with 1st TCHAR* parameter replaced with 2nd TCHAR* parameter
String Remove(TCHAR*);                       //Returns A String With All The TCHARs In A TCHAR* Removed (Individual TCHAR removal)
String Remove(const TCHAR*, bool);           //Returns a String with 1st parameter removed.  2nd is bool for case sensitivity.
int InStr(const TCHAR*, bool);               //Returns one based offset of a particular TTCHAR pStr in a String
int InStr(const String&, bool);              //Returns one based offset of where a particular String is in another String
int ParseCount(const TCHAR);                 //Returns count of Strings delimited by a TTCHAR passed as a parameter
void Parse(String*, TCHAR);                  //Returns array of Strings in first parameter as delimited by 2nd TTCHAR delimiter
void SetTCHAR(int, TCHAR);                   //Sets TCHAR at zero based offset in this
void LTrim();                                //Returns String with leading spaces/tabs removed
void RTrim();                                //Returns String with spaces/tabs removed from end
void Trim();                                 //Returns String with both leading and trailing whitespace removed
int iVal();                                  //Returns integral value of String
int Len();                                   //Returns Length Of String Controlled By this
int Capacity();                              //Returns Maximum Permissable TCHARacter Count (One Less Than Allocation).
TCHAR* lpStr();                              //Returns TCHAR* To String
void Print(bool);                            //Outputs String To Console With Or Without CrLf.
~String();                                   //String Destructor

private:
TCHAR* lpBuffer;
int   iLen;
int   iCapacity;
};

String operator+(TCHAR* lhs, String& rhs);
#endif  //#if !defined(STRINGS_H)


From the 'private' data members of the class, you can see I catch the pointer to the underlying string buffer (this->lpBuffer), the length of the string presently in it (this->iLen), and the present capacity of the allocated buffer (this->iCapacity).  This is pretty much standard practice.  Its also designed to have a MINIMUM_ALLOCATION and an EXPANSION_FACTOR.  These come into play to facilitate rapid string concatenations.  It works with the TCHAR macros, so can run either ansi or wide character.

For anyone wishing to test this demo, the String.cpp file is here on Jose's site in Post #42 here ...

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

Its the 3rd file in my Post #42.

Here is a fun little program exercising the string class ...


//Main.cpp
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include "StrDemo.h"
#include "frmOutput.h"
#include "Strings.h"


long fnWndProc_OnCreate(lpWndEventArgs Wea)
{
TCHAR szClassName[]=_T("frmOutput");
WNDCLASSEX wc;
HWND hCtrl;

Wea->hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
wc.lpszClassName=szClassName;                          wc.lpfnWndProc=frmOutput;
wc.cbSize=sizeof (WNDCLASSEX);                         wc.style=CS_DBLCLKS;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);               wc.hInstance=Wea->hIns;
wc.hIconSm=NULL;                                       wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=0;
wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;
RegisterClassEx(&wc);
hCtrl=CreateWindow(_T("button"),_T("String Concatenation"),WS_CHILD|WS_VISIBLE,45,20,250,25,Wea->hWnd,(HMENU)IDC_CONCATENATION,Wea->hIns,0);
hCtrl=CreateWindow(_T("button"),_T("String Parsing And Trimming"),WS_CHILD|WS_VISIBLE,45,55,250,25,Wea->hWnd,(HMENU)IDC_PARSE,Wea->hIns,0);
hCtrl=CreateWindow(_T("button"),_T("Good Old Left, Right, And Mid!"),WS_CHILD|WS_VISIBLE,45,90,250,25,Wea->hWnd,(HMENU)IDC_LEFT_RIGHT_MID,Wea->hIns,0);
hCtrl=CreateWindow(_T("button"),_T("But Led's Not Forget InStr( )!"),WS_CHILD|WS_VISIBLE,45,125,250,25,Wea->hWnd,(HMENU)IDC_INSTR,Wea->hIns,0);

return 0;
}


void btnConcatenation_OnClick(void)
{
HWND hOutput;
String s;
int iY=0;
HDC hDC;

hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,450,20,305,480,0,0,GetModuleHandle(0),0);
ShowWindow(hOutput,SW_SHOWNORMAL);
hDC=GetDC(hOutput);
for(unsigned int i=65; i<91; i++)
{
     s=s+i;                                   //65 Is Capital 'A'
     TextOut(hDC,0,iY,s.lpStr(),s.Len());     //TextOut() is the Windows Version Of Basic's 'Print'
     iY=iY+16;                                //A Spacing Of 16 Pixels Works Good For Most Normal Sized Fonts
}
ReleaseDC(hOutput,hDC);
}


void btnParse_OnClick(void)
{
String* ptrStrings;
unsigned int iCt;
HWND hOutput;
String s;
int iY=0;
HDC hDC;

hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,50,400,310,180,0,0,GetModuleHandle(0),0);
ShowWindow(hOutput,SW_SHOWNORMAL);
hDC=GetDC(hOutput);
s=_T("Frederick,  Mary,  James,  Samuel,  Edward,  Richard,  Michael,  Joseph");  //Create comma delimited text string
iCt=s.ParseCount(_T(','));                                                        //Count delimited substrings
ptrStrings = new String[iCt];                                                     //Create array of String Pointers
s.Parse(ptrStrings,_T(','));                                                      //Parse 's' based on comma delimiter
for(unsigned int i=0; i<iCt; i++)
{
     ptrStrings[i].LTrim();  //Comment This Out To See Effect.                     //There are some spaces in delimited strings so remove them
     TextOut(hDC,0,iY,ptrStrings[i].lpStr(),ptrStrings[i].Len());                  //Output/Print array of strings one at a time
     iY=iY+16;
}
delete [] ptrStrings;
ReleaseDC(hOutput,hDC);
}


void btnLeftRightMid_OnClick(void)
{
HWND hOutput;
String s1,s2;
HDC hDC;

hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,800,100,325,250,0,0,GetModuleHandle(0),0);
ShowWindow(hOutput,SW_SHOWNORMAL);
hDC=GetDC(hOutput);
s1=_T("George Washington Carver");
s2=s1.Left(6);
TextOut(hDC,20,20,s1.lpStr(),s1.Len());
TextOut(hDC,20,40,s2.lpStr(),s2.Len());
s2=s1.Mid(8,10);
TextOut(hDC,20,60,s2.lpStr(),s2.Len());
s2=s1.Right(6);
TextOut(hDC,20,80,s2.lpStr(),s2.Len());
ReleaseDC(hOutput,hDC);
}


void btnInStr_OnClick(void)
{
String s1,s3;
double dblPi;
HWND hOutput;
HDC hDC;

hOutput=CreateWindow(_T("frmOutput"),_T("String Class Minipulations In C++"),WS_OVERLAPPEDWINDOW,550,575,525,200,0,0,GetModuleHandle(0),0);
ShowWindow(hOutput,SW_SHOWNORMAL);
hDC=GetDC(hOutput);
s1=_T("InStr('Some Text') Locates The One Based Offset Of Where Something Is At.");
TextOut(hDC,0,0,s1.lpStr(),s1.Len());
s1=_T("C++ Can Be A Real Pain In The Butt To Learn!");
TextOut(hDC,0,16,s1.lpStr(),s1.Len());
String s2(s1.InStr(_T("real"),false));
s3 =_T("The Offset Of 'real' In The Above String Is ");
s3 = s3 + s2 + _T('.');
TextOut(hDC,0,32,s3.lpStr(),s3.Len());
s1=_T("Note In The Above Code We Used The Case Insensitive Version Of InStr().");
TextOut(hDC,0,48,s1.lpStr(),s1.Len());
s1=_T("Let's See If We Can Remove() 'Real' From Our 1st String!");
TextOut(hDC,0,64,s1.lpStr(),s1.Len());
s1=_T("C++ Can Be A Real Pain In The Butt To Learn!");
TextOut(hDC,0,80,s1.lpStr(),s1.Len());
s2=s1.Remove(_T("Real"),true);
TextOut(hDC,0,96,s2.lpStr(),s2.Len());
s1=_T("Dieses Program funktioniert aber sehr gut, nicht wahr?");
TextOut(hDC,0,112,s1.lpStr(),s1.Len());
dblPi=3.14159;
s1=_T("dblPi = ");
s2=dblPi;
s1=s1+s2;
TextOut(hDC,0,128,s1.lpStr(),s1.Len());
ReleaseDC(hOutput,hDC);
}


long fnWndProc_OnCommand(lpWndEventArgs Wea)
{
switch(LOWORD(Wea->wParam))
{
  case IDC_CONCATENATION:
    btnConcatenation_OnClick();
    break;
  case IDC_PARSE:
    btnParse_OnClick();
    break;
  case IDC_LEFT_RIGHT_MID:
    btnLeftRightMid_OnClick();
    break;
  case IDC_INSTR:
    btnInStr_OnClick();
    break;
}

return 0;
}


long fnWndProc_OnDestroy(lpWndEventArgs Wea)
{
PostQuitMessage(0);
return 0;
}


LRESULT CALLBACK fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
WindowsEventArguments Wea;

for(unsigned int i=0; i<dim(EventHandler); i++)
{
     if(EventHandler[i].iMsg==msg)
     {
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;
        return (*EventHandler[i].fnPtr)(&Wea);
     }
}

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


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

wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
wc.cbSize=sizeof (WNDCLASSEX);               wc.style=0;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
wc.hIconSm=0,                                wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
RegisterClassEx(&wc);
hWnd=CreateWindow(szClassName,_T("StrDemo"),WS_OVERLAPPEDWINDOW,20,20,350,210,HWND_DESKTOP,0,hIns,0);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
    TranslateMessage(&messages);
    DispatchMessage(&messages);
}

return messages.wParam;
}



//frmOutput.cpp  All this file consists of is the Window Procedure for the frmOutput
//Window Class.  A window of class frmOutput is created whose sole purpose is to be
//printed to with TextOut().  All default window procedure processing is ok so the
//window procedure frmOutput doesn't have to do anything on its own other than exist
//and pass all messages received onto DefWindowProc().  It has to exist because its
//referenced in the Class Registration code in fnWndProc_OnCreate() where the frmOutput
//class is registered. 
#include <windows.h>
#include "frmOutput.h"

long __stdcall frmOutput(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
return (DefWindowProc(hwnd, msg, wParam, lParam));
}




//StrDemo.h
#ifndef StrDemo_h
#define StrDemo_h

#define IDC_CONCATENATION       1500
#define IDC_PARSE               1505
#define IDC_LEFT_RIGHT_MID      1510
#define IDC_INSTR               1515

#define dim(x) (sizeof(x) / sizeof(x[0]))

struct                          WindowsEventArguments
{
HWND                           hWnd;
WPARAM                         wParam;
LPARAM                         lParam;
HINSTANCE                      hIns;
};

typedef WindowsEventArguments*  lpWndEventArgs;

long fnWndProc_OnCreate         (lpWndEventArgs Wea);
long fnWndProc_OnCommand        (lpWndEventArgs Wea);
long fnWndProc_OnDestroy        (lpWndEventArgs Wea);

void btnConcatenation_OnClick   (void);
void btnParse_OnClick           (void);
void btnLeftRightMid_OnClick    (void);
void btnInStr_OnClick           (void);

struct EVENTHANDLER
{
unsigned int                   iMsg;
long                           (*fnPtr)(lpWndEventArgs);
};

const EVENTHANDLER EventHandler[]=
{
{WM_CREATE,                    fnWndProc_OnCreate},
{WM_COMMAND,                   fnWndProc_OnCommand},
{WM_DESTROY,                   fnWndProc_OnDestroy}
};

#endif



//frmOutput.h
long __stdcall frmOutput(HWND, unsigned int, WPARAM, LPARAM);


What the program does is create a little window with I believe five buttons on it.  Each button says what string functions its going to exercise.  When you click a button it outputs to another window various strings it minipulated in some way.  Just tested it with Code::Blocks and MinGW.  Should work OK.

Patrice Terrier

#49
Jim--

QuotePatrice, do you think you'll end up with a "PowerBASIC to C" translator after all your work???
Or are you just doing it by hand?

I thought about writing my own translator, but for now i am doing everything by hand, because i am still learning most of the C syntax, thus i have to do much try before i could get the good syntax, and many of the PowerBASIC keywords need to be rewriten/translated from ground zero.

The nastiest thing i am faced with, is the strong typing of C, making hard to use "long" or "dword" like in PB.
For example: HANDLE, HDC, HBITMAP, HWND, etc. are all "long/dword" but they could not be intermixed together, because of the way they are declared in the SDK headers.

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

James C. Fuller

Fred,
  What is your main problem with c++ STL strings?
I wrote wrapper functions for a number  of the "BASIC "functions  (mid,left,right,trim....) for one of my first forks of BCX.

James

Theo Gottwald

Charles, I'd prefer to have a PB compatible Compiler that can generate x64.
How long do you think would it take if you'd make one?  ;D

Patrice Terrier

#52
Theo--

Translating 32-bit to 64-bit envolved much changes, especially when you have to deal with memory offset.

And some constants are not valid anymore, for example GWL_WNDPROC becomes GWLP_WNDPROC, much headache in perspective before you could get a 64-bit code to work (even if it compiles).

Rules for Using Pointers.

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

Theo Gottwald

I trust in Charles. He knows that already.

If you want, test his 64 bit compiler.

He must just change the syntax to be PB compatible, then a LOT may already compile.

Frederick J. Harris

That PureBasic compiler sure looks like a good fit to me.  I'm staying with PowerBASIC myself, as I don't have any need for 64 bit yet, but different times I've thought of buying it just to play with like Theo did.  From what Patrice said, its developed by a team of programmers instead of just one, which is an advantage, and it does do 64 bit apparently. 

I guess my main gripes about STL James are the bloated executables.  And that's just me.  I know in the grand scheme of things it isn't important to anybody but me.  I suppose I measure everything against the small size, power, and performance of C or PowerBASIC code.   

 

Patrice Terrier

#55
QuotePatrice said, its developed by a team of programmers instead of just one

No, Patrice didn't said that, i said you will swap Bob Zale with Frédéric Laboureur (main programmer), meaning this could lead to the same issue sooner or later.

And about Charles, with all my respect, that would be probably the same issue.

On the other hand, i couldn't see how C/c++ would disapeared.

Also, speaking for myself, there is no comparison between the C/C++ addon market, and the small PB's one, i can make already the comparison with PB and WinDev, doing 90% of my business with PC-Soft.


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

Frederick J. Harris

I see what you mean Patrice.  Today I actually spent a little time examining the situation with PureBasic, and I reread Theo's detailed writeup once again, which I read when he first posted it, but I  had forgotten the details.  I'd be really 'put off' by the fact that the language doesn't require code to be within a procedure (like PB DOS, and all older versions of BASIC).  In fact, I'd reject it out of hand for that reason alone.  That may be unfair, I admit, but its just me.  I realize if one would spend a lot of time writting assembler one might come to an acceptance of that as not anything significant, a matter of form over substance, so to speask, but I've been with more modern highly structured languages long enough that I wouldn't be able to get past that one.

I recall a discussion Charles and I had a long time ago about FreeBasic.  I learned from him that was the situation there too.  That's why I didn't persue any further investigation of FreeBasic.  I'm beginning to wonder if PowerBASIC is the only version of Basic that follows the more modern convention of requiring all code to be within function blocks?  I believe your Bcx doesn't allow code lines scattered about does it James?

James C. Fuller

Quote from: Frederick J. Harris on January 17, 2013, 11:23:51 PM
I believe your Bcx doesn't allow code lines scattered about does it James?

As a matter of fact it does but it's not just hanging out there!
Any code not in a function is stuffed into main or WinMain.

James

Charles Pegge


It is quite feasible to create a PowerBasic emulation/front-end with Oxygen, since unlike C, it has the essential core constructs, including dynamic string expressions, and COM object support.

I provide all the source code for Oxygen without licensing restrictions, and most of it is written in Basic. I trust this substantially addresses the problem of vendor lock-in :)

However, PowerBasic has a much much larger set  of commands and functions. To build these would be a major undertaking requiring some fairly intensive team work. It is not an quick fix.

The full PB emulatior option would be worth considering if PB were to become moribund, without prospect of further progress. But I hope the PowerBasic developers will be able to build upon Bob's work and take PB forward in a timely manner. This is how it should be.

Patrice Terrier

Charles,

QuoteTo build these would be a major undertaking requiring some fairly intensive team work. It is not an quick fix.

In the long run, i have never seen an intensive team work, going to full completion without some kind of financial support.

But of course dreaming, doesn't cost anything  :)

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