• Welcome to Powerbasic Museum 2020-B.
 

News:

Forum in repository mode. No new members allowed.

Main Menu

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.

Frederick J. Harris

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???

James C. Fuller

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

Patrice Terrier

#17
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)
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

James C. Fuller

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

James C. Fuller

Fred,
  I just tried your class using a global var and it failed too.

James

Frederick J. Harris

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.

Frederick J. Harris

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.

James C. Fuller

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

Frederick J. Harris

Attached is a *.doc file of Matt's article.

James C. Fuller

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


James C. Fuller

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

Frederick J. Harris

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.   





Frederick J. Harris

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.   

Frederick J. Harris

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.

James C. Fuller

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