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"

Office Holiday

Epic Games' offices will be on holiday from June 22nd to July 7th. During this period support will be limited. Our offices will reopen on Monday, July 8th. 

Help With C++ Sort By Predicate

I am new to lambdas and predicates. I am trying to sort an array of sensed (via the UPerceptionComponent) actors by distance from the pawn.

 GetSensedActors().Sort([ControlledMob](const AActor*& LHS, const AActor*& RHS)
  { 
      float distanceOne = (LHS->GetActorLocation() - ControlledMob->GetActorLocation()).Size();
      float distanceTwo = (RHS->GetActorLocation() - ControlledMob->GetActorLocation()).Size()
      return  distanceOne > distanceTwo ; 
 });

This gives me a compiler error: note: see reference to function template instantiation 'void TArray::Sort>(const PREDICATE_CLASS &)' being compiled with [ PREDICATE_CLASS=AMythicAIController::FindClosestSensedEnemy:: ]

Thanks in advance!

Product Version: UE 4.15
Tags:
more ▼

asked May 31 '17 at 12:17 PM in C++ Programming

avatar image

Cetaurefie
11 3 4 6

(comments are locked)
10|2000 characters needed characters left
Viewable by all users

1 answer: sort voted first

I'd actually use a simple Struct with the () operator overloaded to help you here (I do something very similar in my own projects).

 struct FSortByDistance
 {
     FSortByDistance(const FVector& InSourceLocationg)
         : SourceLocation(InSourceLocation)
     {
 
     }
 
     /* The Location to use in our Sort comparision. */
     FVector SourceLocation;
 
     bool operator()(const AActor* A, const AActor* B) const
     {
         float DistanceA = FVector::DistSquared(SourceLocation, A.GetActorLocation());
         float DistanceB = FVector::DistSquared(SourceLocation, B.GetActorLocation());
 
         return DistanceA > DistanceB;
     }
 };

Then to use it with your Sort, you just call:

 GetSensedActors().Sort(FSortByDistance(ControlledMob->GetActorLocation()));
more ▼

answered May 31 '17 at 04:15 PM

avatar image

ExtraLifeMatt
1.6k 34 14 48

avatar image Cetaurefie May 31 '17 at 04:56 PM

Relatively new to C++ (about 1.5 years using it, completely self taught... I have big knowledge gaps). Just to make sure I understand correctly .Sort() is giving FSortByDistance AActor* A and AActor* B, correct?

Is this a better practice, in general, than using the lambda method?

avatar image ExtraLifeMatt May 31 '17 at 05:00 PM

Correct.

Think of Lamba's just as a function pointer, you're just setting that function pointer to the Struct's () operator. You don't have to use a struct, I just do if I need to pass parameters that aren't contained within the array as it's easier to read IMO and you can reuse that struct in other arrays (assuming they use the same types).

avatar image Steve Robb STAFF Jun 05 '17 at 05:29 PM

Actually, using a struct with state is identical to using a lambda with a capture. A lambda is not like a function pointer, though capture-less lambdas are convertible to function pointers.

The FSortByDistance code above is equivalent to the following with a lambda:

 FVector SourceLocation = ControlledMob->GetActorLocation();
 GetSensedActors().Sort([SourceLocation](const AActor& A, const AActor& B)
 {
     float DistanceA = FVector::DistSquared(SourceLocation, A.GetActorLocation());
     float DistanceB = FVector::DistSquared(SourceLocation, B.GetActorLocation());
 
     return DistanceA > DistanceB;
 });

But while lambdas are more than capable of expressing stateful predicates, you may still want to define a standalone FSortByDistance struct to allow code reuse.

However, note that I have changed the code above to pass AActor by reference instead of by pointer. This is due to a legacy decision that causes TArray::Sort() to auto-deference pointers, and will have been the reason for your compile errors. Unfortunately, we can't realistically revert that behaviour now, so instead, you should prefer to use Algo::Sort over TArray::Sort, as this correctly passes pointers to the predicate as expected:

 struct FSortByDistance
 {
     explicit FSortByDistance(const FVector& InSourceLocation)
         : SourceLocation(InSourceLocation)
     {
     }
 
     /* The Location to use in our Sort comparison. */
     FVector SourceLocation;
 
     bool operator()(const AActor* A, const AActor* B) const
     {
         float DistanceA = FVector::DistSquared(SourceLocation, A->GetActorLocation());
         float DistanceB = FVector::DistSquared(SourceLocation, B->GetActorLocation());
 
         return DistanceA > DistanceB;
     }
 };

 Algo::Sort(GetSensedActors(), FSortByDistance(ControlledMob->GetActorLocation()));

Hope this helps,

Steve

(comments are locked)
10|2000 characters needed characters left
Viewable by all users
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