I pretty much had this multi-dimensional array code from a couple years ago Abyssion, but I reworked things to see if it would work with my TCLib.lib and it does fantastically, I’m really glad to see! If you’ve downloaded and used my TCLib.lib from earlier zip you’re good to go as nothing has changed there. However, as a result of some comments on one of my String Class methods I posted a couple days ago (a copy constructor) by Laserlight and Elysia in the C++ Forum, I decided to go through the whole String Class and modify methods to test for memory allocation failures and deal with them. I actually feel pretty ‘sheepish’ about that that I allowed that situation to go on as long as it has. For you see, I NEVER, EVER, do memory allocations without testing the return values for success. At least not in my Windows Api work where I routinely use HeapAlloc() to allocate memory. But for whatever reason my String Class has all totally unprotected calls to C++ new to allocate char/wchar_t arrays. Its been like that since I first started coding it about 10 years ago. And I’ve never had any failures regarding it. But that doesn’t make it right. In my personal use of it in my work I almost never work with really large strings or so many of them that Windows can’t handle the memory allocations. I guess that’s why it has never failed for me. In thinking about why I’ve left this untenable situation go on for so long all I can come up with is that testing the return from new for a non NULL pointer doesn’t do any good as rather than returning NULL for a memory allocation failure as is typical of all Win32 memory allocation functions, C++ raises an exception instead. And if the exception isn’t handled in a try/catch block then the app GPFs. So testing for NULL does no good. And since I don’t use C++ Exception Handling I’m out of luck. The only other issue here is that if the std::nothrow option is used with new instead new returns NULL on memory allocation failures rather than throwing an exception. So I decided to ‘run’ with that.
So what I did was go through my whole String Class the past two days changing all the calls to new to work like that, i.e., use std::nothrow, and test for memory allocation failures. The other part of the puzzle is I added another member variable to the private member variables section of my Class named this->blnSucceeded. If a memory allocation fails I set this to FALSE obviously, so I think this more or less ‘hack’ of mine satisfactorily addresses the problem, at least to my way of thinking. And it didn’t seem to increase the size of the binaries anything noticeable. So attached is DynArrays.zip which contains my revised String Class (Strings.cpp and Strings.h), as well as Demo26.cpp which uses my templated CArray Class. Far as I know, you can create dynamic arrays of any type of object out to four dimensions. What Demo26.cpp does is create a three dimensional array of Strings whose output looks like this…
Code:
(0,0,0) (0,1,0) (0,2,0) (0,3,0) (0,4,0)
(1,0,0) (1,1,0) (1,2,0) (1,3,0) (1,4,0)
(2,0,0) (2,1,0) (2,2,0) (2,3,0) (2,4,0)
(3,0,0) (3,1,0) (3,2,0) (3,3,0) (3,4,0)
(0,0,1) (0,1,1) (0,2,1) (0,3,1) (0,4,1)
(1,0,1) (1,1,1) (1,2,1) (1,3,1) (1,4,1)
(2,0,1) (2,1,1) (2,2,1) (2,3,1) (2,4,1)
(3,0,1) (3,1,1) (3,2,1) (3,3,1) (3,4,1)
(0,0,2) (0,1,2) (0,2,2) (0,3,2) (0,4,2)
(1,0,2) (1,1,2) (1,2,2) (1,3,2) (1,4,2)
(2,0,2) (2,1,2) (2,2,2) (2,3,2) (2,4,2)
(3,0,2) (3,1,2) (3,2,2) (3,3,2) (3,4,2)
(0,0,3) (0,1,3) (0,2,3) (0,3,3) (0,4,3)
(1,0,3) (1,1,3) (1,2,3) (1,3,3) (1,4,3)
(2,0,3) (2,1,3) (2,2,3) (2,3,3) (2,4,3)
(3,0,3) (3,1,3) (3,2,3) (3,3,3) (3,4,3)
(0,0,4) (0,1,4) (0,2,4) (0,3,4) (0,4,4)
(1,0,4) (1,1,4) (1,2,4) (1,3,4) (1,4,4)
(2,0,4) (2,1,4) (2,2,4) (2,3,4) (2,4,4)
(3,0,4) (3,1,4) (3,2,4) (3,3,4) (3,4,4)
(0,0,5) (0,1,5) (0,2,5) (0,3,5) (0,4,5)
(1,0,5) (1,1,5) (1,2,5) (1,3,5) (1,4,5)
(2,0,5) (2,1,5) (2,2,5) (2,3,5) (2,4,5)
(3,0,5) (3,1,5) (3,2,5) (3,3,5) (3,4,5)
For a mental image think of a big apartment building six stories high taking up a whole city block. You can think of each apartment as having an x/y or col/row two dimensional position relative to the city streets alongside it. And there are six floors. The floor or z displacement is the 3rd dimension. Or alternately, think of the little wooded square building blocks babies play with where you can stack them one upon one another. Anyway, the indexes work like so…
Code:
ar_3(row, column, height)
I named the array ar_3 in main and set it up like so…
Code:
int rows=4, cols=5, levels=6, i, j, k;
CArray<String> ar_3(rows, cols, levels);
Using my TCLib.lib the program compiles x64 UNICODE 5,632 bytes and I’ve no complaint about that! Using static linkage to the C Runtime brings it up to 145,408 bytes. Also interestingly, remarked out at the bottom of the source code file is the same program using my CArray Class, but using the C++ Standard Library’s String Class. That actually came out to 143,872 bytes – about 1,500 bytes smaller than my String Class, and I have no explanation for that. I find that startling actually. It’s the first occasion I’ve ever seen of the C++ Standard Library’s String Class building smaller than mine. But as I’ve mentioned before you can’t use the C++ Standard Library’s String Class with my TCLib.lib.
Kind of a ‘twist’ to things I’d better mention is that I use my String Class with both MS VC and GCC and with MS VC I sometimes use standard C Runtime linkage and at other times I use my TCLib.lib. This presents something of a problem because I do not have the std::nothrow option with new implemented within my TCLib.lib. The actual internal implementation is this…
Code:
//=====================================================================================
// Developed As An Addition To Matt Pietrek's LibCTiny.lib
//
// LIBCTINY -- Matt Pietrek 2001
// MSDN Magazine, January 2001
//
// With Help From Mike_V
//
// By Fred Harris, January 2016
//
// cl newdel.cpp /GS- /c /W3 /DWIN32_LEAN_AND_MEAN
//=====================================================================================
#include <windows.h>
void* __cdecl operator new(size_t s)
{
return HeapAlloc(GetProcessHeap(), 0, s);
}
void __cdecl operator delete(void* p)
{
HeapFree(GetProcessHeap(), 0, p);
}
void* operator new [] (size_t s)
{
return HeapAlloc(GetProcessHeap(), 0, s);
}
void operator delete [] (void* p)
{
HeapFree(GetProcessHeap(), 0, p);
}
So since these implementations are just simple wrappers around HeapAlloc() calls which return NULL upon failure I don’t need the std::nothrow option with my TCLib.lib; just using new returns NULL on failure. But if I’m using standard linkages with the C and C++ runtimes I need to use new(std::nothrow) and #include <new>. The way I set it up so as to be able to have one codebase and master String Class for any usage is I set this up with a #define macro…
Code:
#ifdef TCLib
#include "stdio.h" // Since Microsoft Recently 'Refactored' The C Runtime, I Can't Use Their Includes With TCLib Anymore
#include "stdlib.h" // They've Apparently Added Some New function descriptors that screw things up for me.
#define NEW new // My newdel.cpp file has alternate definitions (simpler) of new and new []
extern "C" int _fltused=1; // I don't want to get into this here; concerns floating point support
#else
#include <cstdio>
#include <cstdlib>
#include <new>
#define NEW new(std::nothrow)
#endif
So you have to be careful about defining or commenting out #define TCLib in both Strings.cpp and Demo26.cpp for whatever usage you are running, or you’ll get particularly nasty, ugly error messages. Definitely nastier than average ones!
Lot of stuff I’m throwing at you here. Ask if you have any questions!
Code:
// cl Demo26.cpp Strings.cpp /O1 /Os /GS- /Zc:sizedDealloc- TCLib.lib kernel32.lib user32.lib // <<< For Use With TCLib.lib
// cl Demo26.cpp Strings.cpp /O1 /Os /GS- /EHs kernel32.lib user32.lib // <<< For Standard C Runtime Linkage
// 145,408 Bytes C Runtime Linkage; x64; VC19 (VS 2015); UNICODE
// 5,632 Bytes TCLib Linkage; x64; VC19 (VS 2015); UNICODE
#define TCLib
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <windows.h>
#ifdef TCLib
#include "stdio.h" // Since Microsoft Recently 'Refactored' The C Runtime, I Can't Use Their Includes With TCLib Anymore
#include "stdlib.h" // They've Apparently Added Some New function descriptors that screw things up for me.
#define NEW new // My newdel.cpp file has alternate definitions (simpler) of new and new []
extern "C" int _fltused=1; // I don't want to get into this here; concerns floating point support
#else
#include <cstdio>
#include <cstdlib>
#include <new>
#define NEW new(std::nothrow)
#endif
#include "Strings.h" // My String Class
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(int 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(int i, int 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(int i, int j, int 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(int i, int j, int k, int 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;
}
datatype& operator()(int i) // One Dimensional Accessor
{
return pObjs[i];
}
datatype& operator()(int i, int j) // Two Dimensional Accessor
{
return pObjs[i*d2 + j];
}
datatype& operator()(int i, int j, int k) // Three Dimensional Accessor
{
return pObjs[i*d2 + j + k*d1*d2];
}
datatype& operator()(int i, int j, int k, int 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
int iNumObjects; // We'll need this to zero memory for non-class types
int d1; // Dimension #1
int d2; // Dimension #2
int d3; // Dimension #3
int d4; // Dimension #4
};
template<typename t1> void Output(CArray<t1>& Ar3)
{
for(int h=0; h<=Ar3.UBound(3); h++)
{
for(int i=0; i<=Ar3.UBound(1); i++)
{
for(int j=0; j<=Ar3.UBound(2); j++)
wprintf(L"%s\t",Ar3(i,j,h).lpStr());
printf("\n");
}
printf("\n");
}
}
int main()
{
int rows=4,cols=5,levels=6,i,j,k;
wchar_t szBuffer[16];
CArray<String> ar_3(rows,cols,levels);
for(i=0; i<=ar_3.UBound(3); i++)
{
for(j=0; j<=ar_3.UBound(1); j++)
{
for(k=0; k<=ar_3.UBound(2); k++)
{
ar_3(j,k,i)=L"(";
swprintf(szBuffer,L"%d",j);
ar_3(j,k,i)=ar_3(j,k,i)+szBuffer+L",";
swprintf(szBuffer,L"%d",k);
ar_3(j,k,i)=ar_3(j,k,i)+szBuffer+L",";
swprintf(szBuffer,L"%d",i);
ar_3(j,k,i)=ar_3(j,k,i)+szBuffer+L")";
}
}
}
Output(ar_3);
getchar();
return 0;
}
#if 0
(0,0,0) (0,1,0) (0,2,0) (0,3,0) (0,4,0)
(1,0,0) (1,1,0) (1,2,0) (1,3,0) (1,4,0)
(2,0,0) (2,1,0) (2,2,0) (2,3,0) (2,4,0)
(3,0,0) (3,1,0) (3,2,0) (3,3,0) (3,4,0)
(0,0,1) (0,1,1) (0,2,1) (0,3,1) (0,4,1)
(1,0,1) (1,1,1) (1,2,1) (1,3,1) (1,4,1)
(2,0,1) (2,1,1) (2,2,1) (2,3,1) (2,4,1)
(3,0,1) (3,1,1) (3,2,1) (3,3,1) (3,4,1)
(0,0,2) (0,1,2) (0,2,2) (0,3,2) (0,4,2)
(1,0,2) (1,1,2) (1,2,2) (1,3,2) (1,4,2)
(2,0,2) (2,1,2) (2,2,2) (2,3,2) (2,4,2)
(3,0,2) (3,1,2) (3,2,2) (3,3,2) (3,4,2)
(0,0,3) (0,1,3) (0,2,3) (0,3,3) (0,4,3)
(1,0,3) (1,1,3) (1,2,3) (1,3,3) (1,4,3)
(2,0,3) (2,1,3) (2,2,3) (2,3,3) (2,4,3)
(3,0,3) (3,1,3) (3,2,3) (3,3,3) (3,4,3)
(0,0,4) (0,1,4) (0,2,4) (0,3,4) (0,4,4)
(1,0,4) (1,1,4) (1,2,4) (1,3,4) (1,4,4)
(2,0,4) (2,1,4) (2,2,4) (2,3,4) (2,4,4)
(3,0,4) (3,1,4) (3,2,4) (3,3,4) (3,4,4)
(0,0,5) (0,1,5) (0,2,5) (0,3,5) (0,4,5)
(1,0,5) (1,1,5) (1,2,5) (1,3,5) (1,4,5)
(2,0,5) (2,1,5) (2,2,5) (2,3,5) (2,4,5)
(3,0,5) (3,1,5) (3,2,5) (3,3,5) (3,4,5)
#endif