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.30Its 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.