Powerbasic Museum 2020-B

IT-Consultant: James C. Fuller => Discussion => Topic started by: Patrice Terrier on January 18, 2017, 12:35:55 PM

Title: Address UNICODE 64-bit VS2015
Post by: Patrice Terrier on January 18, 2017, 12:35:55 PM
James--

Here is the WIP project, to work with UNICODE and 64-bit.
EXE binary size: 24 Kb

Not done yet:
- dynamic array to replace vector. (search for //zizi for the things to do)

Tools.h with my new LTRIM, RTRIM, TRIM (any) functions.

...
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 18, 2017, 03:02:39 PM
Patrice,
  WOW! 24Kb
There is a LOT of source code in there to produce such a small exe!
Does this mean you are a fan of TCLib :)

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Patrice Terrier on January 18, 2017, 04:32:09 PM
The problem is:
We have to check thorougly each of the function being used to avoid embedding exta code.

Especially when dealing with strings or any oher facilities like Vector.

We have to write our own, but the result is this tiny piece of code that is back to the origin of C, the time where one single exe could fit on a single floppy disk ;)

Hutch will feel very frustrated when seing 64-bit UNICODE code that is able to create smaller exe file than his belowed PB 32-bit ANSI.  ;D
Title: Re: Address UNICODE 64-bit VS2015
Post by: Mike Lobanovsky on January 18, 2017, 06:02:46 PM
<OT>

Quote from: Patrice Terrier on January 18, 2017, 04:32:09 PMHutch will feel very frustrated when seing 64-bit UNICODE code that is able to create smaller exe file than his belowed PB 32-bit ANSI.  ;D

Oh, you're exhibiting the competition and sportsmanship egos of your inner character I've never suspected to exist! ;D

</OT>
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 18, 2017, 06:22:05 PM
Patrice,
  I could not find the vector code ??
new/delete are fine for classes as they call the constructor/destructor but for arrays we also need realloc so calloc/realloc/free seem to be more in line.
I have array code from the bcx/bc9 RTL that does DIM,REDIM, and REDIM PRESERVE that should work.
I'll post a c++ demo shortly

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Patrice Terrier on January 18, 2017, 06:51:12 PM
In order to enable the FindDialog, and avoid
LNK2001 unresolved external symbol __chkstk

add this to Linker, Input, Additional Dependencies
chkstk.obj;Winspool.lib;TCLib.lib;kernel32.lib;user32.lib;gdi32.lib;comctl32.lib

QuoteDIM,REDIM, and REDIM PRESERVE
That would be nice if you have it, because this is the last thing to add to the attachmnent
see:
void Array_Resize(IN long maxenties) {
//zizi        gAddressBook.resize(gP.maxentries);
}

void Array_Erase(IN long entry) {
//zizi        gAddressBook.erase(gAddressBook.begin() + gP.currententry -1);
}
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 18, 2017, 07:09:17 PM
Patrice,
  Give this a try.
Not commented but you should be able to follow it.

Re:  unresolved external symbol __chkstk
I chased this this too and ended up with a compile or link option I believe.
I do not have it manually added.
Take a look at my batch file.

Have you tested if code not used in an #include file is added to the exe?
I know any code in the main cpp file that's not used  is not added but am not sure on #include's

James

Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 19, 2017, 09:54:06 AM
Quote
We have to write our own, but the result is this tiny piece of code that is back to the origin of C, the time where one single exe could fit on a single floppy disk

One of the most absolute and fundamental goals of my TCLib work was that it be capable of using the capabilities of the C++ language.  I never considered C to be any substitute for a high level language and definitely not a substitute for PowerBASIC.  That's why in my very earliest work on this after I saw I could get both a "Hello, World!" console program and a Windows GUI up and running in 2560 bytes in x64, I immediately started experimenting with my String Class, which uses C++ new and delete in the typical manner, and of course uses C++ Classes.  The final step was in many ways getting my templated Dynamic Multi-Dimensional Array Class working, which was no work at all.  It simply worked as is with no special modifications required by TCLib.

About that whole chkstk thing; I recall running into difficulties with that at some point.  It got some mention in various sources I was using.  I'll have to review that later.  Just now I forget.  Getting old too! :(
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 19, 2017, 11:05:57 AM
Fred,
  Do you have a c++  array class for use with TCLib not using any STL (vector)?

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 19, 2017, 12:01:11 PM
Fred,
  I found it. No redim or redim preserve simulation is probably why I forgot.
James
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 19, 2017, 03:11:17 PM
Patrice,
  This might be another vector substitute:
http://www.anyexample.com/programming/cplusplus/cplusplus_dynamic_array_template_class.xml
I needed to remove the throw's as we don't have exception handling with TCLib.

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 19, 2017, 05:51:26 PM
Quote
No redim or redim preserve simulation is probably why I forgot

Right.  I just never bothered coding it.  Its highly doable though.
Title: Re: Address UNICODE 64-bit VS2015
Post by: Patrice Terrier on January 19, 2017, 05:54:08 PM
I have been thinking of something based on hidden.....   LISTBOX(s)
i use it very often, for quick array replacement (PlayList) , very easy to add, sort, remove, erase one line/record or a group of lines/records.
I even use 2 or more // LISTBOX to store extra informations, using a leading one to handle them all.
And the good things with it, is that i do not have to mess with all the memory management, Windows does all the hard work for me.  8)

Can do also the same with a hidden ListView, using a specific column for each of the field.

...
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 19, 2017, 06:32:11 PM
Patrice,
  While the listbox should work fine for strings how would you use it for arrays of structures?
I really think the aedynarray link I posted minus the throws will do fine as a vector substitute for me.
I have REDIM,REDIM PRESERVE as part of bc9Basic. I can use that for multi-dimensional should I need it.

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Patrice Terrier on January 19, 2017, 06:40:57 PM
For the purpose of the address demo, ListView makes much sense because it is also very common to display a database in table mode :)
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 20, 2017, 12:24:54 PM
Everybody seems to love C++ vectors.  Don't know why.  I've never found any use for them in my work.  I take it one can add to them without having to worry about their size or capacity, as they automatically grow.  The nature of my work in my technical speciality area is such that I need to expend a lot of code determining the required size of the memory allocations I'll need for multi-dimensional arrays, but once I've figured that out, I only need to dimension them once and I'm good for whatever processing I need to do. 

But I have occasionally made use of PowerBASIC's Redim Preserve capability, which is somewhat related to C++ vectors, and I never implemented that in my templated CArray Class for dynamic multi-dimensional arrays for my C++ work or TCLib.  So I took a crack at it now.  There are issues to consider though. 

I have my CArray Class implemented out to four dimensions.  I needed that for the work I do.  If I fully implement this Redim Preserve capability do I need to do all four dimensions?  I'm not sure that would go.  The PowerBASIC docs say that for multi-dimensional arrays only the last dimension can be resized.  The special case of one dimensional arrays is such that they can always be redimensioned and their contents preserved.   I'm thinking that this might relate to the fact that PowerBASIC stores arrays in column major order, which always seemed a bit unusual to me, but I'm sure Bob had his reasons and I'm sure they were good ones. 

I'm wondering if it would be good enough to just add Redim Preserve capability to my CArray Class and just stop at the first dimension?  I'm thinking I might be able to pull it off for multiple dimensions if only the first dimension can be resized - opposite from PowerBASIC's situation it seems to me.  Isn't that C++ vector stuff only really useful for single dimension arrays?  Doesn't it break down and fall apart into incomprehensible syntactic gibberish for multiple dimensions?  I'm asking because I don't know.  I've looked at them but never used them.

In any case I put together Redim Preserve for my templated CArray Class.  I only did one dimension.  Took me an hour or two or three.  I've tested it with ints and Strings (my String Class).  Seems to be working OK.  Here is my CArray Class which includes Dim and Redim....


// CArray.cpp
#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
{
  this->iNumObjects=i;
  this->pObjs = NEW datatype[this->iNumObjects]();
  d1=i, d2=0, d3=0, d4=0;
}

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

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

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

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

bool Redim(size_t i, bool blnPreserve)
{
  datatype* pObj=NULL;
 
  if(this->pObjs)
  {   
     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];
        }
        delete [] this->pObjs;
        this->pObjs=pObj;
        this->iNumObjects=i;
        d1=i;
        return true;
     }
     else
        return false;
  } 
 
  return false; 
}

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

bool Dim(size_t i, size_t j, size_t k)
{
  if(this->pObjs)
     delete [] this->pObjs;
  this->iNumObjects=i*j*k;
  this->pObjs=NEW datatype[this->iNumObjects]();
  d1=i, d2=j, d3=k, d4=0;
  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;
  this->iNumObjects=i*j*k*l;
  this->pObjs=NEW datatype[this->iNumObjects]();
  d1=i, d2=j, d3=k, d4=l;
  if(this->pObjs)
     return true;
  else
     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
       

You can't set these templated thingies up in the standard C/C++ project organization of *.h and *.cpp files.  At least I can't, and believe me I've tried.  So I keep the above in a CArray.cpp file and take the unusual (for me) route of #including a *.cpp file as opposed to a *.h file.  Here is a program that exercises the above.   


// Demo31.cpp
// cl Demo31.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib
// 4,096 Bytes TCLib, x64 ansi
#include <windows.h>
#include "stdio.h"
#include "CArray.cpp"

int main()
{
CArray<int> ar;                                // Declare Undimensioned int array.

ar.Dim(5);                                     // Dimension/Allocate 5 ints
printf("ar.UBound(1) = %d\n",ar.UBound(1));    // Output UBound of array.
for(int i=0; i<5; i++)                         // Set five elements to numbers
     ar(i)=i;                                   // 0 through 5.
for(int i=0; i<5; i++)                         // Output five elements
     printf("%d\n",ar(i));                      // to console
ar.Redim(10,true);                             // Do Redim Preserve (the true part means Preserve)
printf("ar.UBound(1) = %d\n",ar.UBound(1));    // Output New UBound of array.
for(int i=0; i<10; i++)                        // Output Redimensioned Array.  First
     printf("%d\n",ar(i));                      // Five Elements Preserved
getchar();

return 0;
}

// Here's my output.  First five elements preserved...

#if 0
C:\Code\VStudio\VC++9\LibCTiny\x64\Test1>cl Demo31.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.

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

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

C:\Code\VStudio\VC++9\LibCTiny\x64\Test1>Demo31
ar.UBound(1) = 4
0
1
2
3
4
ar.UBound(1) = 9
0
1
2
3
4
0
0
0
0
0
#endif


It works with objects too.  Everybody must have my String Class by now.  Let's try the above with String Objects...


// Demo32.cpp
// cl Demo32.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib
// 5,120 Bytes x64 TCLib ansi
#include <windows.h>
#include "stdio.h"
#include "Strings.h"
#include "CArray.cpp"

int main()
{
CArray<String> ar;

ar.Dim(10);                                    // Allocate/Dimension 10 Dynamic Strings
printf("ar.UBound(1) = %d\n",ar.UBound(1));    // Output Upper Bound Of Array, i.e., Highest Accessable Element
for(int i=0; i<10; i++)                        // Assign 0 through 10 To Strings (numbers converted to strings).
     ar(i)=i;                                   // Will call String Constructor for size_t as argument
for(int i=0; i<10; i++)                        // Output 10 String Objects
     ar(i).Print(true);                         // String Class Has String::Print() Member For Console Output
ar.Redim(5,true);                              // Resize Array Smaller (10 to 5 Elements), And Presetve Existing Contents
printf("ar.UBound(1) = %d\n",ar.UBound(1));    // Output New Upper Bound
for(int i=0; i<5; i++)                         // Dump New Resized Array
     ar(i).Print(true);
getchar();

return 0;
}

#if 0

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

Demo32.cpp
Strings.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:Demo32.exe
Demo32.obj
Strings.obj
TCLib.lib
kernel32.lib
user32.lib

C:\Code\VStudio\VC++9\LibCTiny\x64\Test1>Demo32
ar.UBound(1) = 9
0
1
2
3
4
5
6
7
8
9
ar.UBound(1) = 4
0
1
2
3
4
#endif


Any thoughts???
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 20, 2017, 02:00:10 PM
Fred,
  Personally I am giving the aedynarray a test after removing all the throws.
It appears it will satisfy my needs for a vector replacement. (still early stage testing)
I already have code to handle multi-dim arrays and the REDIM REDIM preserve.
I have never used REDIM/REDIM PRESERVE on anything but single dimension arrays.

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Patrice Terrier on January 20, 2017, 02:13:59 PM
i for mysel will delegate all the hard work to Windows to have the smallest code, then we will have multiple solutions to choose from  8)
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 20, 2017, 06:22:44 PM
Well the AeDynArray was a bust with TCLib.
Cannot create a Global class variable.
unresolved external symbol atexit referenced in function ....
Works fine with a normal c++ config.

Also works fine with TCLib as long as the class var is local.


James
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 20, 2017, 06:49:01 PM
Fred,
  I just tried your class using a global var and it failed too.

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 20, 2017, 06:50:19 PM
You've just hit one of the limitations with TCLib - at least my version of it, no global/static objects.  In Matt Pietrek's seminal work on this he spoke to that very topic in detail.  He also provided code which he claimed would take care of it.  It is a surprisingly complex topic.  For myself, I never paid it much mind as its extremely rare for me to use global objects.
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 20, 2017, 06:52:50 PM
If its something you are interested in, follow up my Matt Pietrek LibCTiny links I provided when I started on this stuff exactly one year ago.  He provided code to deal with it.  I never tested it though.
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 20, 2017, 07:48:11 PM
Fred, 
  Well CRAP that's a game changer. I knew there had to be a catch somewhere!
It's not only global but no static's either.
I do like TCLib but not enough to do anymore research than I have done already.

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 20, 2017, 07:49:12 PM
Attached is a *.doc file of Matt's article.
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 20, 2017, 11:55:40 PM
Fred,
  Not really as bad as I first thought. :)
Most coding will be gui windows so I create and fill my aedynarray in winmain and pass the address in the win/dlg creation code. Preliminary testing is promising.

James

Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 21, 2017, 12:24:05 AM
I haven't got the editing or find done but here is RC1. 13,824 bytes.
I included a 50 record data file.
format:

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


James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 21, 2017, 04:35:39 AM
Jim,

Here is Matt Pietrek's crt0tcon.cpp file whose inclusion in his libctiny.lib allows for both global/static constructors and command line arguments.  The necessary code will add about a k to a k and a half to executable size (1 to 1.5 k).


//==========================================
// LIBCTINY - Matt Pietrek 2001
// MSDN Magazine, January 2001
// FILE: CRT0TCON.CPP
// cl crt0tcon.cpp /D "_CRT_SECURE_NO_WARNINGS" /O1 /Os /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//==========================================
#include <windows.h>
#include "argcargv.h"
#include "initterm.h"
#pragma comment(linker, "/defaultlib:kernel32.lib")
extern "C" int __cdecl main(int, char **, char **);    // In user's code

extern "C" void __cdecl mainCRTStartup(void)
{
int mainret, argc;

argc = _ConvertCommandLineToArgcArgv();
_atexit_init();                // set up our minimal cheezy atexit table
_initterm( __xc_a, __xc_z );   // Call C++ constructors
mainret = main(argc, _ppszArgv, 0);
_DoExit();
ExitProcess(mainret);
}


Here is a quick test I put together to test it and I found it works.  I hadn't ever tested it before because it didn't interest me much.  I don't use globals or statics, although I know most everybody else does...


// GlobalTest.cpp
// cl GlobalTest.cpp libctiny.lib Kernel32.lib /O1 /Os /FeGlobalTest.exe
// 4,608 Bytes x64 Includes Functionality For Global/Static Constructors & Command Line Strings
#include <windows.h>
#include <stdio.h>

class CBox
{
public:
CBox(int iLen, int iWth, int iHt)
{
  m_Length = iLen;
  m_Width  = iWth;
  m_Height = iHt;
}

int Volume()
{
  return m_Length * m_Width * m_Height;
}

private:
int m_Length;
int m_Width;
int m_Height;
}Box(2,3,4);


int main()
{
printf("Box.Volume = %d\n", Box.Volume());
return 0;
}

#if 0
C:\Code\VStudio\VC++9\64_Bit\libctiny>cl GlobalTest.cpp libctiny.lib Kernel32.lib /O1 /Os /FeGlobalTest.exe
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

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

/out:GlobalTest.exe
GlobalTest.obj
libctiny.lib
Kernel32.lib

C:\Code\VStudio\VC++9\64_Bit\libctiny>GlobalTest
Box.Volume = 24

C:\Code\VStudio\VC++9\64_Bit\libctiny>
#endif


I'll attach Matt Pietrek's libctiny.lib so you can try it out if you want.  I want to warn you though that this lib won't work with any of your or my stuff.  This was something I created at the offset of my trials with this nearly a year ago.  It is x64 though, and proves that global/static constructors can be made to work without too much trouble.  Here are some of the files referenced...


// initterm.h
typedef void (__cdecl *_PVFV)(void);
extern _PVFV __xc_a[], __xc_z[];    /* C++ initializers */

void __cdecl _initterm ( _PVFV * pfbegin, _PVFV * pfend );
void __cdecl _atexit_init(void);
void __cdecl _DoExit( void );



// initterm.cpp
//==========================================
// LIBCTINY - Matt Pietrek 2001
// MSDN Magazine, January 2001
//==========================================
#include <windows.h>
#include <malloc.h>
#include "initterm.h"

#pragma data_seg(".CRT$XCA")
_PVFV __xc_a[] = { NULL };


#pragma data_seg(".CRT$XCZ")
_PVFV __xc_z[] = { NULL };

#pragma data_seg()  /* reset */

#pragma comment(linker, "/merge:.CRT=.data")

typedef void (__cdecl *_PVFV)(void);

void __cdecl _initterm (
        _PVFV * pfbegin,
        _PVFV * pfend
        )
{
    /*
     * walk the table of function pointers from the bottom up, until
     * the end is encountered.  Do not skip the first entry.  The initial
     * value of pfbegin points to the first valid entry.  Do not try to
     * execute what pfend points to.  Only entries before pfend are valid.
     */
    while ( pfbegin < pfend )
    {
        // if current table entry is non-NULL, call thru it.
        if ( *pfbegin != NULL )
            (**pfbegin)();
        ++pfbegin;
    }
}

static _PVFV * pf_atexitlist = 0;
static unsigned max_atexitlist_entries = 0;
static unsigned cur_atexitlist_entries = 0;

void __cdecl _atexit_init(void)
{
    max_atexitlist_entries = 32;
    pf_atexitlist = (_PVFV *)calloc( max_atexitlist_entries,
                                     sizeof(_PVFV*) );
}

int __cdecl atexit (_PVFV func )
{
    if ( cur_atexitlist_entries < max_atexitlist_entries )
    {
        pf_atexitlist[cur_atexitlist_entries++] = func;
        return 0;
    }

    return -1;
}

void __cdecl _DoExit( void )
{
    if ( cur_atexitlist_entries )
    {
        _initterm(  pf_atexitlist,
                    // Use ptr math to find the end of the array
                    pf_atexitlist + cur_atexitlist_entries );
    }
}



// argcargv.h
extern char * _ppszArgv[];
int __cdecl _ConvertCommandLineToArgcArgv( void );



//==========================================
// LIBCTINY - Matt Pietrek 2001
// MSDN Magazine, January 2001
//==========================================
#include <windows.h>
#include "argcargv.h"

#define _MAX_CMD_LINE_ARGS  128

char * _ppszArgv[_MAX_CMD_LINE_ARGS+1];

int __cdecl _ConvertCommandLineToArgcArgv( void )
{
    int cbCmdLine;
    int argc;
    PSTR pszSysCmdLine, pszCmdLine;
   
    // Set to no argv elements, in case we have to bail out
    _ppszArgv[0] = 0;

    // First get a pointer to the system's version of the command line, and
    // figure out how long it is.
    pszSysCmdLine = GetCommandLine();
    cbCmdLine = lstrlen( pszSysCmdLine );

    // Allocate memory to store a copy of the command line.  We'll modify
    // this copy, rather than the original command line.  Yes, this memory
    // currently doesn't explicitly get freed, but it goes away when the
    // process terminates.
    pszCmdLine = (PSTR)HeapAlloc( GetProcessHeap(), 0, cbCmdLine+1 );
    if ( !pszCmdLine )
        return 0;

    // Copy the system version of the command line into our copy
    lstrcpy( pszCmdLine, pszSysCmdLine );

    if ( '"' == *pszCmdLine )   // If command line starts with a quote ("),
    {                           // it's a quoted filename.  Skip to next quote.
        pszCmdLine++;
   
        _ppszArgv[0] = pszCmdLine;  // argv[0] == executable name
   
        while ( *pszCmdLine && (*pszCmdLine != '"') )
            pszCmdLine++;

        if ( *pszCmdLine )      // Did we see a non-NULL ending?
            *pszCmdLine++ = 0;  // Null terminate and advance to next char
        else
            return 0;           // Oops!  We didn't see the end quote
    }
    else    // A regular (non-quoted) filename
    {
        _ppszArgv[0] = pszCmdLine;  // argv[0] == executable name

        while ( *pszCmdLine && (' ' != *pszCmdLine) && ('\t' != *pszCmdLine) )
            pszCmdLine++;

        if ( *pszCmdLine )
            *pszCmdLine++ = 0;  // Null terminate and advance to next char
    }

    // Done processing argv[0] (i.e., the executable name).  Now do th
    // actual arguments

    argc = 1;

    while ( 1 )
    {
        // Skip over any whitespace
        while ( *pszCmdLine && (' ' == *pszCmdLine) || ('\t' == *pszCmdLine) )
            pszCmdLine++;

        if ( 0 == *pszCmdLine ) // End of command line???
            return argc;

        if ( '"' == *pszCmdLine )   // Argument starting with a quote???
        {
            pszCmdLine++;   // Advance past quote character

            _ppszArgv[ argc++ ] = pszCmdLine;
            _ppszArgv[ argc ] = 0;

            // Scan to end quote, or NULL terminator
            while ( *pszCmdLine && (*pszCmdLine != '"') )
                pszCmdLine++;
               
            if ( 0 == *pszCmdLine )
                return argc;
           
            if ( *pszCmdLine )
                *pszCmdLine++ = 0;  // Null terminate and advance to next char
        }
        else                        // Non-quoted argument
        {
            _ppszArgv[ argc++ ] = pszCmdLine;
            _ppszArgv[ argc ] = 0;

            // Skip till whitespace or NULL terminator
            while ( *pszCmdLine && (' '!=*pszCmdLine) && ('\t'!=*pszCmdLine) )
                pszCmdLine++;
           
            if ( 0 == *pszCmdLine )
                return argc;
           
            if ( *pszCmdLine )
                *pszCmdLine++ = 0;  // Null terminate and advance to next char
        }

        if ( argc >= (_MAX_CMD_LINE_ARGS) )
            return argc;
    }
}


As you may note, its non-trevial code written by a Microsoft systems engineer who worked on compiler and linker technology and who wrote articles for Microsoft Systems Journal for a long time.  But I expect it could be bolted inside TCLib code somehow or other and gotten to work.  It works in the lib I attached for you, and my CBox class above worked OK with it when allocated in global static memory.   




Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 21, 2017, 04:42:44 AM
Like I said, for me it wasn't much of an issue.  In all the years I've been posting here in Jose's Forum, I think I only remember one project where I instantianted a global class object (a struct, actually, but same difference).  What I typically do in all my projects is allocate a struct such as 'AppData' or something like that which contains all the state variables which must persist across function calls, and I allocate it on the heap and store a pointer to it  in my WNDCLASS::cbWndExtra bytes.  I release it in WM_DESTROY.

I'm surprised vou took it so hard.  I really didn't keep that issue a secret.  I posted fairly extensively about it last February or March.  I know I produce a lot of verbage.  You might have  forgot about the issue, or it just didn't make an impression at the time.   
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 21, 2017, 04:49:30 AM
In that GlobalTest.cpp file above --- I doubt you could get that to build with VStudio 2015.  I can with VStudio 2008 with #include <stdio.h>, but MS put an end to that with the newer versions.  They add all those wierd qualifiers and macros around the prototypes that screw up the original function declarations.  If you eliminate the <stdio.h> and just declare printf as I have it I expect it'll work.
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 21, 2017, 11:05:57 AM
Fred,
  Thanks for the extra work but I am fine with TCLib the way it is.
It was just a knee jerk reaction. I forgot my roots where I prided myself in NO GLOBALS.
I too, did it your way but time fades the old brain....

Thanks again,
James
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 21, 2017, 02:01:16 PM
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();
}

Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 21, 2017, 06:31:50 PM
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. 
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 21, 2017, 07:14:51 PM
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
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 21, 2017, 10:48:20 PM
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.
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 22, 2017, 12:47:27 AM
Patrice,
  Is your printing code supposed to work?
Ir doesn't here.

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 22, 2017, 02:04:55 PM
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
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 22, 2017, 02:38:32 PM
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.
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 22, 2017, 03:06:42 PM
Fred,
  Where is your address demo. I looked on the board but....

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 22, 2017, 06:13:20 PM
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...
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 22, 2017, 06:31:19 PM
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

Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 22, 2017, 06:41:45 PM
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.
Title: Re: Address UNICODE 64-bit VS2015
Post by: Patrice Terrier on January 22, 2017, 07:31:31 PM
30 Kb.
without class and the hassle of dynamic memory allocation.
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 22, 2017, 10:20:31 PM
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
Title: Re: Address UNICODE 64-bit VS2015
Post by: Patrice Terrier on January 22, 2017, 10:36:00 PM
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;
}
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 23, 2017, 03:28:38 PM
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.

Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 23, 2017, 03:41:15 PM
In the above CVector Class code I initially set the size of the underlying array to 10.  But when it hits that it doubles the buffer to accomodate pushes 10 through 19.  Then it bumps it up again to accomodate pushes 20 to 25.  So its infinitely (within reason) growable as a container with constant time pushes.
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 23, 2017, 04:25:34 PM
Fred,
  While that is indeed interesting I still am not able to figure out how to access from my demo above.

James
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 23, 2017, 04:31:17 PM
Let me look at that.  It involves the crazy angle brackets at the end of the expression.  I have a doctors appt in a bit and might not get back until later.
Title: Re: Address UNICODE 64-bit VS2015
Post by: Frederick J. Harris on January 23, 2017, 05:50:31 PM
Had to use the getptr() member to get it to work...


AeDynArray<AddressType>* pAddArray = new AeDynArray<AddressType>;   // 399,872
...
...
pAddArray->Add(Address);
...
...
AddressType* pAdd=(AddressType*)pAddArray->getptr();
_tprintf(_T("%s\n"),pAdd[4].Company);
Title: Re: Address UNICODE 64-bit VS2015
Post by: James C. Fuller on January 23, 2017, 06:58:10 PM
Fred,
  Not quite.
I did some more surfing and found the answer I think. It works anyway whether it's politically correct or not :)
James


_tprintf(_T("%ls\n"),(*AddArrayPtr)[2].CityName);