x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

TArray::Remove bug

When calling TArray::Remove in an array with a single object (TArray store copies of objects), the CheckAddress() function report as following:

Assertion failed: Addr < GetData() || Addr >= (GetData() + ArrayMax) [File:C:\Program Files\Epic Games\4.10\Engine\Source\Runtime\Core\Public\Containers\Array.h] [Line: 1409] Attempting to add a container element (0x8031bba0) which already comes from the container (0x8031bba0, ArrayMax: 4)!

If you notice, the error doesn't even make sense. The object pointer lies well between GetData() and GetData() + ArrayMax range. I solved the problem by calling RemoteAt, since it works properly. Following is the code that triggers the bug:

 namespace
 {
   struct PathTile
   {
     TArray<ABaseTile*> pathToThis;
     ABaseTile* tile;
     int g;
     int h;
     int f;
 
     PathTile(ABaseTile* t, int g, int h, TArray<ABaseTile*> currentPath)
       : tile(t), g(g), h(h), f(g + h)
     {
       check(tile != nullptr);
 
       pathToThis = currentPath;
       pathToThis.Add(tile);
     }
 
     bool operator == (const PathTile& other)
     {
       UE_LOG(LogTemp, Warning, TEXT("EQUALS"));
       return tile == other.tile;
     }
   };
 
   PathTile extractTileWithLowestScore(TArray<PathTile>& tiles)
   {
     check(tiles.Num() > 0);
 
     PathTile* lowestScore = &tiles[0];
     for (int i = 1; i < tiles.Num(); ++i)
     {
       PathTile* tile = &tiles[i];
       if (tile->f < lowestScore->f)
         lowestScore = tile;
     }
 
     // Copy tile properties.
     PathTile tileCopy(*lowestScore);
 
     // Remove from list. ** Where it triggers the bug **
     tiles.Remove(*lowestScore);
 
     // Return copy.
     return tileCopy;
   }
 }
 
 And inside my function:
 
   TArray<PathTile> openList;
   openList.Add(PathTile(startTile, 0, getHScore(startTile, endTile), TArray<ABaseTile*>()));
 
   while (openList.Num() > 0)
   {
     PathTile tile = extractTileWithLowestScore(openList);
   }
Product Version: UE 4.10
Tags:
more ▼

asked Mar 14 '16 at 01:59 AM in Bug Reports

avatar image

nightz
1 2 4 3

avatar image Doug E ♦♦ STAFF Mar 17 '16 at 03:28 PM

Hey nightz-

Was Shadowriver's information helpful for you? If you are still having this issue can you explain where you've added this code (what class/function does the code belong to)? Line 50 above mentions "inside my function" - what function is this referring to?

(comments are locked)
10|2000 characters needed characters left

1 answer: sort voted first

The error is not related to index, but memory address related. GetData() returns pointer which points to first memory address of array and if you add ArrayMax it will point to address of last item. If you don't know that already, pointer is a memory address varable and it behaves same as int... yes you can do normal math on it and you have example of that here.

So what happens here is you create pointer of array item, which points to array memory space, then you try to remove it the argument of Remove function is reference, so memory address of item you give it same as pointer you used (which points to array). Remove function (and also Add and Insert functions) have check if address of argument not the range of array in memory, if it is not it rise the error. This was added probably to avoid buggy behavior of pointer, in which it starts to point at wrong item because things moved around in array. But i don't see point to do so remove, maybe to force developer to not direly address items in array to avoid unsafe situations, since if that Remove function was successful the pointer would point something else or be invalid (which is state that is not checkable).

RemoveAt indeed would solve this problem (in fact it much faster), also not using pointer is good solution, you could use varable that you copied which would naturally have diffrent memoery address then array.

more ▼

answered Mar 14 '16 at 04:33 AM

avatar image

Shadowriver
35.7k 925 168 1101

avatar image nightz Mar 14 '16 at 04:54 PM

I am aware of how pointers, references and arrays work (Hence why I copied the element at line 40 and returned it as a copy, instead of a ref). I am also aware that RemoveAt should be faster, since it doesn't need to look at the element position on the array or its copies.

So what happens here is you create pointer of array item, which points to array memory space, then you try to remove it the argument of Remove function is reference, so memory address of item you give it same as pointer you used (which points to array). Remove function (and also Add and Insert functions) have check if address of argument not the range of array in memory, if it is not it rise the error. This was added probably to avoid buggy behavior of pointer, in which it starts to point at wrong item because things moved around in array. But i don't see point to do so remove, maybe to force developer to not direly address items in array to avoid unsafe situations, since if that Remove function was successful the pointer would point something else or be invalid (which is state that is not checkable)

I understand your point about safety on the operations (checking the range of data to make sure the user doesn't end with an invalid pointer at hand - which would happen in a loop scenario), but it still doesn't make any sense to force the user to call Remove only on non-array-addressable references. I'd say the API should have at least a comment stating that this is invalid behavior or at least accept parameters as a copy (instead of const ref), and that's why I am filling this bug. Maybe I explained myself wrong, because what I'd expect is a better documentation on the function about the const ref parameter or to accept elements from the array memory space itself:

Checking the docs at https://docs.unrealengine.com/latest/INT/API/Runtime/Core/Containers/TArray/Remove/index.html

 int32 Remove
 (
     const ElementType & Item
 )

doesn't show how my code could be wrong.

avatar image Shadowriver Mar 14 '16 at 05:16 PM

Well don't expect that documentation tells you all case scenarios that may go wrong and that error is there to inform you :p having text description in check() is quite rare in engine code. Either way in order to raport bug you need to submit question in "Bug Raports" category, i will move your question there now (so you dont need to do anything ;).

(comments are locked)
10|2000 characters needed characters left
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question