Implement Map in C++ with a predefined size - c++

I want to implement my own version of a map data structure in C++.
I need its size to be predefined so I decided that when the user creates the map he will have to specify not only the size, but also a default value for the keys and for the values.
before you ask - I need this in order to be able to use that data structore in an embedded system.
I have written this constructor:
template <typename T, typename K>
myMap<T, K>::myMap(int size, const T& keyInit, const K& valueInit) :
size(nSize), defaultKey(keyInit), defaultValue(valueInit)
{
for (int i = 0; i < nSize; i++)
{
container.insert(std::make_pair(keyInit,valueInit));
}
}
where container is a member of type: std::map<T, K> (and basically the class is just a wrapper for the stl map)
now I'm not sure how to implement the insert function. aside from that I have figured out how to delete (reassgin default values) and how to search (use stl's maps find).
So my only problem right now is the insert method.
I thought about iterating through the map and looking for the first free cell but it got me confused and I' stuck.
Any other ideas would be great.

If I've understood well, you make a wrapper for a standard container, i.e. a map.
At construction, you intend to insert several the same default key. This is not allowed in a std::map, where the key MUST be unique.
If you want to use several time the same key in a map, you have to use a multimap: you can use it almost as a map, but it allows to have duplicate keys.
I'm not sure why you create items with a default key, but you have to be aware that maps and multimaps work with ordered keys. So if you intend later to replace the default key with another one, you can't just replace the values: you have to delete the entry and insert a new one.

Key in std::map is unique, so what you do at construction does not make sense.
If you seek great performance (as this may be the case in embedded world), then I would advise to find some other hash table implementation.
You can also implement your own hash table to meet your needs.

For an embedded system what I think you actually want is custom memory allocators. You can define an allocation pool for each data structure you want to use. Then you pass it to the constructor for the map and it will allocate data from the pool. When the pool is empty it will send out bad_alloc exceptions.

Related

std::unordered_set::find - construct an instance only for find()

A lot of times I see my key is actually inside my value.
For example:
struct Elem {
int key;
// ... Other variables ...
}
That makes me want to use std::unordered_set instead of std::unordered_map, because I already have the key stored inside my value - no need to waste more place for std::unordered_map's .first (key).
Then I start implementing with std::unordered_set and get to the place I need to perform a find() over my std::unordered_set.
Then I realize I need to create an empty-shell Elem so I would be able to find(), beacuse std::unordered_set::find gets a Key for input
template < class Key, // unordered_set::key_type/value_type
class Hash = hash<Key>, // unordered_set::hasher
class Pred = equal_to<Key>, // unordered_set::key_equal
class Alloc = allocator<Key> // unordered_set::allocator_type
> class unordered_set;
Sometimes building an empty-shell Elem is hard / wasteful / maybe even not possible?
For example, when my key/value is
An iterator
A reference
A class with specific c'tor (not constructing the instance only with the key)
Q. Am I missing something?
Q. Is there a way to do find() that isn't wasteful? I mean that doesn't make me create an instance I didn't want to
Something really strange to me - that I already should have the element I'm looking for in order to find it, or at least an empty-shell of it.
When choosing a data structure to hold your data you need to consider your use case.
If you want to look up data from a key you should use a map. If you just want to store unique values in a collection and you don't need to look them up use set.
I don't see why its so much trouble to insert a element as map.emplace_back(elem.key, elem) vs set.emplace_back(elem) if it means that down the road you can just query the elem as map.at(key) or map[key] vs having create an empty elem.
Besides, std::set does the whole key thingamajig (roughly) underwater anyway. (source: What is the difference between set vs map in C++?)

Proper Qt data structure for storing and accessing struct pointers

I have a certain struct:
struct MyClass::MyStruct
{
Statistics stats;
Oject *objPtr;
bool isActive;
QDateTime expiration;
};
For which I need to store pointers to in a private container. I will be getting objects from client code for which I need to return a pointer to the MyStruct. For example:
QList<MyStruct*> MyClass::structPtr( Statistics stats )
{
// Return all MyStruct* for which myStruct->stats == stats (== is overloaded)
}
or
QList<MyStruct*> MyClass::structPtr( Object *objPtr )
{
// Return all MyStruct* for which myStruct->objPtr == objPtr
}
Right now I'm storing these in a QLinkedList<MyStruct*> so that I can have fast insertions, and lookups roughly equivalent to QList<MyStruct*>. Ideally I would like to be able to perform lookups faster, without losing my insertion speed. This leads me to look at QHash, but I am not sure how I would use a QHash when I'm only storing values without keys, or even if that is a good idea.
What is the proper Qt/C++ way to address a problem such as this? Ideally, lookup times should be <= log(n). Would a QHash be a good idea here? If so, what should I use for a key and/or value?
If you want to use QHash for fast lookups, the hash's key type must be the same as the search token type. For example, if you want to find elements by Statistics value, your hash should be QHash<Statistics, MyStruct*>.
If you can live with only looking up your data in one specific way, a QHash should be fine for you. Though, in your case where you're pulling lists out, you may want to investigate QMultiHash and its .values() member. However, it's important to note, from the documentation:
The key type of a QHash must provide operator==() and a global hash function called qHash()
If you need to be able to pull these lists based on different information at different times you might just be better off iterating over the lists. All of Qt's containers provide std-style iterators, including its hash maps.

Is it possible to change the comparator of a C++ std::set?

I have a set of data which in some occasion I need to sort them in one way and some occasion in another way. For example, suppose the data set is a set of strings,{"abc", "dfg",...}. Sometimes I need to sort them in alphabetic order and sometimes by comparing their length.
Initially I used std::set as a container of my data and implemented 2 comparators, hoping that I can change the comparator of the set on the fly, cause the data is huge and it's not a good idea to copy it from one set to another..I just want to sort it using different comparators from time to time. Is this possible or what's the right way to do it?
You have to specify the comparator of std::set at construction time.
As a solution, I would maintain two 'index' sets instead, each referring to the actual collection. This would yield the greatest flexibility. To keep everything together, I suggest you wrap it up in a single class:
// to be compiled, debugged etc..., but ideal
// to grab the idea
// caveats: maintain the index objects whenever the collection
// gets resized/reallocated etc...
// so not to be written yourself, use an existing library :)
template< typename T, typename comp1, typename comp2 >
struct MultiIndex {
std::deque<T> collection;
std::set<T*, comp1> index1;
std::set<T*, comp2> index2;
void insert( const T& t ){
collection.push_back(t);
index1.insert( &collection.back() );
index2.insert( &collection.back() );
}
};
Boost library has such a class: Multiindex.
The set is internally always kept sorted (otherwise you wouldn't have the needed performance), so no, the comparator can't be changed. What I think the best solution here is to maintain two sets with same data, but with different comparators. I'd encapsulate the two sets in a class and have the functions like insertion work on both sets to ensure the data is the same on both sets.
If you only don't need to have the data sorted all the time, another way to accomplish what you want would be to simply use e.g. a vector and sort by whichever comparator you need when necessary.
No, not on the fly. The tree is built based on the sort criteria specified at construction time. You are talking about building multiple indexes into a single dataset, which could be accomplished with multiple sets. There are probably lots of libs like boost which have something already created for this.

C++: insert into std::map without knowing a key

I need to insert values into std::map (or it's equivalent) to any free position and then get it's key (to remove/modify later). Something like:
std::map<int, std::string> myMap;
const int key = myMap.insert("hello");
Is it possibly to do so with std::map or is there some appropriate container for that?
Thank you.
In addition to using a set, you can keep a list of allocated (or free)
keys, and find a new key before inserting. For a map indexed by
int, you can simply take the last element, and increment its key. But
I rather think I'd go with a simple std::vector; if deletion isn't
supported, you can do something simple like:
int key = myVector.size();
myVector.push_back( newEntry );
If you need to support deletions, then using a vector of some sort of
"maybe" type (boost::optional, etc.—you probably already have
one in your toolbox, maybe under the name of Fallible or Maybe) might be
appropriate. Depending on use patterns (number of deletions compared to
total entries, etc.), you may want to search the vector in order to
reuse entries. If your really ambitious, you could keep a bitmap of the
free entries, setting a bit each time you delete and entry, and
resetting it whenever you reuse the space.
You can add object to an std::set, and then later put the whole set into a map. But no, you can't put a value into a map without a key.
The closest thing to what you're trying to do is probably
myMap[myMap.size()] = "some string";
The only advantage this has over std::set is that you can pass the integer indexes around to other modules without them needing to know the type of std::set<Foo>::iterator or similar.
It is impossible. Such an operation would require intricate knowledge of the key type to know which keys are available. For example, std::map would have to increment int values for int maps or append to strings for string maps.
You could use a std::set and drop keying altogether.
If you want to achieve something similar to automatically generated primary keys in SQL databases than you can maintain a counter and use it to generate a unique key. But perhaps std::set is what you really need.

Which sorted STL container to use for fast insert and find with a special key?

I have some data with a key associated with each data item. The key is made of two parts: let's call them color and id. I want to iterate the container by color to speed up rendering and I also want to find items in the container by id alone.
I tried using std::map for this with a key
class MyKey {
public:
int color;
int id;
bool operator<(...)
bool operator==(...)
};
But I cannot provide a < operator that would keep the data sorted by color and at the same time allow map::find to work on id alone (i.e. no information about color).
I want both the insert and find operations to be fast (e.g. O(log(n))).
Any ideas what kind of container I could use to implement this?
Adapt the example here from Boost.Multi_index based on the following modifications:
typedef multi_index_container<
MyKey,
indexed_by<ordered_unique<identity<MyKey> >,
ordered_non_unique<member<MyKey,int,&MyKey::color> >
>
> DataSet;
Try Boost.BiMap, it's a map with 2 views.
Otherwise there is the more complicated Boost.MultiIndex.
If you'd rather write your own than use Boost (dumb in my opinion) you can make a class that holds two maps. One to map color and the other to map ID. The map values would be pointers. You'd define insert and find operations for your new class. The insert would new a data struct and insert the pointer into both maps. The find operations would locate the item using either map as appropriate. For delete/remove operations it is a little trickier: you'd need to look up the data struct by a map, then remove it from both maps, probably by using the data in the data struct to find the other map entry.