Skip to main content
GameDev.net gamedev.net
🔒 Locked

Safe Arrays

Started by aboeing May 20, 2003 at 1:19 AM 10 replies 700+ views
Original Post
aboeing
aboeing
hi, im just wondering if there is some way to do ''safe'' arrays in c++? specifically i want them to delete themselves after they are not used, and also dont want to allow access to out of range items, but i also want them to be as identical as possible to the standard array usage.. this is what i have so far (keeping it simple without automated-delete): template class SafeArray { public: SafeArray(); SafeArray(int size) { array = new T[size]; array_size=size; } ~SafeArray() { delete [] array; } void Set(int pos, T val) { array[pos % array_size]=val; } T Get(int pos) { return array[pos % array_size]; } T operator [] (int x) { lastaccess=x % array_size; return array[lastaccess]; } void operator = (T val) { array[lastaccess]=val; } int lastaccess; int array_size; T *array; }; used in the following way: SafeArray sa(5); sa.Set(3,7.5f); printf("%g\n",sa.Get(3)); //sa[3]=5.5f doesnt work.. sa[3]; sa=5.5f; printf("%g\n",sa[3]); printf("%g\n",sa[3+5]); //output == 7.5,5.5,5.5 so the problem is that i cant simply say sa[3]=5.5f; or whatever.. anyone know if there is a solution to this? (other than just using arrays&pointers correctly..) thanks.
billybob
billybob
make the return value to operator [] a reference:

T & operator [] (int x)
aboeing
aboeing
kool, thanks, i cant believe i forgot about that. i have another question:
i want to be able to ''incorporate'' existing code.. eg:
double darray[5];
....
SafeArray sad;
sad.SetArray(darray,5);
printf("%g\n",sad[1]);
with:
void SetArray(T *old_array, int size) {
array=old_array;
array_size=size;
lastaccess=0;
}

but i have the issue, that i dont want to have to tell it the size of the array (sizeof doesnt work right?) is there some way around this?
and also, ~SetArray will try to delete the array, is there some way to find out whether the array was dynamically created or not? (ie: if i need to delete it or not..) automatically?
thanks.
civguy
civguy
quote:
Original post by aboeing
but i have the issue, that i dont want to have to tell it the size of the array (sizeof doesnt work right?) is there some way around this?
No. C++ pointers don''t know anything about what they point to.
quote:
is there some way to find out whether the array was dynamically created or not?
There''s no portable way, but a hacky way that depends on how the stack/heap works on your OS. You don''t want to take that route. You could have a "transfer ownership"-flag in your SafeArray, which tells if the SafeArray owns the pointer, or the code that uses SafeArray. Depending on that, SafeArray would choose whether to delete the pointer or not. You''d have to set this flag manually.

{
int darray[5];
SafeArray sad;
sad.SetArray(darray, 5, SafeArray::DONT_TRANSFER_OWNERSHIP);
SafeArray sad2;

int* darray2 = new int[5];
sad2.SetArray(darray2, 5, SafeArray::TRANSFER_OWNERSHIP);
}

Ok that''s quite verbose but you get the idea.
Beer Hunter
Beer Hunter
quote:
Original post by aboeing
hi, im just wondering if there is some way to do ''safe'' arrays in c++? specifically i want them to delete themselves after they are not used, and also dont want to allow access to out of range items, but i also want them to be as identical as possible to the standard array usage..
You mean, like the classes in the <vector> and <valarray> headers?
aboeing
aboeing
well as far as i know you cant do this using stl?


    
void AddOne(SafeArray <int> sarray) {
int i;
for (i=0;i<sarray.size();i++) {
sarray[i]++;
}
}

SafeArray <double> CreateSquareRoots(int num) {
SafeArray<double> returnarray(num);
int i;
for (i=0;i<num;i++) {
returnarray[i]=sqrt(i);
}
return returnarray;
}

int main(int argc, char *argv[]) {

int i;
printf("Hello world!\n");

SafeArray <int> Simple(2);
Simple[0]=1;
Simple[1]=5;
printf("Simple[1]=%d\n",Simple[1]); //should say 5

AddOne(Simple);
printf("Simple[1]=%d\n",Simple[1]); //should say 6

printf("Simple[1]=%d\n",Simple[3]); //should still say 6

printf("Simple[1]=%d\n",Simple[-5]); //should still say 6



SafeArray <double> Roots;
Roots = CreateSquareRoots(5);
printf("Roots[4]=%g\n",Roots[4]); //should say 2



float stack_array_from_old_code[50];
for (i=0;i<50;i++) stack_array_from_old_code[i]=i*3.0f;
SafeArray <float> StackArray;
StackArray.SetArray(stack_array_from_old_code,50,false);
printf("StackArray[3]=%g\n",StackArray[3]); //should say 9


float *dynamic_array_from_old_code = new float [50];
for (i=0;i<50;i++) dynamic_array_from_old_code[i]=i*2.0f;
SafeArray <float> DynamicArray;
DynamicArray.SetArray(dynamic_array_from_old_code,50,true);
printf("DynamicArray[40]=%g\n",DynamicArray[-40]); //should say 80


//delete [] dynamic_array_from_old_code; //not needed: SafeArray takes care of this now.

return 0;
}


and im also targetting an embedded platform and stl causes serious bloat..

.. this is the code as it stands atm:
i think it still need some fixing with the reference counting..
[**EDIT: updated code so that it works**]

  
template <class T> class SafeArray {
public:
SafeArray() {
array=NULL;
varray=NULL;
array_size=0;
lastaccess=0;
copies=NULL;
manage=true;
};
SafeArray(const int size) {
#ifdef USE_MALLOC
array=(T *)malloc(sizeof(T)*size);
#else
array=new T[size];
#endif
if (array==NULL) {
varray=NULL;
array_size=0;
copies=NULL;
manage=false;
} else {
varray = array;
array_size=size;
#ifdef USE_MALLOC
copies=(int *)malloc(sizeof(int));
#else
copies=new int;
#endif
*copies=1;
manage=true;
// printf(" 0x%x: copies:%d\n",this,*copies);

}
lastaccess=0;
}
SafeArray(SafeArray &other) {
manage=other.manage;
array_size=other.array_size;
lastaccess=other.lastaccess;
varray = other.varray;
(*other.copies)++;
copies=other.copies;
array= (T*)varray;
// printf(" 0x%x: copies:%d\n",this,*copies);

}
~SafeArray() {
if (copies==NULL)
return;
(*copies)--;
// printf("~0x%x: copies:%d\n",this,*copies);

if ((varray)&&((*copies)<=0)&&(manage)) {
// printf("i am deleteing myself\n");

#ifdef USE_MALLOC
free(varray);
free(copies);
#else
delete [] array;
delete copies;
#endif
varray=NULL;
array=NULL;
}
}
void SetArray(T *old_array, const int size, const bool dynamic=false) {
varray=old_array;
array_size=size;
lastaccess=0;
array= (T*)varray;
manage=dynamic;
if (manage) {
#ifdef USE_MALLOC
copies=(int *)malloc(sizeof(int));
#else
copies=new int;
#endif
*copies = 1;
}
}
T & operator [] (const int x) {
lastaccess=((+1 | (x >> (sizeof(int) * 8 - 1))) * x) % array_size; //fast absolute value

if (array)
return array[lastaccess];
}
void operator = (T val) {
if (!array) return;
array[lastaccess]=val;
}
void operator = (SafeArray &other) {
manage=other.manage;
array_size=other.array_size;
lastaccess=other.lastaccess;
varray = other.varray;
(*other.copies)++;
copies=other.copies;
array= (T*)varray;
// printf(" 0x%x: copies:%d\n",this,*copies);

}
const int size(void) {
return array_size;
}
private:
int array_size;
int lastaccess;
int *copies;
bool manage;
void *varray;
T *array;
};


[edited by - aboeing on May 20, 2003 8:51:59 AM]

[edited by - aboeing on May 20, 2003 10:36:11 AM]
Qw3r7yU10p!
Qw3r7yU10p!
quote:
stl causes serious bloat..

how much did it bloat your executable?


    
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>

using std::vector;
using std::transform;
using std::plus;
using std::bind2nd;
using std::cout;
using std::endl;

int main() {
vector<int> simple(3);
simple[0] = 1;
simple[1] = 5;
simple[2] = 11;

transform(simple.begin(), simple.end(), simple.begin(), bind2nd(plus<int>(), 1));


cout << "simple[0] = " << simple[0] << endl;
cout << "simple[1] = " << simple[1] << endl;
cout << "simple[2] = " << simple[2] << endl;

return 0;
}


[edited by - petewood on May 20, 2003 9:54:05 AM]
Qw3r7yU10p!
Qw3r7yU10p!
Also, if you want checked ranges you can use vector::at

simple.at(4) = 17;

will throw an exception.
aboeing
aboeing
using stl:
-rw-r--r-- 1 boein-aj student 317772 May 20 21:39 a.out
not using stl:
-rw-r--r-- 1 boein-aj student 131228 May 20 21:40 a.out

and when you only have 512kb of ram, it counts.
(btw, the stl program i had was

  
#include <vector>
#include <stdio.h>

using namespace std;

int main() {
vector <int> x(3);
x[1]=1;
printf("hello world! %d",x[1]);
return 0;
}

which is less complex than the non-stl test program i compiled.

also, i think the safearray has a more-familiar syntax as you dont really need to know anything (much) about templates.. but thanks for your suggestion anyway!
cgoat
cgoat
quote:
Original post by aboeing
using stl:
-rw-r--r-- 1 boein-aj student 317772 May 20 21:39 a.out
not using stl:
-rw-r--r-- 1 boein-aj student 131228 May 20 21:40 a.out



Wow. I tried building that same STL program with g++ 3.2 on Linux, and ended up with a 4092 byte executable:

g++ -O3 -o test test.cpp
strip test

EDIT: In other words, perhaps you need a better compiler or STL implementation.

[edited by - cgoat on May 20, 2003 10:49:24 AM]
aboeing
aboeing
using a custom version of gcc 2.95.3 specific for our os&platform. yes, it bloats, but we''re working on it.
Shannon Barber
Shannon Barber
Update to gcc 3.2.x if you can, 2.95 did not perform nearly as many optimizations as 3.x does.

Also, check that you are using -O (or O1,O2,O3) when compiling. You can also add -DNDEBUG to remove assertion code. -O3 will probably make the exe bigger, so don''t use that if size is your concern (it turns on loop-unrolling).

quote:
Original post by petewood
how much did it bloat your executable?


Think exception handling, RTTI, and the abstraction penalty which is significant on non-optimizing compilers. ''course, we''re adding the abstraction penalty right back it with the safe array (perhaps more of it).
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.