Pointers and arrays.
Gnamra

I've got a problem. Check the edit.

#SelectExpand
1Obj objectArray[255]; 2Obj *activeObjects[255]; 3 if(doLogic && al_is_event_queue_empty(event_queue)){ 4 doLogic = false; 5 if(Input::mouse[1]) 6 { 7 8 Input::mouse[1] = false; 9 for(int i = 0; i < 80; i++) 10 { 11 float r2 = (float)rand()/((float)RAND_MAX/ALLEGRO_PI*4); 12 objectArray[nextStartingIndex].lifespan = 4; 13 objectArray[nextStartingIndex].rotation = r2; 14 objectArray[nextStartingIndex].velX = -2 + (4 * (rand()/(RAND_MAX + 1.0))); 15 objectArray[nextStartingIndex].velY = -3 + (4 * (rand()/(RAND_MAX + 1.0))); 16 objectArray[nextStartingIndex].x = Input::mouseX; 17 objectArray[nextStartingIndex].y = Input::mouseY; 18 activeObjects[nextStartingIndex] = &objectArray[nextStartingIndex]; 19 nextStartingIndex++; 20 activeObjectsCount++; 21 if(nextStartingIndex >= 255) nextStartingIndex = 0; 22 } 23 } 24 25 for(int i = 0; i < activeObjectsCount; i++) 26 { 27 activeObjects[i]->lifespan--; 28 activeObjects[i]->velY += gravity; 29 activeObjects[i]->x += activeObjects[i]->velX; 30 activeObjects[i]->y += activeObjects[i]->velY; 31 } 32 }

(I know that the activeObjectsCount isn't being decremented so it'll just increase to beyond 255 and cause out of bounds.)

I've tried a couple of different ways to deal with the pointers in the activeObjects array.

I want to do it this way because having a list of pointers referencing the active objects in the objectArray eliminates the need to loop through the entire objectArray.

I clearly don't understand enough about pointers and / or arrays. I've tried to delete the element, I've found that doesn't make sense since it's not a dynamic array.

Could I do this with an array of Obj pointers? I've tried making a dynamic array of object pointers but the compiler kept complaining at me in every way I tried to do it. except if I did:

Obj *activeObjects;

activeObjects = new Obj;

but that doesn't make sense to me. it points at directly to a new object not it's address? What?

I do realize that I could do this with one of the standard containers, but I want to do it with arrays for educational reasons.

Also, I'm sorry if I explained it badly. It's 9 am and I haven't slept yet, and I'm sick on top of that.

Edit: After some more research, it seems like what I want to do is impossible? I'd have to create a new pointer array and copy the elements every time I wanted to add or remove elements? Guess I've got to switch to lists then.

#SelectExpand
1 2if(doLogic && al_is_event_queue_empty(event_queue)){ 3 doLogic = false; 4 if(Input::mouse[1]) 5 { 6 7 Input::mouse[1] = false; 8 for(int i = 0; i < 80; i++) 9 { 10 float r2 = (float)rand()/((float)RAND_MAX/ALLEGRO_PI*4); 11 objList.push_back(Obj()); 12 objList.back().lifespan = 160; 13 objList.back().rotation = r2; 14 objList.back().velX = -2 + (4 * (rand()/(RAND_MAX + 1.0))); 15 objList.back().velY = -3 + (4 * (rand()/(RAND_MAX + 1.0))); 16 objList.back().x = Input::mouseX; 17 objList.back().y = Input::mouseY; 18 pObjList.push_back(&objList.back()); 19 nextStartingIndex++; 20 } 21 } 22 23 for(auto it = pObjList.begin(); it != pObjList.end();) 24 { 25 (*it)->lifespan--; 26 (*it)->velY += gravity; 27 (*it)->x += (*it)->velX; 28 (*it)->y += (*it)->velY; 29 if((*it)->lifespan <= 0) 30 { 31 it = pObjList.erase(it); 32 }else it++; 33 } 34 } 35 36 37 if(redraw && al_is_event_queue_empty(event_queue)) 38 { 39 redraw = false; 40 41 for(auto it = pObjList.begin(); it != pObjList.end(); 42 { 43 al_identity_transform(&transform); 44 al_translate_transform(&transform, -(*it)->x+5, -(*it)->y+5); 45 al_scale_transform(&transform, 2, 2); 46 al_rotate_transform(&transform, ALLEGRO_PI*1.5); 47 al_translate_transform(&transform, (*it)->x+5, (*it)->y+5); 48 al_use_transform(&transform); 49 al_draw_rectangle((*it)->x, (*it)->y, (*it)->x+10, (*it)->y+10, al_map_rgb(255,255,255), 2); 50 } 51 al_flip_display(); 52 }

is what I'm doing here terribly expensive / ineffective? cause I was running debug with 1 std::cout in one of the for loops and the fps was roughly 5.

Removing the std::cout fixed the fps, but I'm still concerned. Switching transforms like that, does that cost a lot? Also, my rectangles are scaling but not rotating? I know all of them are in use, so the second list is not needed and is slowing down my program but only a small part of them will be in use later on.

taronĀ 

(stl) Lists are quite slow, but they shouldn't drop your fps that low.
Arrays are the fastest solution, albeit not the most dynamic.

To create an array of pointers this is the syntax you should use:

Obj **activeObjects;

activeObjects = newObj*[length];

An easy and fast way to remove an object using a collection like a vector is by moving the element to the back and then removing the last element.

This won't preserve the order, but it's quite fast. It can even be done using a simple array, although it won't automatically handle resizing in case the container is too small.

edit: fixed grammar errors, in both my post and code.

Gnamra

I tried using an array of pointers but I just couldn't get it to work.

The length is varying, it could be 40 or over 200 and I don't know what to do with all the pointers pointing to inactive objects. I don't want to loop through them, that's what I'm trying to prevent.

I tried removing elements from the array of pointers but that crashed and gave me errors no matter what I tried. All my searching has given me that I have to create a new array with the elements I want and get rid of the old one. (Is that perhaps how vectors work?)

bamccaig

Rather than starting a thread with a big[, ugly] code block you should first describe what you're trying to do, why, and what the thread is about (i.e., what you want to discuss or need help with).

Second, you should be try to be disciplined with your code. Every space, tab, brace or semi-colon should be intentional. We don't want to read through code that jumps around like this:

Gnamra said:

Obj objectArray[255];
Obj *activeObjects[255];
    if(doLogic && al_is_event_queue_empty(event_queue)){
      doLogic = false;
      if(Input::mouse[1])

If you're concerned about performance then something like this doesn't make sense:

Gnamra said:

objList.push_back(Obj());
objList.back().lifespan = 160;
objList.back().rotation = r2;
objList.back().velX = -2 + (4 * (rand()/(RAND_MAX + 1.0)));
objList.back().velY = -3 + (4 * (rand()/(RAND_MAX + 1.0)));
objList.back().x = Input::mouseX;
objList.back().y = Input::mouseY;

You are needlessly invoking std::list::back repeatedly. It makes better sense to create the object first and push it into the list afterward.

Obj obj;
obj.lifespan = 160;
obj.rotation = r2;
obj.velX = -2 + (4 * (rand()/(RAND_MAX + 1.0)));
obj.velY = -3 + (4 * (rand()/(RAND_MAX + 1.0)));
obj.x = Input::mouseX;
obj.y = Input::mouseY;
objList.push_back(obj);

That said, taking the address of an std::list element is also probably wrong and dangerous. If you want pointers to things then you need to be in control of the pointers. You could use operator new to allocate your objects and store their pointers in your collections instead. You should use smart pointers to help with that to avoid memory leaks or ugly and error-prone cleanup code.

Yes, the STL containers allocate new buffers when they run out of space. It's good for you to practice this yourself for educational purposes, but should encapsulate the functionality into an object, just as the STL does. You don't want to be manually manipulating these low-level arrays everywhere. You want to put that in a single place and reuse it. It's hard to get right and you only want to have to get it right one time.

Audric

It's not really clear what you call "inactive". If things may move from one state to the other, an efficient method would be to manage two different std:lists of pointers : a list of actives, and a list of inactives.
When something becomes inactive you remove it from the first list (without deleting the actual object), and add it to the other one. The first list loses a pointer, the second list gains a pointer. The value of these pointers is the same : the address in memory of the actual object, which doesn't change in the process.
When the object is no longer needed at all, you delete it and remove it from whichever list it's in.

bamccaig
Audric said:

The value of these pointers is the same : the address in memory of the actual object, which doesn't change in the process.

If you allocate it on the heap with new. :)

Gnamra
bamccaig said:

Rather than starting a thread with a big[, ugly] code block you should first describe what you're trying to do, why, and what the thread is about (i.e., what you want to discuss or need help with).

You're right, I'm sorry. The whole post ended up being a huge mess.
My code isn't like that, I just copied some of the stuff I thought relevant. I should have spent some time making it look less messy. Again, I'm sorry.

bamccaig said:


You are needlessly invoking std::list::back repeatedly. It makes better sense to create the object first and push it into the list afterward.

Oh, I thought since back returns an address and using that to mess with the element in the list wouldn't be that bad.

Audric said:

It's not really clear what you call "inactive".

Oh, uhm what I mean is that when the lifespan integer reaches 0 it should no longer do anything. It should not be pointed to in the active list and when more objects are needed it can use the objects that has a lifespan of 0 or less.

I really didn't have a problem with the array of elements since it's constant. But the array of pointers pointing to the various elements in the object array. When those objects have their lifetime set to 0 they are ready to be used again and should no longer be pointed to until they are used again.

I couldn't find out what to do with the pointers pointing to the elements with a lifespan of 0 or less. I want to remove them from the array, shrinking the array by one and when more objects are created they will have a pointer pointing to them.

An integer will count how many are active and I will only have to loop through the active ones instead of going through the entire list of objects and checking which ones have a lifespan of 0 or less.

I hope I've made myself clearer.

Perhaps a better question for you guys would be: How would you manage an array containing pointers to objects within a different array, how can I add or remove elements from the array containing the pointers.

Edgar Reynaldo

You need to learn the STL, and
you need to learn C++,
and about dynamic memory.

Use either a std::list, or a std::vector. Both will work fairly well for your purposes here. Use erase(iterator) or swap pointers for a vector and pop_back().

bamccaig
Gnamra said:

Oh, I thought since back returns an address and using that to mess with the element in the list wouldn't be that bad.

In practice the performance penalty is probably negligible, though only a profiler can really identify whether it's a problem. Performance isn't the main reason to change it. It's just a bonus. The main reason is readability and maintainability. It's much easier to read code that does only what it needs to do. If you see the same expression repeated over and over again then it's probably a good sign that you should refactor the code.

Gnamra said:

When those objects have their lifetime set to 0 they are ready to be used again and should no longer be pointed to until they are used again.

"No longer be pointed to" is basically the definition of a memory leak. At least with heap-allocated memory, and you don't want to use pointers to stack-allocated memory unless you're passing it deeper into the call stack. So you may need to clarify what you mean here.

Gnamra said:

I couldn't find out what to do with the pointers pointing to the elements with a lifespan of 0 or less. I want to remove them from the array, shrinking the array by one and when more objects are created they will have a pointer pointing to them.

An integer will count how many are active and I will only have to loop through the active ones instead of going through the entire list of objects and checking which ones have a lifespan of 0 or less.

I hope I've made myself clearer.

Perhaps a better question for you guys would be: How would you manage an array containing pointers to objects within a different array, how can I add or remove elements from the array containing the pointers.

Arrays can't really be resized. An array is just a contiguous sequence of memory, and typically all you can do is ask for a new contiguous sequence of memory. So you basically have two options: reallocate and copy the array whenever the number of elements changes (an expensive operation) or just use an is_active method to skip over inactive objects when you process the array. An alternative option is to move inactive objects to the end of the array and keep a count of active objects. That way you know that you only need to process the first num_active elements and can skip the tail. Which one is best will be context specific.

beoran

Actually, bamccaig, in C, arrays could be resized, if you use realloc. It's not guaranteed, but many implementations of realloc may just make the reserved area your pointer points to bigger if that is possible.

A for the original poster: Why not learn C first before learning C++? :)
You'll only have to learn about pointers and malloc realloc/calloc and free then.
Some untested code to show the idea:

#SelectExpand
1typedef struct GameObject_ GameObject; 2typedef struct GameObjectArray_ GameObjectArray; 3 4struct GameObject { 5 int index; 6 float x; 7 float y; 8 int active; 9 /* add anything you need here. */ 10}; 11 12struct GameObjectArray { 13 GameObject ** data; 14 int size; 15 int used; 16} 17 18GameObjectArray * gameobjectarray_resize(GameObjectArray * self, int size) { 19 GameObject ** aid; 20 aid = realloc(self->data, size); 21 if(aid) { 22 self->data = aid; 23 return self; 24 } else { 25 return NULL; 26 } 27} 28 29GameObjectArray * gameobjectarray_init(GameObjectArray * self, int size) { 30 int index; 31 if(!self) return NULL; 32 self->size = size; 33 self->used = 0; 34 self->data = (GameObject **) malloc(sizeof(GameObject*) * size); 35 if(!self->data) return NULL; 36 for(index = 0; index < self->size; index ++) { self->data[index] = NULL; } 37 return self; 38} 39 40void gameobjectarray_done(GameObjectArray * self) { 41 int index; 42 if(!self) return; 43 for(index = 0; index < self->size; index ++) { free(self->data[index]); } 44 free(self->data); 45} 46 47int gameobjectarray_putobject(GameObjectArray * self, GameObject * gob) { 48 int index; 49 if(!self) return; 50 if (self->used >= self->size) { 51 if(!gameobjectarray_resize(self, self->size + 128)) return -1; 52 } 53 for(index = 0; index < self->size; index ++) { 54 if(!self->data[index]) { 55 self->data[index] = gob; 56 gob->index = index; 57 self->used++; 58 return index; 59 } 60 } 61 return -1; 62} 63 64GameObject* 65gameobjectarray_newobject(GameObjectArray * self) { 66 GameObject * object; 67 object = (GameObject *) malloc(sizeof(GameObject)); 68 if(!object) return NULL; 69 if(gameobjectarray_putobject(self, object) < 0) { 70 free(object); return NULL; 71 } 72 return object; 73} 74 75 76GameObjectArray * 77gameobjectaray_freeobject(GameObjectArray * self, GameObject * obj) { 78 int index; 79 index = obj->index; 80 free(obj); 81 self->data[index] = NULL. 82 self->used--; 83 return self; 84} 85 86void gameobjectaray_freealldone(GameObjectArray * self) { 87 for(index = 0; index < self->size; index ++) { 88 GameObject * obj = self->data[index]; 89 if(obj && ((obj->alive < 1))) { 90 gameobjectaray_freeobject(self, obj); 91 } 92 } 93} 94 95 96int main(int argc, char * argv[]) { 97 GameObjectArray array; 98 GameObject * player; 99 int game_loop = true 100 gameobjectarray_init(&array); 101 player = gameobjectarray_newobject(&aray); 102 player->alive = 100; 103 /* then later: ... */ 104 while(game_loop) { 105 player->alive = 0; 106 gameobjectaray_freealldone(&array); 107 } 108 gameobjectarray_done(&array); 109 return 0; 110}

Thread #612017. Printed from Allegro.cc