• Welcome to Jose's Read Only Forum 2023.
 

Address UNICODE 64-bit VS2015

Started by Patrice Terrier, January 18, 2017, 12:35:55 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

James C. Fuller

Fred,
  My lack of c++ knowledge or maybe it can not be done with templates??
I want to use a aedynarray pointer but I don't know how to access the items.
My version of aedynarray.h  is attached where I removed all the throws along with this source.
Everything seems to work fine but I don't know how to get at the saved data?

James


#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif

#include <windows.h>
#define   x64
#include "TCLib\stdio.h"
#include "TCLib\string.h"
#include "TCLib\stdlib.h"
#include "TCLib\memory.h"
#include "TCLib\malloc.h"
#include "TCLib\math.h"
#include "TCLib\tchar.h"
#include "TCLib\Strings.cpp"
typedef String fstring;
// Fred you may need to comment these //
FILE* stdin;
FILE* stdout;
FILE* stderr;
// My version with all the throws removed
#include "aedynarray.h"

void    Pause (void);

struct AddressType
{
    _TCHAR Company[64];
    _TCHAR LastName[32];
    _TCHAR FirstName[32];
    _TCHAR Address1[64];
    _TCHAR Address2[64];
    _TCHAR Address3[64];
    _TCHAR CityName[24];
    _TCHAR StateName[4];
    _TCHAR ZipCode[12];
    long Country;
    _TCHAR Phone[24];
    _TCHAR Fax[24];
    _TCHAR Email[64];
    _TCHAR Url[64];
    _TCHAR Comments[1024];
};

int     _tmain (void);

void Pause(void)
{
    _tprintf(_T("\n%ls\n"), _T("Press any key to continue..."));
    _getwch();
}


int _tmain ()
{
    AeDynArray<AddressType> AddArray;
    AeDynArray<AddressType>*AddArrayPtr = new AeDynArray<AddressType>;
    int      i = {0};
    AddressType  Address;
    fstring  fs ;
    for(i = 1; i <= 10; i += 1)
    {
        fs = _T("MyCompany#") + fstring( i);
        _tcscpy(Address.Company, fs.lpStr());
        fs = _T("Last#") + fstring( i);
        _tcscpy(Address.LastName, fs.lpStr());
        fs = _T("First#") + fstring( i);
        _tcscpy(Address.FirstName, fs.lpStr());
        fs = _T("Add#") + fstring( i) + _T("1");
        _tcscpy(Address.Address1, fs.lpStr());
        fs = _T("Add#") + fstring( i) + _T("2");
        _tcscpy(Address.Address2, fs.lpStr());
        fs = _T("Add#") + fstring( i) + _T("3");
        _tcscpy(Address.Address3, fs.lpStr());
        fs = _T("City#") + fstring( i);
        _tcscpy(Address.CityName, fs.lpStr());
        fs = _T("NY#") + fstring( i);
        _tcscpy(Address.StateName, fs.lpStr());
        _tcscpy(Address.ZipCode, _T("12828"));
        Address.Country = 1;
        _tcscpy(Address.Phone, _T("111-111-1111"));
        _tcscpy(Address.Fax, _T("222-222-2222"));
        fs = _T("Hello@Me.com#") + fstring( i);
        _tcscpy(Address.Email, fs.lpStr());
        fs = _T("Url#") + fstring( i);
        _tcscpy(Address.Url, fs.lpStr());
        fs = _T("Comment#") + fstring( i);
        _tcscpy(Address.Comments, fs.lpStr());
        AddArray.Add(Address);
        AddArrayPtr->Add(Address);
    }

    _tprintf(_T("%ls\n"), AddArray[4].Company);
    // ???? how to access records using AddArrayPtr ????
   
    delete AddArrayPtr;
    Pause();
}


Frederick J. Harris

I'd have thought...


AddArrayPtr[4]->Address


... but I tried it and it doesn't work.  Also tried a few other things too without any luck.  So I'm sorry to say I don't know how to do it.

Do you really need that construction?  I mean, within the array constructor I see a constant of 128 which is the starting number of objects of whatever type is being fed into the array.  Each of your AddressType structures is like 3128 bytes or something like that, so you are getting about 400,000 bytes of them constructed dynamically just by your initial declaration. 

Just occurred to me perhaps there is some kind of bizarre special syntax when referring to pointers to templated objects.  Not sure. 

James C. Fuller

Fred,
  Thanks for trying.
I was playing with that earlier while trying to overcome the no globals and got frustrated when I couldn't access the data!!
This works and shows it does have data ....
    AddArray.Clear();
    AddArray = *AddArrayPtr;
    _tprintf(_T("%ls\n"), AddArray[4].Company);

James

Frederick J. Harris

Yea, I got your top one to work, but not the one with a pointer to a templated array. 

It got me to wondering though.  I was wondering if I could do it with my array class and I couldn't either.  Don't know what's going on there.

Thing is, I've persisted array type data in heap memory before - just never my templated array class.  Unfinished business.  Will have to tuck that one away for future pondering and solution perhaps.

You know though, I get the feeling you are working on that Address PB Demo?  If so, why bother attempting to store the program's data file in an array?  When I did my version of it, I just read one record at a time out of the random access file.

Which brings up a philosophical issue (Oh No!).  I seldom store program data in memory if there is a file source somewhere.  I'll tell you why I don't.  I worked so long with the handheld data recorder situation, that it affected the way I do things.  One of the biggest issues one faces when creating data entry applications for handhelds is data loss.  These things run on batteries that could fail at any moment.  Imagine this scenerio.  You've got a crew of folks out in the woods tallying timber.  Maybe one person as the tally man and three four or five others hollering out trees to him, which he's entering.  This has been going on all day and maybe 1200 Black cherry trees have been entered worth maybe a half million dollars.  You've created a program that stores all this data in a big array of structs that dynamically resizes itself as the number of records grow.  But its all in memory.  Then at the end of the day the battery dies.  Instead of suspending the machine just dies, kind of like you tripped over the cord where you PC was plugged in and pulled the cord out of the socket.  Everything's gone! 

To prevent such a situation I code everything to just have one record at a time in memory.  Every time a record is finished I write it to disk, clear out my screen and variables, and wait for the next entry.  Folks can navigate back and forth through the data file like in that Address example, but I never store more than one record in memory.

This is actually something that taxed me hard.  Getting the data collectors used for the first time 20 years ago was tough.  It wasn't the same world we're living in now.  At that time there were a lot of older foresters around who weren't real computer literate.  People didn't walk around with their faces stuck in iPads and iPhones like now.  Everybody figurred they would lose their data if they used the data collectors.  So I had to give it tremendous thought and work.

At the time I was using QuickBasic 4.5.  What I found out when I was developing the software was that even though I was writing each record to disk as the data was entered, it was really getting stored in a write buffer awaiting transfer to disk.  The write buffer could be something like 4096 bytes.  What the operating system would do was empty the whole write buffer to disk at infrequent intervals.  Each of my Tree records was only 16 bytes.  So thee could be a lot of tree data lost if the data recorder locked up or lost power or whatever.  What I had to do was use a utility I believe was named smartdrive.exe or something like that to force the operating system to immediately empty the buffer to disk after each tree was entered.

When I picked up PowerBASIC 3.5 and started using that I learned it had a Flush command to do what I was using smartdrive for.  So that helped.

I had a little demonstration I used to give people to get them convinced to start using the data recorders.  The data recorders we were using had a capacitor in them that kept memory charged for like 8 to 10 seconds in case of changing a battery.  I'd open up my program and start entering a tree while I had somebody's attention who was skeptical.  Then in the middle of that I'd open up the battery compartment on the data collector and pop the battery out while my program was in the middle of a tree entry!  Then, while they were watching I'd put the battery back in and show them that their tree was still there!  The capacitor prevented the loss of a tree that wasn't even written to disk yet.

Thought you might be interested in that.

James C. Fuller

Patrice,
  Is your printing code supposed to work?
Ir doesn't here.

James

James C. Fuller

Quote from: Frederick J. Harris on January 21, 2017, 10:48:20 PM
You know though, I get the feeling you are working on that Address PB Demo?  If so, why bother attempting to store the program's data file in an array?  When I did my version of it, I just read one record at a time out of the random access file.

Fred,
  Yes I am, but I want to do it as close to the PB Demo as possible. I'd probably use sqlite.
The next thing to tackle is printing. Got any wrapper solutions stashed away somewhere?

James

Frederick J. Harris

Quote
Got any wrapper solutions stashed away somewhere?

No, just what I posted when I did my Address Demo.  To tell the truth, I'd never worked with the printer Apis.  Sad but true.  I cobbled together what I did for that Address Demo from Petzold's old books.  That was my first excursion into printer code.

James C. Fuller

Fred,
  Where is your address demo. I looked on the board but....

James

Frederick J. Harris

Let me see if I can find that.  I never did a C++ version.  It was over in the PowerBASIC SDK Forum right after it started.  Be back in a minute.  In any case, I can easy find the routines on my computers.  Be back in a minute...

Frederick J. Harris

Geez!  How time flies!  All that was about 2 years and two months ago!

Here is my long winded post with quite a few versions, I think culminating at the end about here...

https://forum.powerbasic.com/forum/user-to-user-discussions/sdk-programming/59992-sdk-version-of-ddt-address-sample/page3

I believe all my printer code was encapsulated in this function which was an adaptation out of Petzold's Win 95 and in98/2000 book...


Function blnPrintAddress(Wea As WndEventArgs) As Long
  Local iRecNum,iNumRecs,xPage,yPage,xChar,yChar,iLineCount,iCharsCopied As Long
  Local hPrintDC,dwBytes,hCtl,hFont,hTmp,dwWord,hFile As Dword
  Local addtyp As AddressType
  Local szBuffer As ZStr*1024
  Local MyDocInfo As DOCINFO
  Local pDword As Dword Ptr
  Local tm As TEXTMETRIC
  Register i As Long
  Local rc As RECT

  dwBytes=128
  If GetDefaultPrinter(szBuffer,dwBytes) Then
     hPrintDC=CreateDC(Byval %NULL,szBuffer,Byval %NULL,Byval %NULL)
     If hPrintDC Then
        xPage = GetDeviceCaps(hPrintDC, %HORZRES)
        yPage = GetDeviceCaps(hPrintDC, %VERTRES)
        szBuffer=""
        hCtl=GetDlgItem(Wea.hWnd,%ID_INDEX)
        Call GetWindowText(hCtl,szBuffer,128)
        Call GetRecordData(Wea.hWnd,iRecNum,iNumRecs)
        hFile=CreateFile($Data_File,%GENERIC_READ,ByVal %NULL,ByVal %NULL,%OPEN_ALWAYS,%FILE_FLAG_RANDOM_ACCESS,ByVal %NULL)
        If hFile<>%INVALID_HANDLE_VALUE Then
           blnGet(hFile, iRecNum, Varptr(addtyp), sizeof(addtyp))
           Call CloseHandle(hFile)
        Else
           Function = %False : Exit Function
        End If   
        MyDocInfo.cbSize=sizeof(DOCINFO)
        MyDocInfo.lpszDocName=%NULL
        If StartDoc(hPrintDC,MyDocInfo)>0 Then
           If StartPage(hPrintDC)>0 Then
              hFont=CreateFont(-1*(15*GetDeviceCaps(hPrintDC,%LOGPIXELSY))/72,0,0,0,%FW_HEAVY,0,%TRUE,0,0,0,0,0,0,"Ariel")
              hTmp=SelectObject(hPrintDC,hFont)
              Call GetTextMetrics(hPrintDC, tm)
              yChar = tm.tmHeight + tm.tmExternalLeading
              xChar = tm.tmAveCharWidth
              rc.top  = 200 : rc.bottom = 200+yChar
              rc.left = 0   : rc.right  = xPage
              szBuffer="Address Book, Record " & szBuffer
              Call DrawText(hPrintDC,szBuffer,Len(Trim$(szBuffer)),rc,%DT_CENTER)
              Call DeleteObject(SelectObject(hPrintDC,hTmp))
              hFont=CreateFont(-1*(11*GetDeviceCaps(hPrintDC,%LOGPIXELSY))/72,0,0,0,%FW_HEAVY,0,0,0,0,0,0,0,0,"Ariel")
              hTmp=SelectObject(hPrintDC,hFont)
              Call GetTextMetrics(hPrintDC, tm)
              yChar = tm.tmHeight + tm.tmExternalLeading + 20
              xChar = tm.tmAveCharWidth
              TextOut(hPrintDC,300,600,"Company:",8)
              TextOut(hPrintDC,300,600+1*yChar,"Name:",5)
              TextOut(hPrintDC,300,600+3*yChar,"Address:",8)
              TextOut(hPrintDC,300,600+4*yChar,"City:",5)
              TextOut(hPrintDC,300,600+5*yChar,"State/Prov:",11)
              TextOut(hPrintDC,300,600+6*yChar,"Zip/Postal:",11)
              TextOut(hPrintDC,300,600+7*yChar,"Country:",8)
              TextOut(hPrintDC,300,600+9*yChar,"Phone:",6)
              TextOut(hPrintDC,300,600+10*yChar,"Fax:",4)
              TextOut(hPrintDC,300,600+11*yChar,"Email:",6)
              TextOut(hPrintDC,300,600+12*yChar,"Url:",4)
              TextOut(hPrintDC,300,600+14*yChar,"Comments:",9)
              Call DeleteObject(SelectObject(hPrintDC,hTmp))
              szBuffer=addtyp.Company                                           : TextOut(hPrintDC,1000,600,szBuffer,Len(RTrim$(addtyp.Company)))
              szBuffer=RTrim$(addtyp.FirstName) & " " & RTrim$(addtyp.LastName) : TextOut(hPrintDC,1000,600+1*yChar,szBuffer,Len(szBuffer))
              szBuffer=RTrim$(addtyp.Address1)                                  : TextOut(hPrintDC,1000,600+3*yChar,szBuffer,Len(szBuffer))
              szBuffer=RTrim$(addtyp.CityName)                                  : TextOut(hPrintDC,1000,600+4*yChar,szBuffer,Len(szBuffer))
              szBuffer=RTrim$(addtyp.StateName)                                 : TextOut(hPrintDC,1000,600+5*yChar,szBuffer,Len(szBuffer))
              szBuffer=RTrim$(addtyp.ZipCode)                                   : TextOut(hPrintDC,1000,600+6*yChar,szBuffer,Len(szBuffer))
              hCtl=GetDlgItem(Wea.hWnd,%ID_COUNTRY)
              Call GetWindowText(hCtl,szBuffer,128)
              TextOut(hPrintDC,1000,600+7*yChar,szBuffer,Len(szBuffer))
              szBuffer=RTrim$(addtyp.Phone)                                     : TextOut(hPrintDC,1000,600+9*yChar,szBuffer,Len(szBuffer))
              szBuffer=RTrim$(addtyp.Fax)                                       : TextOut(hPrintDC,1000,600+10*yChar,szBuffer,Len(szBuffer))
              szBuffer=RTrim$(addtyp.Email)                                     : TextOut(hPrintDC,1000,600+11*yChar,szBuffer,Len(szBuffer))
              szBuffer=RTrim$(addtyp.Url)                                       : TextOut(hPrintDC,1000,600+12*yChar,szBuffer,Len(szBuffer))
              hCtl=GetDlgItem(Wea.hWnd,%ID_COMMENTS)
              iLineCount=SendMessage(hCtl,%EM_GETLINECOUNT,0,0)
              pDword=Varptr(szBuffer) : dwWord=Mak(Dword, &HFF,0)
              For i=0 To iLineCount -1
                @pDword=dwWord
                iCharsCopied=SendMessage(hCtl,%EM_GETLINE,i,Byval Varptr(szBuffer))
                TextOut(hPrintDC,1000,600+(15+i)*yChar,szBuffer,iCharsCopied)
              Next i
              If EndPage(hPrintDC)>0 Then
                 Call EndDoc(hPrintDC)
              End If
           End If
        End If
        Call DeleteDC(hPrintDC)
        Function = %True  : Exit Function
     Else
        Function = %False : Exit Function
     End If
  Else
     Function    = %False : Exit Function
  End If
End Function


Frederick J. Harris

Past two days I've had dynamic arrays and vectors on my mind.  I still need to return to your pointer to the templated array and find out what's going on there.

Had been thinking about my CArray templated class, and I changed it to be in conformance with the way PowerBASIC handles arrays in terms of UBound() returning the highest accessable element, as opposed to the way I had it up to now with the C ism of returning the count of elements, which means the last valid element is one less than what UBound returns.  I think I like PowerBASIC's implementation better, in that its more usable and intuitive.

Other thing I'm thinking hard about is just what it is that I'm not seeing in C++ vectors that others are seeing.  I find it hard to believe that I'm right and everybody else is wrong.  It simply has to be a failure on my part to see or comprehend something.  I've been through this before.  It happens to me every several years.  Perhaps this time I'll discover it.

Patrice Terrier

#41
30 Kb.
without class and the hassle of dynamic memory allocation.
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

James C. Fuller

Fred,
  Thank you for the print code.
I was looking here for your demo I didn't know (or forgot most likely ) it was PowerBASIC.
I was spoiled using ddoc for all my commercial work.

Patrice,
  Looking forward to seeing your listview for storage code if that's what you used.

James

Patrice Terrier

I switched from ListView to LISTBOX, much more simple.

example:

void AddRecord(IN AddressType record) {
    List_Add(gP.hCol[C_Company], record.Company);
    List_Add(gP.hCol[C_LastName], record.LastName);
    List_Add(gP.hCol[C_FirstName], record.FirstName);
    List_Add(gP.hCol[C_Address1], record.Address1);
    List_Add(gP.hCol[C_Address2], record.Address2);
    List_Add(gP.hCol[C_Address3], record.Address3);
    List_Add(gP.hCol[C_CityName], record.CityName);
    List_Add(gP.hCol[C_StateName], record.StateName);
    List_Add(gP.hCol[C_ZipCode], record.ZipCode);
    List_Add(gP.hCol[C_Country], STRL(record.Country));
    List_Add(gP.hCol[C_Phone], record.Phone);
    List_Add(gP.hCol[C_Fax], record.Fax);
    List_Add(gP.hCol[C_Email], record.Email);
    List_Add(gP.hCol[C_Url], record.Url);
    List_Add(gP.hCol[C_Comments], record.Comments);
}

long CreateMemoryList(IN HWND hWnd) {
    long nRet = 0;
    DWORD dwStyle = WS_CHILD;
    long iCol;
    for (iCol = 0; iCol < C_MAX; iCol++) {
        gP.hCol[iCol] = CreateWindowEx(0, L"LISTBOX", L"", dwStyle, 0, 0, 0, 0, hWnd, (HMENU) iCol + 300, gP.hinstance, NULL);
    }
    if (IsWindow(gP.hCol[C_MAX - 1])) {
        DWORD bufferSize = FileSize(DATA_BASE_NAME);
        if (bufferSize) {
            DWORD ByttesReaded = 0;
            AddressType record = { 0 };
            DWORD recordsize = sizeof(AddressType);
            gP.currententry = 1;
            gP.maxentries = bufferSize / recordsize;
            HANDLE hFile = CreateFile(DATA_BASE_NAME, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
            if (hFile != INVALID_HANDLE_VALUE) {

                for (long nRow = 0; nRow < gP.maxentries; ++nRow) {

                    ReadFile(hFile, &record, recordsize, &ByttesReaded, NULL);

                    AddRecord(record);
                }

                CloseHandle(hFile);
                nRet = -1;
            }
        }
    }
    return nRet;
}
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Frederick J. Harris

#44
Quote
I'd have thought...


AddArrayPtr[4]->Address


... but I tried it and it doesn't work.  Also tried a few other things too without any luck.  So I'm sorry to say I don't know how to do it.

Well, I finally figured that rotten thing out.  It was, as I was beginning to expect, a template syntax misery.  Before getting into that though, I finally had my big realization of the use of vectors!  After all these years of wondering what I could do with something like that, I finally discovered a very worthwhile use I could make of them.  The beginning of my realization was this from an old Microsoft Visual Studio 98 MSDN CD...

Quote
5. Sequence Containers
Sequence containers store and retrieve their data in a sequential fashion. There are three
different sequence containers defined in STL: vector, deque and list.

5.1 Vector
#include <vector>
vector<class TYPE, allocator<class TYPE> >

A vector is similar to a normal C array, except that it can change size automatically as
needed. Data access or modification is random and can be accomplished via operator[].
Insertion or erasure is efficient at the end only. Insertion or erasure of data at the
beginning or in the middle requires shifting the entire array. Therefore, insertion or
erasure anywhere but at the end is a linear operation, meaning that the time to execute the
operation is a function of the number of elements that must be shifted right or left.
Insertion or erasure at the end is a constant operation, meaning that the time to execute
the operation will remain unchanged regardless of how many (or how few) elements are in the
array.

The following function declares a vector of 10 ints, fills the vector with the values
0 - 9, and then prints the values.

     typedef vector<int, allocator<int> > INTVECT;
     void somefunct()
     {
     // declare an INTVECT with slots for 10 ints
          INTVECT myVector(10);
          int i;

          for(i = 0; i < 10; i++) // fill myVector with the values 0 - 9
               myVector = i;

          for(i = 0; i < 10; i++)  // print the values in myVector
               cout << myVector << ", ";
          cout << endl;
     }

Output of somefunct:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

The critical part of the above for me was that insertion at the end is a constant operation which won't increase as the length increases, but that insertion anywhere else requires all the contents to shift.  Backing up a bit, two days ago I was struggling with some of the underlying concepts and issues relating to my CArray Class where I had added Redim Preserve capability to it about like PowerBASIC's Redim Preserve.  There are performance problems involved in that though if used in the context of a 'growable container'.  Let me give an interesting example of that.

In one of my work applications, many years ago I had to develop some string routines for converting a number in numeric format such as an int or long to its string representation, and by that I don't mean Str$(1234) but rather ...

"One thousand, two hundred and thirty four"

Here is some code to do that in terms of my String Class...


#define TCLib
#ifndef UNICODE
   #define   UNICODE
#endif
#ifndef _UNICODE   
   #define   _UNICODE
#endif   
#include <windows.h>
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"
#include "AlphaNumbers.h"


String strOneDigit(const size_t iNumber)
{
switch(iNumber)
{
   case 0:
     return _T("zero");
   case 1:
     return _T("one");
   case 2:
     return _T("two");
   case 3:
     return _T("three");
   case 4:
     return _T("four");
   case 5:
     return _T("five");
   case 6:
     return _T("six");
   case 7:
     return _T("seven");
   case 8:
     return _T("eight");
   case 9:
     return _T("nine");
   default:
     return _T("unknown");
}
}


String strTwoDigit(const size_t iNumber)
{
String strNumber,strTens,strUnits;
size_t iTens=0,iUnits=0;

if(iNumber<10)
    return strOneDigit(iNumber);
strNumber=iNumber;
strTens=strNumber.Left(1);
strUnits=strNumber.Right(1);
iTens=strTens.iVal();
iUnits=strUnits.iVal();
if(iTens==1)
{
    switch(iUnits)
    {
      case 0:
        return _T("ten");
      case 1:
        return _T("eleven");
      case 2:
        return _T("twelve");
      case 3:
        return _T("thirteen");
      case 4:
        return _T("fourteen");
      case 5:
        return _T("fifteen");
      case 6:
        return _T("sixteen");
      case 7:
        return _T("seventeen");
      case 8:
        return _T("eighteen");
      case 9:
        return _T("nineteen");
    }
}
else
{
    switch(iTens)
    {
      case 2:
        strNumber=_T("twenty ");
        break;
      case 3:
        strNumber=_T("thirty ");
        break;
      case 4:
        strNumber=_T("fourty ");
        break;
      case 5:
        strNumber=_T("fifty ");
        break;
      case 6:
        strNumber=_T("sixty ");
        break;
      case 7:
        strNumber=_T("seventy ");
        break;
      case 8:
        strNumber=_T("eighty ");
        break;
      case 9:
        strNumber=_T("ninety ");
        break;
    }
}
if(iUnits)
{
    String strLastDigit=strOneDigit(iUnits);
    strNumber=strNumber+strLastDigit;
}

return strNumber;
}


String strThreeDigit(const size_t iNumber)
{
String strLeftOne,strRightTwo,strNumber;

strNumber=iNumber;
strLeftOne=strNumber.Left(1);
strRightTwo=strNumber.Right(2);
if(strRightTwo==_T("00"))
{
    return strOneDigit(strLeftOne.iVal()) + _T(" hundred");
}
else
{
    String strLastTwo=strTwoDigit(strRightTwo.iVal());
    return strOneDigit(strLeftOne.iVal()) + _T(" hundred ") + strLastTwo;
}
}


String GetAlphaNumber(const size_t iNumber)
{
if(iNumber<10)                       //   0 through 9
    return strOneDigit(iNumber);
if(iNumber>=10 && iNumber<100)       //  10 through 99
    return strTwoDigit(iNumber);
if(iNumber>=100 && iNumber<1000)     // 100 through 999
    return strThreeDigit(iNumber);
else
    return _T("");
}
 

And here is a header for that...


#ifndef AlphaNumbers_h
#define AlphaNumbers_h

String strOneDigit(const size_t iNumber);
String strTwoDigit(const size_t iNumber);
String strThreeDigit(const size_t iNumber);
String GetAlphaNumber(const size_t iNumber);

#endif


So here is a program using the above and my new Redim Preserve capability of my CArray Class to create a 'Growable Container' to accumulate the numbers 0 through 25 converted to Strings....


// Demo37.cpp
// cl Demo37.cpp Strings.cpp AlphaNumbers.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib
#ifndef UNICODE
   #define   UNICODE
#endif
#ifndef _UNICODE   
   #define   _UNICODE
#endif   
#include <windows.h>
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"
#include "CArray.h"
#include "AlphaNumbers.h"

int main()
{
CArray<String> ar;            // No initial size or underlying buffer for array ar, i.e., undimensioned

for(size_t i=0; i<=25; i++)   // Zero Through Twenty Five
{     
     ar.Redim(i,true);         // Re-Dimension call starting at zero, i.e., will contain 1 element                                     
     ar(i)=GetAlphaNumber(i);  // For Example, Convert 0 to "zero", 1 to "one", 2 to "two", etc.
     ar(i).Print(true);        // Output result to console
}
getchar();

return 0;
}

/*
Output:
=========
zero
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
seventeen
eighteen
nineteen
twenty
twenty one
twenty two
twenty three
twenty four
twenty five
*/


Note that in PowerBASIC one can dimension an array with a zero subscript like so...

Dim array() As string
Redim array(0)

In the case above array has 1 element accessabe through 0 subscript, so you could write...

array(0)="PowerBASIC : Compile Without Compromise!"

...and the UBound of it would be zero.  Well, you could run that concept through a loop starting at zero and doing a Redim Preserve at each iteration to 'grow' the container and maintain its present contents as my C++ code above does with these lines from the loop....


ar.Redim(i,true);                                     
ar(i)=GetAlphaNumber(i);
ar(i).Print(true);


However, that is absolutely not extensible for large data sets or where performance would be required, because for each iteration of the loop a new buffer is going to have to be allocated, the existing string objects moved to that new buffer (and the number is increasing with each iteration), and the old buffer released.  In any case though that was a fun program to work on to make sure my new Redim Preserve was working.  Here is an update of my CArray Class if anyone would care to run the above code.  The changes to it are the Redim Preserve plus it now calculates and uses subscripts like PowerBASIC, i.e., this...


CArray<String> ar(10);


...will now give you eleven elements (0 - 10) instead of the previous ten (0 - 9). 


// CArray.h
#ifndef CArray_cpp
#define CArray_cpp
#define NEW new


template <class datatype> class CArray            // This entity is a templated class for creating dynamic
{                                                 // multi-dimensional arrays of from one to four dimensions.
public:                                          // It allows for a basic language type syntax for doing
CArray() : pObjs(0)                              // the above.  GNU C++ compilers implement dynamic array
{                                                // allocation but MS VC++ compilers don't.  Since I wanted
  this->iNumObjects=0;                            // to test compile with both I developed this class.
  d1=d2=d3=d4=0;
}


CArray(size_t i) : pObjs(0)                                  // One Dimensional Array Constructor
{
  d1=i+1, d2=0, d3=0, d4=0;   
  this->iNumObjects=d1;
  this->pObjs = NEW datatype[this->iNumObjects]();
}


CArray(size_t i, size_t j) : pObjs(0)                        // Two Dimensional Array Constructor
{
  d1=i+1, d2=j+1, d3=0, d4=0;
  this->iNumObjects=d1*d2;
  this->pObjs = NEW datatype[this->iNumObjects]();
}


CArray(size_t i, size_t j, size_t k) : pObjs(0)              // Three Dimensional Array Constructor
{
  d1=i+1, d2=j+1, d3=k+1, d4=0; 
  this->iNumObjects=d1*d2*d3;
  this->pObjs = NEW datatype[this->iNumObjects]();
}


CArray(size_t i, size_t j, size_t k, size_t l) : pObjs(0)    // Four Dimensional Array Constructor
{
  d1=i+1, d2=j+1, d3=k+1, d4=l+1; 
  this->iNumObjects=d1*d2*d3*d4;
  this->pObjs = NEW datatype[this->iNumObjects]();
}


bool Dim(size_t i)
{
  if(this->pObjs)
     delete [] this->pObjs;
  d1=i+1, d2=0, d3=0, d4=0;
  this->iNumObjects=d1;
  this->pObjs=NEW datatype[this->iNumObjects]();
  if(this->pObjs)
     return true;
  else
     return false;
}


bool Dim(size_t i, size_t j)
{
  if(this->pObjs)
     delete [] this->pObjs;
  d1=i+1, d2=j+1, d3=0, d4=0;;
  this->iNumObjects=d1*d2;
  this->pObjs=NEW datatype[this->iNumObjects]();
  if(this->pObjs)
     return true;
  else
     return false;
}


bool Dim(size_t i, size_t j, size_t k)
{
  printf("Are We Getting In Here???\n");   
  if(this->pObjs)
     delete [] this->pObjs;
  d1=i+1, d2=j+1, d3=k+1, d4=0; 
  this->iNumObjects=d1*d2*d3;
  this->pObjs=NEW datatype[this->iNumObjects]();
  printf("j  = %Iu\n",j);
  printf("d2 = %Iu\n",d2);
  if(this->pObjs)
     return true;
  else
     return false;
}

 
bool Dim(size_t i, size_t j, size_t k, size_t l)
{
  if(this->pObjs)
     delete [] this->pObjs;
  d1=i+1, d2=j+1, d3=k+1, d4=l+1;
  this->iNumObjects=d1*d2*d3*d4;
  this->pObjs=NEW datatype[this->iNumObjects]();
  if(this->pObjs)
     return true;
  else
     return false;
}

/*
bool Redim(size_t i, bool blnPreserve)
{
  datatype* pObj=NULL;
 
  if(this->pObjs)
  {
     printf("i    = %Iu\n",i); 
     d1=++i;
     printf("d1   = %Iu\n",d1);
     pObj=NEW datatype[i]();
     if(pObj)
     {
        if(blnPreserve)
        {   
           size_t iObs=0;
           if(i<this->iNumObjects)
              iObs=i;
           else         
              iObs=this->iNumObjects;
           for(size_t i=0; i<iObs; i++)
               pObj[i]=this->pObjs[i];
           printf("iObs = %Iu\n",iObs);
        }
        delete [] this->pObjs;
        this->pObjs=pObj;
        this->iNumObjects=i;
        return true;
     }
     else
        return false;
  } 
 
  return false; 
}
*/

bool Redim(size_t i, bool blnPreserve)
{
  datatype* pObj=NULL;
 
  //printf("i    = %Iu\n",i); 
  d1=++i;
  //printf("d1   = %Iu\n",d1);
  pObj=NEW datatype[i]();
  if(pObj)
  {
     if(blnPreserve)
     {   
        size_t iObs=0;
        if(i<this->iNumObjects)
           iObs=i;
        else         
           iObs=this->iNumObjects;
        //printf("iObs = %Iu\n",iObs);
        for(size_t i=0; i<iObs; i++)
        {   
            pObj[i]=this->pObjs[i];
            //printf("We Got Inside The If!\n");   
        }   
     }
     if(this->pObjs)
        delete [] this->pObjs;
     this->pObjs=pObj;
     this->iNumObjects=i;
     return true;
  }
  else
     return false;
   
  return false; 
}


datatype& operator()(size_t i)                               // One Dimensional Accessor
{
  return pObjs[i];
}


datatype& operator()(size_t i, size_t j)                     // Two Dimensional Accessor
{
  return pObjs[i*d2 + j];
}


datatype& operator()(size_t i, size_t j, size_t k)           // Three Dimensional Accessor
{
  return pObjs[i*d2 + j + k*d1*d2];
}


datatype& operator()(size_t i, size_t j, size_t k, size_t l) // Four Dimensional Accessor
{
  return pObjs[i*d2 + j + k*d1*d2 + l*d1*d2*d3];
}


bool blnMemoryIsGood()
{
  return !!pObjs;
}


int UBound(int iDim)
{
  if(iDim==1)
     return d1-1;
  if(iDim==2)
     return d2-1;
  if(iDim==3)
     return d3-1;
  if(iDim==4)
     return d4-1;
  else
     return 0;
}


~CArray()
{
  if(this->pObjs)
     delete [] this->pObjs;
}


private:
datatype*    pObjs;       // pointer to the base memory allocation for array
size_t       iNumObjects; // We'll need this to zero memory for non-class types
size_t       d1;          // Dimension #1
size_t       d2;          // Dimension #2
size_t       d3;          // Dimension #3
size_t       d4;          // Dimension #4
};

#endif


So that's where my head was at when I started ruminating about C++ vectors.  I had a growable container, but performance could be quite terrible if used in the fashion I used it above - as cool as I thought that program was!

So, in thinking about it, and playing around with C++ vectors, I realized for sure their construction was a stack based design.  If you do this...


vector<int) v;
printf("v.size() = %u",v.size());


...you'll get zero for size.  Same for vector::capacity().  For it to have any size or capacity one must use the vector::push() semantics.  And there doesn't appear to be any initial default memory allocation that would enable anything at all to be put within a vector without a first push() operation.  So I dug up some of my old template based stack code and came up with this CVector Class...


// cl Test6.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib
#include <windows.h>
#include "stdio.h"
extern "C" int _fltused=1;

template <class type, size_t iSize> class CVector
{
public:
CVector(size_t iSize) : pObjs(NULL)
{
  this->m_iSize = iSize;
  this->m_Pos   = 0;
  this->pObjs=new type[this->m_iSize]();
}

bool push(type var)
{
  if(m_Pos < m_iSize)
  {
     pObjs[m_Pos++] = var;
     return true;
  }
  else
  {
     type* pNewBuffer = new type[m_iSize*2]();
     if(pNewBuffer)
     {
        for(size_t i=0; i<m_iSize; i++)
            pNewBuffer[i] = pObjs[i];
        delete []           pObjs;
        pObjs             = pNewBuffer;
        pObjs[m_Pos++]    = var;
        m_iSize           = m_iSize*2;
        return true;       
     }
  }     

  return false;
}

type pop()
{
  if(m_Pos)
     return pObjs[--m_Pos];
  else
     return false;
}

type& operator()(size_t i)
{
  return pObjs[i];
}

~CVector()
{
  delete [] this->pObjs;
}

private:
size_t m_Pos;
size_t m_iSize;
type*  pObjs;
};

int main()
{
CVector<size_t,10>* pCV = new CVector<size_t,10>(10);

for(size_t i=0; i<25; i++)
     printf("pCV->push(%Iu) = %d\ti = %Iu\n",i,pCV->push(i),i);
printf("\n");
for(size_t i=0; i<25; i++)
     printf("pCV->pop()   = %d\ti = %Iu\n",pCV->pop(),i);
printf("\n");
for(size_t i=0; i<25; i++)
     printf("pCV->operator()(%Iu) = %Iu\n",i,pCV->operator()(i));
getchar();

return 0;   
}

#if 0


C:\Code\VStudio\VC++9\LibCTiny\x64\Test2>cl Test6.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

Test6.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Test6.exe
Test6.obj
TCLib.lib
kernel32.lib

C:\Code\VStudio\VC++9\LibCTiny\x64\Test2>test6
pCV->push(0) = 1        i = 0
pCV->push(1) = 1        i = 1
pCV->push(2) = 1        i = 2
pCV->push(3) = 1        i = 3
pCV->push(4) = 1        i = 4
pCV->push(5) = 1        i = 5
pCV->push(6) = 1        i = 6
pCV->push(7) = 1        i = 7
pCV->push(8) = 1        i = 8
pCV->push(9) = 1        i = 9
pCV->push(10) = 1       i = 10
pCV->push(11) = 1       i = 11
pCV->push(12) = 1       i = 12
pCV->push(13) = 1       i = 13
pCV->push(14) = 1       i = 14
pCV->push(15) = 1       i = 15
pCV->push(16) = 1       i = 16
pCV->push(17) = 1       i = 17
pCV->push(18) = 1       i = 18
pCV->push(19) = 1       i = 19
pCV->push(20) = 1       i = 20
pCV->push(21) = 1       i = 21
pCV->push(22) = 1       i = 22
pCV->push(23) = 1       i = 23
pCV->push(24) = 1       i = 24

pCV->pop()   = 24       i = 0
pCV->pop()   = 23       i = 1
pCV->pop()   = 22       i = 2
pCV->pop()   = 21       i = 3
pCV->pop()   = 20       i = 4
pCV->pop()   = 19       i = 5
pCV->pop()   = 18       i = 6
pCV->pop()   = 17       i = 7
pCV->pop()   = 16       i = 8
pCV->pop()   = 15       i = 9
pCV->pop()   = 14       i = 10
pCV->pop()   = 13       i = 11
pCV->pop()   = 12       i = 12
pCV->pop()   = 11       i = 13
pCV->pop()   = 10       i = 14
pCV->pop()   = 9        i = 15
pCV->pop()   = 8        i = 16
pCV->pop()   = 7        i = 17
pCV->pop()   = 6        i = 18
pCV->pop()   = 5        i = 19
pCV->pop()   = 4        i = 20
pCV->pop()   = 3        i = 21
pCV->pop()   = 2        i = 22
pCV->pop()   = 1        i = 23
pCV->pop()   = 0        i = 24

pCV->operator()(0) = 0
pCV->operator()(1) = 1
pCV->operator()(2) = 2
pCV->operator()(3) = 3
pCV->operator()(4) = 4
pCV->operator()(5) = 5
pCV->operator()(6) = 6
pCV->operator()(7) = 7
pCV->operator()(8) = 8
pCV->operator()(9) = 9
pCV->operator()(10) = 10
pCV->operator()(11) = 11
pCV->operator()(12) = 12
pCV->operator()(13) = 13
pCV->operator()(14) = 14
pCV->operator()(15) = 15
pCV->operator()(16) = 16
pCV->operator()(17) = 17
pCV->operator()(18) = 18
pCV->operator()(19) = 19
pCV->operator()(20) = 20
pCV->operator()(21) = 21
pCV->operator()(22) = 22
pCV->operator()(23) = 23
pCV->operator()(24) = 24

#endif   


And there you should have the solution to your issue James with instantiating a pointer to an aeDymArray object and using it, as in the above code I created and used a pointer to one of my new CVector objects.  Not done with this yet by any means.