Access Key from Values and Value from Key - c++

My project needs both accessors.
Access Value using Key (Simple)
Access Key using Value (Bit tricky)
Value too will be unique in my project
Please suggest the better container to use and how ?
I would like to use either the STL or BOOST.

What you're looking for is called a bidirectional map.
There isn't one in the STL, but you can take a look at Boost.Bimap for another implementation.
If you want to implement it yourself, you can simply use two regular one-way maps. If you use pointers, there should be little memory overhead and decent performance.

That's what I used in my project two days ago.
#include <boost/bimap.hpp>
class ClientManager
{
typedef boost::bimap<
boost::bimaps::set_of<int>,
boost::bimaps::set_of<int>
> ConnectedUsers; // User Id, Instance Id
ConnectedUsers m_connectedUsers;
public:
int getUserId(int instanceId);
int getInstanceId(int userId);
};
int ClientManager::getInstanceId(int userId)
{
auto it = m_connectedUsers.left.find(userId);
return it->second;
}
int ClientManager::getUserId(int instanceId)
{
auto it = m_connectedUsers.right.find(instanceId);
return it->second;
}
...
// Insert
m_connectedUsers.insert(ConnectedUsers::value_type(id, instanceId));
// Erase
m_connectedUsers.left.erase(userId);

If you want the either way access, ie key->value and value->key, chances are that your design doesn't need an associative container like a map
Try a vector of std::pair.
On a side note, if you need to store more than two values, you can use std::tuple.
HTH!!

Related

What's the best way to store a maya api object inside an std::map that will never become stale? c++

I have an std::map that I'm using to cache some objects info and I will be using that later, I'd like to have my object as a key so it takes as little time as possible to access it.
I was thinking of converting the UUID to string but I realised you can actually end up with duplicated uuids if the object gets brought to the scene as a reference multiple times.
I've also tried with adding an MDagPath but it won't allow me to store that into an std::map. I imagine I'd have to make it hashable but I can't think of a way to do that safely. Using a name I also think it's a big nono since it can be renamed.
Thanks for the help. I hope I was clear enough with my problem.
std::map isn't a hash map (you'd want unordered_map for that). As for adding an MDagPath to a std::map, you could do it via something along these lines:
// I don't think this is defined for dagpath?
// So you should be able to define it for MDagPath.
static inline bool operator < (const MDagPath& a, const MDagPath& b) {
return a.fullPathName() < b.fullPathName();
}
std::map<MDagPath, MyObjectInfo> myMap;
Alternatively, if you want to generate a lookup based on the object, then you can use MObjectHandle. It may still go stale, but at least it has the isAlive() and isValid() methods to tell you when it is stale.
You could insert that into an unordered_map
namespace std {
// override the std::hash for the MObjectHandle
template <>
struct hash< MObjectHandle >
{
std::size_t operator()(const MObjectHandle& k) const
{
return k.hashCode();
}
};
}
std::unordered_map<MObjectHandle, MyObjectInfo> myMap;
(code is untested - I don't have access to Maya at the moment)

Database like C++ Data Structure

I looked over questions like this one, but it seems most answers suggest that one use something like sqlite with an in memory database. Perhaps that is the only real solution but I'll ask regardless.
I have a number of records that look like this :
struct record
{
int id;
int type;
// bunch of other data
}
The problem involves storing a number of these in a data structure that would allow for efficient runtime queries like
GetAllForId(int)
GetAllOfType(int)
GetAllOfTypeAndId(int,int)
There can be multiple records of 'type' for a given 'id'
There can be multiple records of 'type' for different 'id'
I also want to be able to easily modify the values in the results of any
GetAllOfTypeAndId(int,int)
And ofcourse make insertions and deletions with a (preferably) low cost. Although insertions are infrequent so I can eat a bit of cost here.
For reference, I have tried the following solutions :
multimap<type,record>
Then just iterate on all records to find the one of the relevant type. Feels really cumbersome especially when doing GetAllOfTypeAndId queries, is good for GetAllOfId Queries
map<id,map<type,record>>
Allows for good GetAllOfTypeAndId queries but fails at providing decent access to GetAllofType
Unfortunately because of the nature of this project, I may not be able to use a relational database system, even if its in memory.
It sounds like you want a MultiIndex. Joaquin M Lopez has provided an excellent data structure which allows indexes across multiple fields, member functions, or other properties of a struct/class.
In your case, we could do something like:
struct record
{
int id;
int type;
// bunch of other data
};
struct id_tag {};
struct type_tag {};
using record_container = boost::multi_index_container<
record,
boost::indexed_by<
boost::hashed_unique<
boost::multi_index::tag<id_tag>,
boost::multi_index::member<record, int, &record:id>
>,
boost::hashed_non_unique<
boost::multi_index::tag<type_tag>,
boost::multi_index::member<record, int, &record:type>
>
>
>;
We can now access the data from a unique, hashed index of the ID (a regular hashmap, like unordered_map), or a non-unique, hashed index of the type (like unordered_multimap) using views of the data.
record_container data;
// .... Fill with data
// Find by ID
auto& id_view = data.get<id_tag>();
auto id_it = id_view.find(15);
if (id_it == id_view.end()) {
// not found
} else {
// found
}
You may also create composite keys and do complex logic fairly easy with such a container:
using composite = boost::multi_index::composite_key<
record,
boost::multi_index::member<record, int, &record::id>,
boost::multi_index::member<record, int, &record::type>
>;
Edit
If you cannot use Boost, I have a fork of MultiIndex which uses C++11 features and does not require Boost. It solely depends on a small subset of Brigand, a C++11, template metaprogramming, header-only library.
You could use a class and container and its iterators.
class DB
{
private:
typedef list<Record>::iterator iterator;
list<Record> records;
iterator itForId;
public:
// return iterator of first record with id == parameter
iterator GetFirstForId(int id);
iterator GetNextForId(int id);
iterator end() {return records.end();}
};
Deleting records can mess with the iterators so it might be better to mark records for deletion and later batch delete when not iterating through a container.
class DataRecord : public Record
{
bool markedForDeletion;
};

Boost Variant : How can I do a visitor that returns the type that was set?

I'm trying to write a generic map that uses a boost:variant as the value.
I'm stuck on trying to write the get(std::string key) function that will return the appropriate type.
Here is what I came up with so far:
class GenericHashMap {
private:
std::map< std::string, boost::variant<int, bool, double, std::string> > genericMap;
public:
template<typename T>
bool getValue(const std::string & key, T & value) {
if ( _map.find(key) == _map.end() ) {
return false;
}
T * valuePtr = boost::get<T>(_map[key]);
if (valuePtr == NULL) {
return false;
}
value = *valuePtr;
return true;
}
}
I'm curious how I should handle iterators? Is it worth making my own nested iterators or just return the nested std::map.
Edit
I added the class design I was hoping to achieve (i.e. a generic hashmap). The problem I had was that I wanted a way for the user to query if for a specific key it was stored as a specific type.
If you have such an issue, it probably means you should use a visitor instead of wanting to get the value out of your variant. It is usually the way to go with boost::variant.
If you think about it: you do not want to hardwire a specific type for a specific key value. Otherwise, it means you lose all the power of boost::variant. And it means you should have different maps for each key sets (as you know them statically, you should not put everything in the same map).
boost::variant is really here to help you with dynamic dispatch, not static branching.
Note: In your example you lookup your item twice when it is found, you should store the result of find instead of discarding it, saving you the second lookup.

In a hashmap/unordered_map, is it possible to avoid data duplication when the value already contains the key

Given the following code:
struct Item
{
std::string name;
int someInt;
string someString;
Item(const std::string& aName):name(aName){}
};
std::unordered_map<std::string, Item*> items;
Item* item = new Item("testitem");
items.insert(make_pair(item.name, item);
The item name will be stored in memory two times - once as part of the Item struct and once as the key of the map entry. Is it possible to avoid the duplication? With some 100M records this overhead becomes huge.
Note:
I need to have the name inside the Item structure because I use the hashmap as index to another container of Item-s, and there I don't have access to the map's key values.
OK, since you say you are using pointers as values, I hereby bring my answer back to life.
A bit hacky, but should work. Basicly you use pointer and a custom hash function
struct Item
{
std::string name;
int someInt;
string someString;
Item(const std::string& aName):name(aName){}
struct name_hash
{
size_t operator() (std::string* name)
{
std::hash<std::string> h;
return h(*name);
}
};
};
std::unordered_map<std::string*, Item*, Item::name_hash> items;
Item* item = new Item ("testitem");
items.insert(make_pair(&(item->name), item);
Assuming the structure you use to store your items in the first place is a simple list, you could replace it with a multi-indexed container.
Something along thoses lines (untested) should fulfill your requirements:
typedef multi_index_container<
Item,
indexed_by<
sequenced<>,
hashed_unique<member<Item, std::string, &Item::name
>
> itemContainer;
itemContainer items;
Now you can access items either in their order of insertion, or look them up by name:
itemContainer::nth_index<0>::type & sequentialItems = items.get<O>();
// use sequentialItems as a regular std::list
itemContainer::nth_index<1>::type & associativeItems = items.get<1>();
// uses associativeItems as a regular std::unordered_set
Depending on your needs, you can use other indexings as well.
Don't store std::string name field in your struct. Anyway when you perform lookup you already know name field.
TL;DR If you are using libstdc++ (coming with gcc) you are already fine.
There are 3 ways, 2 are "simple":
split your object in two Key/Value, and stop duplicated the Key in the Value
store your object in a unordered_set instead
The 3rd one is more complicated, unless provided by your compiler:
use an implementation of std::string that is reference counted (such as libstdc++'s)
In this case, when you copy a std::string into another, the reference counter of the internal buffer is incremented... and that's all. Copy is deferred to a time where a modification is requested by one of the owners: Copy On Write.
No, there isn't. You can:
Not store name in Item and pass it around separately.
Create Item, ItemData that has the same fields as Item except the name and either
derive Item from std::pair<std::string, ItemData> (= value_type of the type) or
make it convertible to and from that type.
Use a reference to string for the key. You should be able to use std::reference_wrapper<const std::string> as key and pass key in std::cref(value.name) for key and std::cref(std::string(whatever)) for searching. You may have to specialize std::hash<std::reference_wrapper<const std::string>>, but it should be easy.
Use std::unordered_set, but it has the disadvantage that lookup creates dummy Item for lookup.
When you actually have Item * as value type, you can move the name to a base class and use polymorphism to avoid that disadvantage.
Create custom hash map, e.g. with Boost.Intrusive.

Putting SDL surfaces in a map, along with file names

I'm very new to using maps in C++, so I am having some difficulties using it for my SDL surfaces. This is what I've tried (not working):
map <SDL_Surface*, char*> mSurfaceMap;
mSurfaceMap.insert(pair<SDL_Surface*, char*>(m_poSurfaceTest, "..//..//gfx//testImage.png"));
The idea is to put all surfaces and their corresponding image files in a map to easily initialize them and do IMG_Load() on them, as well as free them when closing the program.
If this is a bad solution for it, please point me in the right direction. I first thought of making two arrays, but I wanted to try this instead, as I felt it was a more elegant solution. If the solution is ok, I'd love to hear what I am doing wrong in the code.
This code works for me. Output is as expected:
#include <map>
#include <stdio.h>
using std::map;
using std::pair;
struct Custom
{
int val;
Custom() {val=0;}
};
int main(int argC,char* argV[])
{
map<Custom*,char*> mMap;
Custom* test = new Custom;
mMap.insert(pair<Custom*,char*>(test,"Test"));
printf("%s\n",mMap[test]);
return 0;
}
std::map is great for looking up data by an ordered key, it is usually implemented as a balanced binary tree that gives O(log n) look-up time. If the look-up order doesn't matter then a std::hash_map will be a better choice with an O(1) look-up time.
The problem with using a pointer as your key in either container is that the they will index by the integer address of the pointer, not the value of what is pointed to.
std::string, however, has value semantics and implements the less-than operator which will let the container index by the value of the string.
You may also want to put your surface in a smart pointer for memory management purposes.
typedef std::tr1::shared_ptr<SDL_Surface> surface_pointer;
typedef pair<std::string, surface_pointer > surface_pair;
std::map<std::string, surface_pointer > mSurfaceMap;
mSurfaceMap.insert(surface_pair("..//..//gfx//testImage.png", surface_pointer(m_poSurfaceTest)));
A couple of other thoughts...
If you don't need the look-up functionality, and are just using a container for housekeeping, then a simple std::vector<std::pair<std::string, SDL_Surface*> > would probably suffice for what you need.
Or, if you're storing the surfaces as members already (assuming from the variable name) then you could store the member variables as a tr1::unique_ptr<SDL_Surface> and when the containing class is deleted so will the SDL_Surface be deleted. For this to work however you need to provide a custom deallocator for the tr1::unique_ptr, that will teach it how to free an SDL_Surface*.
struct SdlSurfaceDeleter {
void operator() (SDL_Surface*& surface) {
if (surface) {
SDL_FreeSurface(surface);
surface = NULL;
}
}
};
Then you would specify your members like this (a typedef makes it less verbose):
typedef std::tr1::unique_ptr<SDL_Surface, SdlSurfaceDeleter> surface_ptr;
class MyClass {
public:
MyClass(const std::string& path)
: m_poSurfaceTest(IMG_Load(path.c_str()) { }
surface_ptr m_poSurfaceTest;
};