KoreanFoodie's Study
언리얼에서 TMap 에 Compare Functor 지정하기 본문
언리얼에서 TMap 에 Compare Functor 지정하기
핵심 :
1. C++ 에서는 Functor class 를 넣어 주기만 하면 된다.
2. TMap 에서는 TMap 자체가 그냥 unordered_map 이라 그런 건 없다. 만약 Predicate 를 지정하고 싶으면, TSortedMap 을 사용하자!
3. TMap 을 정렬할 때는 Sort 를 사용하자... 후...
C++ 에서는 TMap 에서 Key 값을 이용한 정렬을 역순으로 하고 싶을 때, std::less 나 std::greater 같은 predicate 클래스를 사용했다.
혹은 아래와 같이 Functor class 를 만들어 3번째 인자에 넣어 주기만 하면 되었는데...
#include <iostream>
#include <map>
#include <string>
class MyCompare {
public:
bool operator()(const std::string& a, const std::string& b) const {
// Compare strings in reverse order
return a > b;
}
};
int main() {
std::map<std::string, int, MyCompare> myMap;
myMap["one"] = 1;
myMap["two"] = 2;
myMap["three"] = 3;
for (auto& p : myMap) {
std::cout << p.first << ": " << p.second << std::endl;
}
return 0;
}
언리얼 TMap 의 경우, 조금 복잡하다.
뭐 사실 크게 달라진 것은 없는데, 인자도 하나 추가되었고, 만족해야 하는 조건이 조금 추가된 정도이다.
바로 본론으로 가서, 이를 적용하는 코드는 아래와 같다 :
// 템플릿 타입 설명 :
// 첫번째 MyKeyType 은 키값의 타입을 넣는다. int 같은거.
// 두번째의 FMyValueType 은 값의 타입.
// 세 번째 인자는 TMap 의 경우 false, TMultiMap 의 경우 true 이다. bInAllowDuplicateKeys 인데, 자세한건 검색...
template <typename KeyType, typename ValueType, bool bInAllowDuplicateKeys>
struct FMyKeyComparator : TDefaultMapHashableKeyFuncs<KeyType, ValueType, bInAllowDuplicateKeys>
{
bool operator()(const KeyType& A, const KeyType& B) const
{
// Custom comparison logic here
return A > B;
}
};
using TCustomMapType = TMap<MyKeyType, TSharedPtr<FMyValueType>, FDefaultSetAllocator,
FMyKeyComparator<MyKeyType, TSharedPtr<FMyValueType>, false>>;
/* */
// 편-안
TCustomMapType myMap;
// 만약 KeyType 에 int 같은 Primitive Type 이외에 직접 정의한 녀석을 넣는다면,
// 따로 GetTypeHash 를 Override 해야 할 것으로 보인다. Primitive Type 들은 GetTypeHash 가 정의되어 있을 듯.
위 코드를 적용하면 원하는 대로 Functor 가 들어간 TMap 을 만들 수 있을 것이다 😉
(물론 보통 TMap 의 정렬이 필요할 때는 그냥 sort 를 써 주어야 하는 것으로 보인다.)
하지만... 실제로 정렬은 되지 않는다. 왜냐면, TMap 은 unordered_map 같은 녀석이라, operator() 를 삽입/삭제 시에 호출하지는 않기 때문이다....
대안으로, TSortedMap 을 사용하여 우리가 원했던 Predicate 를 Functor 로 넣을 수도 있다 :
/**
* A Map of keys to value, implemented as a sorted TArray of TPairs.
*
* It has a mostly identical interface to TMap and is designed as a drop in replacement. Keys must be unique,
* there is no equivalent sorted version of TMultiMap. It uses half as much memory as TMap, but adding and
* removing elements is O(n), and finding is O(Log n). In practice it is faster than TMap for low element
* counts, and slower as n increases, This map is always kept sorted by the key type so cannot be sorted manually.
*/
template <typename KeyType, typename ValueType, typename ArrayAllocator /*= FDefaultAllocator*/, typename SortPredicate /*= TLess<KeyType>*/ >
class TSortedMap
/* ... */
하지만 TSortedMap 은 조금 느린 듯 하다. 삽입/삭제가 O(n) 이라니? 탐색이 O(Log n) 이다. 🤨
그래서.. 그냥 TMap 을 쓰고, 필요할 때 Sort 를 쓰는게 좋을 것 같다.
TMap 에서 Sort Predicate 쪽이 궁금하면 '더보기'를 참고하자. 안되는 이유를 알 수 있다...
public:
/**
* Sorts the pairs array using each pair's Key as the sort criteria, then rebuilds the map's hash.
* Invoked using "MyMapVar.KeySort( PREDICATE_CLASS() );"
*/
template<typename PREDICATE_CLASS>
FORCEINLINE void KeySort(const PREDICATE_CLASS& Predicate)
{
Super::Pairs.Sort(FKeyComparisonClass<PREDICATE_CLASS>(Predicate));
}
/**
* Stable sorts the pairs array using each pair's Key as the sort criteria, then rebuilds the map's hash.
* Invoked using "MyMapVar.KeySort( PREDICATE_CLASS() );"
*/
template<typename PREDICATE_CLASS>
FORCEINLINE void KeyStableSort(const PREDICATE_CLASS& Predicate)
{
Super::Pairs.StableSort(FKeyComparisonClass<PREDICATE_CLASS>(Predicate));
}
/**
* Sorts the pairs array using each pair's Value as the sort criteria, then rebuilds the map's hash.
* Invoked using "MyMapVar.ValueSort( PREDICATE_CLASS() );"
*/
template<typename PREDICATE_CLASS>
FORCEINLINE void ValueSort(const PREDICATE_CLASS& Predicate)
{
Super::Pairs.Sort(FValueComparisonClass<PREDICATE_CLASS>(Predicate));
}
/**
* Stable sorts the pairs array using each pair's Value as the sort criteria, then rebuilds the map's hash.
* Invoked using "MyMapVar.ValueSort( PREDICATE_CLASS() );"
*/
template<typename PREDICATE_CLASS>
FORCEINLINE void ValueStableSort(const PREDICATE_CLASS& Predicate)
{
Super::Pairs.StableSort(FValueComparisonClass<PREDICATE_CLASS>(Predicate));
}
private:
/** Extracts the pair's key from the map's pair structure and passes it to the user provided comparison class. */
template<typename PREDICATE_CLASS>
class FKeyComparisonClass
{
TDereferenceWrapper< KeyType, PREDICATE_CLASS> Predicate;
public:
FORCEINLINE FKeyComparisonClass(const PREDICATE_CLASS& InPredicate)
: Predicate(InPredicate)
{}
FORCEINLINE bool operator()(const typename Super::ElementType& A, const typename Super::ElementType& B) const
{
return Predicate(A.Key, B.Key);
}
};
/** Extracts the pair's value from the map's pair structure and passes it to the user provided comparison class. */
template<typename PREDICATE_CLASS>
class FValueComparisonClass
{
TDereferenceWrapper< ValueType, PREDICATE_CLASS> Predicate;
public:
FORCEINLINE FValueComparisonClass(const PREDICATE_CLASS& InPredicate)
: Predicate(InPredicate)
{}
FORCEINLINE bool operator()(const typename Super::ElementType& A, const typename Super::ElementType& B) const
{
return Predicate(A.Value, B.Value);
}
};
TMap 의 정렬을 위해 Sort 를 해보자. 예제는 공식 문서에도 잘 나와 있다 😅
FruitMap.KeySort([](int32 A, int32 B) {
return A > B; // sort keys in reverse
});
// FruitMap == [
// { Key: 9, Value: "Melon" },
// { Key: 5, Value: "Mango" },
// { Key: 4, Value: "Kiwi" },
// { Key: 3, Value: "Orange" }
// ]
FruitMap.ValueSort([](const FString& A, const FString& B) {
return A.Len() < B.Len(); // sort strings by length
});
// FruitMap == [
// { Key: 4, Value: "Kiwi" },
// { Key: 5, Value: "Mango" },
// { Key: 9, Value: "Melon" },
// { Key: 3, Value: "Orange" }
// ]
TMap Sort... TMap 정렬은 위처럼 하자.
참고 : 언리얼 공식 문서 - TMap
'Game Dev > Unreal C++ : Dev Log' 카테고리의 다른 글
[언리얼] 스크롤박스에서 왼쪽 클릭으로 드래그하기 (0) | 2023.07.01 |
---|---|
[언리얼] UnrealVS 로 메모리 이슈 예방하기 (-stompMalloc) (0) | 2023.06.15 |
언리얼 에러 : Missing '*' in Expected a pointer type (0) | 2023.06.01 |
[언리얼] 언리얼 최적화 프로파일링 : 멀티 플랫폼을 위한 최적화 드라이브 (언리얼 서밋 2022) 요약 (0) | 2023.05.14 |
[언리얼] 언리얼 인사이트 : 언리얼 최적화 프로파일링 (1) | 2023.05.12 |