I have a list of items that are created each frame and need to be sorted.
Each Item's first member variable to sort by is an unordered_set.
I've moved this to an ordered set everywhere in the system so I can sort it in the list of items. But I'm suffering a performance hit in another are of the code foe this.
Bearing in mind that each item will be destroyed and be recreated on a per-frame basis, is there anything I can do to hold these in unordered_sets and sort them?
class item
{
public:
unordered_set< int > _sortUS;
int _sortI;
//Other members to sort
bool operator<( const item& that ) const
{
if( _sortI != that._sortI )
{
return _sortI < that._sortI;
}
else if( _sortUS != that._sortUS )
{
return ??? // this is what I need. I don't know how to compare these without converting them to sets
}
}
};
Given std::unordered_set<Key, Hash> for arbitrary hashable Key, you could define
template<class Key, class Hash = std::hash<Key>>
bool operator< (std::unordered_set<Key, Hash> const& L, std::unordered_set<Key, Hash> const& R)
{
return std::lexicographical_compare(
begin(L), end(L), begin(R), end(R),
[](Key const& kL, Key const& kR) {
return Hash()(kL) < Hash()(kR);
});
}
which will use the ordering on hash indices of Key. You can then define an ordering on item
bool operator< (item const& L, item const& R)
{
return std::tie(L.sortI, L.sortUS) < std::tie(R.sortI, R.sortUS);
}
and std::tie will make a std::tuple out of references to the members of your item so that you can use the operator< from std::tuple.
NOTE: you can easily prove that the above comparison is a StrictWeakOrder (a requirement for std::sort) since both the std::tuple comparison and the lexicographical_compare have this property.
However, the ordering of unordered_set is very unusual in other respects.
the hashed key index doesn't correspond to the order in which you iterate over elements (there is some modulo operation that maps hashed keys to indices in the container)
adding elements to an unordered_set can result in rehashing and invalidation of previous orderering
Related
I'm calling unordered_map::emplace() and I am storing the returned value (a pair). I just want to access the inserted value from the pair but for the life of me I cannot figure out the correct configuration of this confusing pair.
My unordered map definition:
std::unordered_map<GUID, shared_ptr<Component>> components;
I've looked at the unordered_map::emplace() documentation; according to this the first element in the pair should be the shared_ptr<Component> but the compiler is just not happy.
In the below code I get the error: Error 2 error C2227: left of '->gUid' must point to class/struct/union/generic type
class Component {
public:
template<typename T, typename... Params>
GUID addComponent(Params... params)
{
auto cmp = Component::create<T>(params...);
auto res = components.emplace(cmp->gUid, cmp);
if (!res.second) {
GUID gUid;
getNullGUID(&gUid);
return gUid;
}
return (*res.first)->gUid; // compiler error here
// *Yes I know I can do: return cmp->gUid;
}
GUID gUid; // initialised in constructor
std::unordered_map<GUID, std::shared_ptr<Component>> components;
};
Any idea how to correctly access the pairs second value?
The first of the pair returned from emplace is an iterator -- which, for unordered_map, acts like a pointer to a pair<key, value>. So to get the value from that pair, you need second:
return res.first->second->gUid;
I was trying to implement singly linked list using share_ptr. Here is the implementation...
Below is the node class...
template<typename T>
class Node
{
public:
T value;
shared_ptr<Node<T>> next;
Node() : value(0), next(nullptr){};
Node(T value) : value(value), next(nullptr){};
~Node() { cout << "In Destructor: " << value << endl; };
};
Below is the linked list class...
template<typename T>
class LinkedList
{
private:
size_t m_size;
shared_ptr<Node<T>> head;
shared_ptr<Node<T>> tail;
public:
LinkedList() : m_size(0), head(nullptr) {};
void push_front(T value)
{
shared_ptr<Node<T>> temp = head;
head = make_shared<Node<T>>(Node<T>(value));
head->next = temp;
m_size++;
if (m_size == 1)
tail = head;
}
void pop_front()
{
if (m_size != 0)
{
// Here I am having doubt------------------------!!!
//shared_ptr<Node<T>> temp = head;
head = head->next;
m_size--;
if (m_size == 0)
tail = nullptr;
}
}
bool empty()
{
return (m_size == 0) ? true : false;
}
T front()
{
if (m_size != 0)
return head->value;
}
};
My question is, am I using the shared_ptr properly for allocating a node? If not, how should I use the shared_ptr to allocate and how should I delete the node in the pop_front method?
I believe this belongs on code review.
Most importantly: Why are you using shared_ptr? shared_ptr means the ownership of an object is unclear. This is not the case for linked lists: Every node owns the next. You can express that using unique_ptr which is easier and more efficient.
pop_front seems to be functioning correctly. You may consider throwing an exception or an assertion instead of doing nothing when using pop_front on an empty list.
front is more problematic. If the list is empty you most likely get a garbage object.
What is the significance of tail? It does not seem to be used for anything and since you cannot go backwards there is no real point to getting the tail.
make_shared<Node<T>>(Node<T>(value)) should be make_shared<Node<T>>(value) instead. make_shared<Node<T>>(value) creates a Node using value as the parameter for the constructor. make_shared<Node<T>>(Node<T>(value)) creates a Node with value as the parameter and then creates a new Node with the temporary Node as parameter and then destroys the first Node.
You are missing the copy and move constructor and assignment and move assignment operators.
After you are satisfied with your list implementation consider using std::forward_list instead.
I have this code which works fine in VS 2013 but doesn't compile in either GCC 4.8 or clang 3.3!
AND_end(c)->next = new ListNode<Point>{ b->val };
The error message is the following: "cannot convert from "Point" to "int".
Now, gradually, member val of b is a Point:
struct Point
{
int x;
int y;
double distance(const Point& other) const
{
if (this == &other)
return 0.;
return std::sqrt(std::pow(other.y - y, 2.) + std::pow(other.x - x, 2.));
}
bool operator==(const Point& other)
{
return x == other.x && y == other.y;
}
bool operator!=(const Point& other)
{
return !(*this == other);
}
};
b is a Line:
using Line = ListNode<Point>*;
a ListNode is a typical node for a singly linked list:
template<typename T>
struct ListNode
{
T val; // Value
ListNode* next = nullptr; // Next node in the list
// Constructor: takes a value of type T and optionally a pointer to the next node
explicit ListNode(T v, ListNode* n = nullptr)
: val{ v }, next{ n }
{
// Empty body, both member variables are initialized already
}
};
So, the line of code that doesn't compile should do the following: create a new ListNode, with T = Point, by supplying to the explicit ListNode constructor its first (and only) argument T v, which is a Point (b->val is a Point). This argument will be copied into the ListNode member val by copy, using the default copy constructor.
What seems to happen in both GCC and clang is that b->val is supplied to the Point constructor, hence the error message above (and for the sake of completeness, and additional warning is given: "missing field 'y' initializer").
VC++12 seems to get it all right instead.
So, what's up? Am I missing anything obvious (maybe, happens from time to time) or is there a nasty problem here?
I think the problem is, you do not have copy constructor for Point, therefore, in this line,
explicit ListNode(T v, ListNode* n = nullptr)
: val{ v }, next{ n }
since there's no copy constructor, val{v} will try to initialize by aggregate.
From 8.5.1,
An aggregate is an array or a class (Clause 9) with no user-provided
constructors.
When an aggregate is initialized by an initializer list,
as specified in 8.5.4, the elements of the initializer list are taken
as initializers for the members of the aggregate, in increasing
subscript or member order. Each member is copy-initialized from the
corresponding initializer-clause.
For a point type, the aggregate initialization shall be val {v.x, v.y}.
Or, you can implement a copy constructor for Point class.
GCC & Clang are correct. VS is wrong and it should reject your code.
I have looked over this thread which talks about using this method for comparison:
struct thing
{
int a;
char b;
bool operator<(const thing &o) const
{
return a < o.a;
}
};
priority_queue<thing> pq;
On the other hand other uses method such as this:
struct Time {
int h;
int m;
int s;
};
class CompareTime {
public:
bool operator()(Time& t1, Time& t2) // Returns true if t1 is earlier than t2
{
if (t1.h < t2.h) return true;
if (t1.h == t2.h && t1.m < t2.m) return true;
if (t1.h == t2.h && t1.m == t2.m && t1.s < t2.s) return true;
return false;
}
}
priority_queue<Time, vector<Time>, CompareTime> pq;
While I logic myself with the first method, I don't quit understand the second method. Mostly because of the syntax. I am not quit sure what the overloading operator operator() means. What is that operator overloading?
Also, from cplusplus on priority_queue, I don't quite understand the following, mainly the second parameter.
template < class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;
In another word, I don't understand the second method and its calling convention.
Also, what's the difference and which method is preferred?
I am not quit sure what the overloading operator operator() means.
What is that operator overloading?
What we have here is an overloading of the function call operator (see SO question) , this means that client code can 'treat' the CompareTime class instances as compare functions :
CompareTime ct;
if ( ct(t1, t2) )
{
...
}
I don't quite understand the following, mainly the second parameter.
The cplusplus reference summarizes quite well , the template parameters :
0 arg - The type of the objects within the queue.
1 arg - The underlying container/data structure for the queue, by
default its the std vector
2 arg - Operation on priority queue relies on some precedence
comparison, i.e. which item in the queue should be 'before' other item (see also Wikipedia , so this arg accepts to have compare object (functor) which mean instances of plain class which overload the () operator , the default is the std less functor which is simply a wrapper above the '<' semantics (boolean 2 valued function object).
// TEMPLATE STRUCT less
template<class _Ty>
struct less : public binary_function<_Ty, _Ty, bool>
{
// functor for operator<
bool operator()(const _Ty& _Left, const _Ty& _Right) const
{
// apply operator< to operands
return (_Left < _Right);
}
};
Our game engine uses STL sort for sorting a whole range of different objects using the template sort function. As I understand the requirements for the comparison logic is that you have to write it on the basis that it may internally do a reverse sort ( ie reverse the pairing eg. from (a,b) to (b,a) ).
So typically my compare functions looks like this:
bool CompareSubGroupReqsByDescendingFillPriority::operator()
( const ScenSubGroupReq& lhs,
const ScenSubGroupReq& rhs ) const
{
if( lhs.mFillPriority > rhs.mFillPriority ) return true;
else if( lhs.mFillPriority < rhs.mFillPriority ) return false;
else return lhs.mForceGroup->ObjectID() > rhs.mForceGroup->ObjectID();
}
I refer to the "else" statement as the "deal breaker" - ie. it must be able to resolve a case where both lhs and rhs are the same. I typically use the object ID where we are sorting persistent objects.
My question is how can you create a deal breaker when you are sorting non-persistent objects that are simple data types (eg shorts)?
Here is the example I am wrestling with:
bool
ComparePhaseLineIndexesByAscendingValue::operator() ( const short lhs,
const short rhs ) const
{
if( lhs < rhs ) return true;
else if( lhs > rhs ) return false;
else
{
// should never be here as no two phase lines should have the same index
FPAssert( false );
return false;
}
}
Trouble is I have been testing this and found a valid case where where I can have two phase lines with the same index. I don't care which of the entries with the same value ends up first.
What would you advise?
Technically the sort function takes the less than operator. What you are trying to do seems to have something to do with making sure that even equal objects are returned in a specific order. Generally you would just do
bool
ComparePhaseLineIndexesByAscendingValue::operator() ( const short lhs,
const short rhs ) const
{
return lhs < rhs;
}
Though generally a comparison function isn't required for builtin types (I think it's any type with a < operator specified).