I need a map whose keys are of some composite type T to a vector of iterators of the map itself.
(e.g., think of a graph in which every node holds iterators that point to its parents.)
The reason I'm avoiding storing the parents' keys as values is that the keys are expensive objects, so I'd like to store iterators instead.
Is such a thing possible?
If not, what's the best alternative?
(I know I can use polymorphism and type erasure as a brute-force way to solve this, but I'm wondering if there's a better way.)
Due to 23.2.1 General container requirements:
Containers are objects that store other objects. They control allocation
and deallocation of these objects through constructors, destructors,
insert and erase operations.
Hence it is possible:
struct Key;
struct Value;
typedef std::map<Key, Value> Map;
struct Key {};
struct Value {
std::vector<Map::iterator> parents;
};
(All types and sizes are known)
You most likely want to store a smart pointer to the parent rather than an iterator.
Related
Boost.Intrusive can get an Iterator out of an Object-Ref or Object-Pointer in constant time (see here: https://www.boost.org/doc/libs/1_72_0/doc/html/intrusive/usage_when.html). How does that work? Why is this not possible for standard containers?
Intrusive containers by definition have the information contained inside of elements to know how they're located in the container. A simple example is an intrusive linked list:
struct Object {
Object* next;
int some_data;
};
Obviously, if I have a reference or pointer to Object, I can easily find the next field, and from there, move to the next element, this is just accessing a member, which is O(1), thus constant time iterators.
With non-intrusive containers, it would look like this:
struct Object {
int some_data;
};
Suppose I have a std::vector of these, and a pointer or reference to Object, I can't work backwards from that to where it is in the std::vector without scanning the container to find it (a O(n) operation).
The C++ STL apparently is missing an ordered tree data structure. See here. Boost is also missing an ordered tree, but it does have an "un"ordered one, Property Tree where the data is ordered by insertion. I want the order to be irrespective of memory.
The boost page on Property Trees says that this is conceptually the boost::ptree structure.
struct ptree
{
data_type data; // data associated with the node
list< pair<key_type, ptree> > children; // ordered list of named children by insertion
};
I want to extend boost to keep track of order.
Is this the correct way?
class ordered_ptree : public boost::property_tree::ptree {
public:
ordered_ptree(int id) : _id{id}{};
protected:
int _id;
};
(From the comments in your question, I understand you want something like Python's OrderedDict but taking into account keys' relative order.)
Since none of the standard library's (or boost's) containers are exactly what you want, you might want to extend std::map (especially if you don't need all of the interface).
Say you start with
template<
typename Key,
typename Value,
class Compare=std::less<Key>,
class Alloc=std::allocator<pair<const Key, Value> >
class ordered_map
{
// This needs to be filled.
};
Now inside, you can hold an insertion counter:
std::size_t m_ins_count;
which is initialized to 0 and incremented at each insert.
Internally, your new keys will be std::pairs of the original key and the insertion count. Standard properties of binary search trees imply that nodes with keys differing only by the second pair item (which is the insertion count), will be consecutive in an in-order walk, which means that
you retain order of different keys
you retain order of insertion within a key
the operations are logarithmic time
traversing same-key items is (amortized) linear time
So, internally you'd have something like
typedef
std::map<
std::pair<Key, std::size_t>,
Value,
lex_compare<Compare>,
std::allocator<std::pair<std::pair<Key, std::size_t>, Value> >
internal_map_t;
(where lex_compare<Compare> compares first by the given functor, then by the insertion index).
Now you can choose a (minimal) interface, and implement it, by translating keys in the "outer world" and pairs of keys + insertion indices in the "inner world" of the tree.
If you plan on providing an iterator interface as well, you might find the boost iterator library useful, as you simply want to modify std::map's iterators.
I'm trying to create a template Graph Class, so I need to store Edges somehow. I thought, that it might be great if I can access EdgeValue by two Node smart pointers. But I don't actually know, how make it work. Now it's something like this:
template <class Node, class EdgeValue>
class Graph
{
typedef std::shared_ptr < Node > NodePtr;
std::map < std::pair < NodePtr, NodePtr > , EdgeValue> Edges;
}
But I'm pretty sure, it would not work. Should I create compare class or function? Should it be template? And actually, how to compare smart pointers?
So std::pair has an operator< that orders its contents lexographically.
It first sorts by the first element unless the first element is equal: if so, it sorts by the second.
This is sort of like how we sort two letter words. (std::tuple extends this to n-length elements).
std::shared_ptr orders itself (the operator< is sometimes called the "ordering" operator) by the raw pointer it stores (technically by std::less on the pointer it stores, because < on pointers is not guaranteed to be very well behaved, while std::less is guaranteed to be well behaved).
Between these two, what < on std::pair< std::shared_ptr, std::shared_ptr > does is sort by the object identity of the first, then the second, element of the pair. Which, in the case of Nodes in a graph, is often what you want.
If you wanted to order by the contents of the Node, and not the identity of the Node, you'd have to provide a comparison function to your std::map (or, in theory, override operator<, but I wouldn't do that on a two-layer std construct on primitive types).
I'm in need of a container that has the properties of both a vector and a list. I need fast random access to elements within the container, but I also need to be able to remove elements in the middle of the container without moving the other elements. I also need to be able to iterate over all elements in the container, and see at a glance (without iteration) how many elements are in the container.
After some thought, I've figured out how I could create such a container, using a vector as the base container, and wrapping the actual stored data within a struct that also contained fields to record whether the element was valid, and pointers to the next/previous valid element in the vector. Combined with some overloading and such, it sounds like it should be fairly transparent and fulfill my requirements.
But before I actually work on creating yet another container, I'm curious if anyone knows of an existing library that implements this very thing? I'd rather use something that works than spend time debugging a custom implementation. I've looked through the Boost library (which I'm already using), but haven't found this in there.
If the order does not matter, I would just use a hash table mapping integers to pointers. std::tr1::unordered_map<int, T *> (or std::unordered_map<int, unique_ptr<T>> if C++0x is OK).
The hash table's elements can move around which is why you need to use a pointer, but it will support very fast insertion / lookup / deletion. Iteration is fast too, but the elements will come out in an indeterminate order.
Alternatively, I think you can implement your own idea as a very simple combination of a std::vector and a std::list. Just maintain both a list<T> my_list and a vector<list<T>::iterator> my_vector. To add an object, push it onto the back of my_list and then push its iterator onto my_vector. (Set an iterator to my_list.end() and decrement it to get the iterator for the last element.) To lookup, look up in the vector and just dereference the iterator. To delete, remove from the list (which you can do by iterator) and set the location in the vector to my_list.end().
std::list guarantees the elements within will not move when you delete them.
[update]
I am feeling motivated. First pass at an implementation:
#include <vector>
#include <list>
template <typename T>
class NairouList {
public:
typedef std::list<T> list_t;
typedef typename list_t::iterator iterator;
typedef std::vector<iterator> vector_t;
NairouList() : my_size(0)
{ }
void push_back(const T &elt) {
my_list.push_back(elt);
iterator i = my_list.end();
--i;
my_vector.push_back(i);
++my_size;
}
T &operator[](typename vector_t::size_type n) {
if (my_vector[n] == my_list.end())
throw "Dave's not here, man";
return *(my_vector[n]);
}
void remove(typename vector_t::size_type n) {
my_list.erase(my_vector[n]);
my_vector[n] = my_list.end();
--my_size;
}
size_t size() const {
return my_size;
}
iterator begin() {
return my_list.begin();
}
iterator end() {
return my_list.end();
}
private:
list_t my_list;
vector_t my_vector;
size_t my_size;
};
It is missing some Quality of Implementation touches... Like, you probably want more error checking (what if I delete the same element twice?) and maybe some const versions of operator[], begin(), end(). But it's a start.
That said, for "a few thousand" elements a map will likely serve at least as well. A good rule of thumb is "Never optimize anything until your profiler tells you to".
Looks like you might be wanting a std::deque. Removing an element is not as efficient as a std::list, but because deque's are typically created by using non-contiguous memory "blocks" that are managed via an additional pointer array/vector internal to the container (each "block" would be an array of N elements), removal of an element inside of a deque does not cause the same re-shuffling operation that you would see with a vector.
Edit: On second though, and after reviewing some of the comments, while I think a std::deque could work, I think a std::map or std::unordered_map will actually be better for you since it will allow the array-syntax indexing you want, yet give you fast removal of elements as well.
So I have some legacy code which I would love to use more modern techniques. But I fear that given the way that things are designed, it is a non-option. The core issue is that often a node is in more than one list at a time. Something like this:
struct T {
T *next_1;
T *prev_1;
T *next_2;
T *prev_2;
int value;
};
this allows the core have a single object of type T be allocated and inserted into 2 doubly linked lists, nice and efficient.
Obviously I could just have 2 std::list<T*>'s and just insert the object into both...but there is one thing which would be way less efficient...removal.
Often the code needs to "destroy" an object of type T and this includes removing the element from all lists. This is nice because given a T* the code can remove that object from all lists it exists in. With something like a std::list I would need to search for the object to get an iterator, then remove that (I can't just pass around an iterator because it is in several lists).
Is there a nice c++-ish solution to this, or is the manually rolled way the best way? I have a feeling the manually rolled way is the answer, but I figured I'd ask.
As another possible solution, look at Boost Intrusive, which has an alternate list class a lot of properties that may make it useful for your problem.
In this case, I think it'd look something like this:
using namespace boost::intrusive;
struct tag1; struct tag2;
typedef list_base_hook< tag<tag1> > base1;
typedef list_base_hook< tag<tag2> > base2;
class T: public base1, public base2
{
int value;
}
list<T, base_hook<base1> > list1;
list<T, base_hook<base2> > list2;
// constant time to get iterator of a T item:
where_in_list1 = list1.iterator_to(item);
where_in_list2 = list2.iterator_to(item);
// once you have iterators, you can remove in contant time, etc, etc.
Instead of managing your own next/previous pointers, you could indeed use an std::list. To solve the performance of remove problem, you could store an iterator to the object itself (one member for each std::list the element can be stored in).
You can extend this to store a vector or array of iterators in the class (in case you don't know the number of lists the element is stored in).
I think the proper answer depends on how performance-critical this application is. Is it in an inner loop that could potentially cost the program a user-perceivable runtime difference?
There is a way to create this sort of functionality by creating your own classes derived from some of the STL containers, but it might not even be worth it to you. At the risk of sounding tiresome, I think this might be an example of premature optimization.
The question to answer is why this C struct exists in the first place. You can't re-implement the functionality in C++ until you know what that functionality is. Some questions to help you answer that are,
Why lists? Does the data need to be in sequence, i.e., in order? Does the order mean something? Does the application require ordered traversal?
Why two containers? Does membership in the container indicated some kind of property of the element?
Why a double-linked list specifically? Is O(1) insertion and deletion important? Is reverse-iteration important?
The answer to some or all of these may be, "no real reason, that's just how they implemented it". If so, you can replace that intrusive C-pointer mess with a non-intrusive C++ container solution, possibly containing shared_ptrs rather than ptrs.
What I'm getting at is, you may not need to re-implement anything. You may be able to discard the entire business, and store the values in proper C++ containers.
How's this?
struct T {
std::list<T*>::iterator entry1, entry2;
int value;
};
std::list<T*> list1, list2;
// init a T* item:
item = new T;
item->entry1 = list1.end();
item->entry2 = list2.end();
// add a T* item to list 1:
item->entry1 = list1.insert(<where>, item);
// remove a T* item from list1
if (item->entry1 != list1.end()) {
list1.remove(item->entry1); // this is O(1)
item->entry1 = list1.end();
}
// code for list2 management is similar
You could make T a class and use constructors and member functions to do most of this for you. If you have variable numbers of lists, you can use a list of iterators std::vector<std::list<T>::iterator> to track the item's position in each list.
Note that if you use push_back or push_front to add to the list, you need to do item->entry1 = list1.end(); item->entry1--; or item->entry1 = list1.begin(); respectively to get the iterator pointed in the right place.
It sounds like you're talking about something that could be addressed by applying graph theory. As such the Boost Graph Library might offer some solutions.
list::remove is what you're after. It'll remove any and all objects in the list with the same value as what you passed into it.
So:
list<T> listOne, listTwo;
// Things get added to the lists.
T thingToRemove;
listOne.remove(thingToRemove);
listTwo.remove(thingToRemove);
I'd also suggest converting your list node into a class; that way C++ will take care of memory for you.
class MyThing {
public:
int value;
// Any other values associated with T
};
list<MyClass> listOne, listTwo; // can add and remove MyClass objects w/o worrying about destroying anything.
You might even encapsulate the two lists into their own class, with add/remove methods for them. Then you only have to call one method when you want to remove an object.
class TwoLists {
private:
list<MyClass> listOne, listTwo;
// ...
public:
void remove(const MyClass& thing) {
listOne.remove(thing);
listTwo.remove(thing);
}
};