I'm trying to retrieve a value from a boost::multi_index_container, using a unique numerical id index. I've never used boost::multi_index_container before so I have some trouble understanding how they work. They seem to work a bit like a database, all I want to do is to retrieve an item by specifying the id. Any help would be greatly appreciated.
This is the datatype:
typedef boost::multi_index_container<
// Multi index container holds pointers to the subnets.
Subnet6Ptr,
// The following holds all indexes.
boost::multi_index::indexed_by<
// First is the random access index allowing for accessing
// objects just like we'd do with a vector.
boost::multi_index::random_access<
boost::multi_index::tag<SubnetRandomAccessIndexTag>
>,
// Second index allows for searching using subnet identifier.
boost::multi_index::ordered_unique<
boost::multi_index::tag<SubnetSubnetIdIndexTag>,
boost::multi_index::const_mem_fun<Subnet, SubnetID, &Subnet::getID>
>,
// Third index allows for searching using an output from toText function.
boost::multi_index::ordered_unique<
boost::multi_index::tag<SubnetPrefixIndexTag>,
boost::multi_index::const_mem_fun<Subnet, std::string, &Subnet::toText>
>
>
> Subnet6Collection;
The Subnet6Collection object is created when a dhcpv6-server (KEA) loads its config file. This file contains an optional numerical id value for each subnet, SubnetID in the datatype.
I would like to retrieve a Subnet6Ptr by specifying SubnetID.
Yes, Mutli-index can be a difficult beast to work with. As I wrote in a different answer, "Boost.Multi-index offers an extremely customisable interface, at the cost of offering an extremely complex interface."
Basically, when you want to access the contents of the container, you do it through one of its indices. You therefore start by obtaining a reference to the index which you want to use (the on tagged SubnetSubnetIdIndexTag in your case), and then treat that index pretty much like a container. Which container that is depends on the index's type. For an oredered unique index (as in your case), that would be somewhat like std::map (but with iterators pointing to values only), or like std::set with a transparent comparator which only compares IDs.
Here's what it looks like in code:
Subnet6Collection coll = something();
SubnetID idToLookFor = something2();
auto& indexById = coll.index<SubnetSubnetIdIndexTag>();
auto it = index.find(idToLookFor);
if (it != index.end()) {
Subnet6Ptr p = *it;
} else {
// No such ID found
}
Thanks for answering.
I tried the following (SubnetID is just uint32_t so I used 10 for test):
SubnetID id = 10;
Subnet6Collection coll;
auto& indexById = coll.index<SubnetSubnetIdIndexTag>();
auto it = index.find(id);
if (it != index.end()) {
Subnet6Ptr p = *it;
} else {
// No such ID found
}
but it does not compile:
Opt18_lease_select.cc:38:24: error: invalid use of ‘struct boost::multi_index::multi_index_container, boost::multi_index::indexed_by >, boost::multi_index::ordered_unique, boost::multi_index::const_mem_fun >, boost::multi_index::ordered_unique, boost::multi_index::const_mem_fun, &isc::dhcp::Subnet::toText> > > >::index’
auto& indexById = coll.index<SubnetSubnetIdIndexTag>();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Opt18_lease_select.cc:39:17: error: overloaded function with no contextual type information
auto it = index.find(id);
^~~~
Opt18_lease_select.cc:40:17: error: overloaded function with no contextual type information
if (it != index.end()) {
^~~
Seems like index() find(), end() can't be used this way, or maybe I'm just missing some header file?
Related
I'd like to have a container in cpp that kind of behaves like python dictionaries do, at least in the following regards:
key-value structure (key might be restricted to a single type)
variadic value type (might be restricted to some pod types[int, double, string])
deeply nested (arbitrary depth, but not necessary dynamic)
randomly accessed
accessed type is stored type
So at best, the following example should work;
typedef DictionaryContainer<string, <int, string, bool>, 2> Dict;
Dict mydict; //key-type is string, value-type one of int, string or bool, nested to depth 2
mydict["a"] = 1;
mydict["b"] = true;
mydict["c"] = 'string';
mydict["d"] = Dict();
mydict["d"]["a"] = false;
std::any& dict_d_a = mydict["d"]["a"]; // stores a bool
std::string& dict_c = mydict["c"]; // possibly get<std::string>(mydict["c"])
Is there anything like this out there? I have found containers that allow storing arbitrary types, like boost::variant but they do not allow random access and recursively defined containers (as far as I have figured it out).
Is there not some implementation of a tree / map that can be used in such way?
Novice question, but I searched for this and couldn't find something clearly solving my issue - apologies if this is obvious.
I have defined a map which looks like this:
map<int, string> testmap = {
{ 0, "a" },
{ 1, "b" },
{ 2, "c" }
}
However, I need to retrieve a value from testmap using a uint64_t value provided by another function.
When I do testmap[my_uint64_t_value] it returns an empty string, so I think this is because it's adding my_uint64_t_value as a key and setting the value to NULL.
This is the same if I set the map type to <uint64_t, string>, at least the way I'm currently defining my keys.
However, is there a way that I either:
convert the uint64_t value to a regular int
define the map as <uint64_t, string>, and be able to define my
keys as the 'correct' type?
It seems like int type conversion isn't that common, is this something that should be avoided?
The reason why you get an empty string is std::map::operator[] returns a reference to the value if and only if it exists, otherwise it performs an insertion. I suspect you have the latter case.
You need to use std::map::find for search.
uint64_t keyToFind = 1;
if (auto iter = testmap.find(keyToFind); iter != testmap.cend())
{
// do something
std::cout << "Found!\n";
}
else { std::cout << "Not Found!\n"; }
Like #Rene mentioned in the comments, casting from uint64_t to int can cause overflow. Therefore, making the key to larger type(as per requirement) would be a good idea.
std::map<uint64_t, std::string> testmap;
As said in another answer, the [] operator of the map class will perform an insertion with a default-constructed value if the key is not present in the map.
You can first use the count method to determine if the key is present in the map before accessing it.
if(testmap.count(keyToFind))
return testmap[keyToFind];
else
report_key_not_found();
An alternative solution is to use the at method to access the value. It will throw an std::out_of_range exception if the key is not present instead of inserting a new key.
I'm creating a HandleManager whose purpose is to simply map Handles (which is a typedef of long long int) to strings. The purpose is so that objects that use a Handle can also be identified via strings if it helps a user remember the object. In which case, in this map:
typedef std::unordered_map<Handle, std::string> HandleMap;
both types in the pair are keys insofar they can be used to identify anything. So far everything has compiled apart from the code which needs to get the Handle. The purpose is such that when a user allocates a string like so:
handle("myHandle");
A Handle is generated randomly and then the string passed is paired with it in the foresaid map. What I want now is to be able to get the Handle that is paired with the string based on the string that is passed:
Handle HandleManager::id(const std::string &name)
{
HandleMap::iterator it = pHandles.find(name);
if (it != pHandles.end())
return it->first;
return -1;
}
But for some weird reason the compiler complains about this:
HandleManager.cpp:48:45: error: no matching function for call to ‘std::unordered_map<long long int, std::basic_string<char> >::find(const string&)’
In the foresaid map, the string is the value and the Handle is the key. So how can I get the key from the unordered_map based on the value contained therein?
You can use the member function find to search for key only. To search for a value, you can use a std::find_if with a lambda function (if you use C++11), or to traverse the map (ok in previous C++ version):
for (HandleMap::const_iterator it = map.begin(); it != map.end(); ++it) {
if (it->second == name) return it->first;
}
// or value not found
On the other hand, if searching for a value is a very common operation, you may want to have two maps: std::unordered_map<Handle, std::string> and std::unordered_map<std::string, Handle>. In that case, you have to make sure you perform insertions, deletions, etc. in both maps to keep then synchronized.
std::unordered_map::find operates on the key, not the value. You can use std::find_if:
Handle HandleManager::id(const std::string &name)
{
auto it = std::find_if(std::begin(pHandles), std::end(pHandles),
[](auto&& p) { return p->second == name; });
if (it == std::end(pHandles))
return -1;
return it->first
}
Note that auto, std::begin, std::end and lambdas are C++11 and generic lambdas are C++14, so substitute those out if you're stuck with an old compiler.
But for some weird reason the compiler complains about this:
Of course it does, the find function is for lookup up by key and you're not doing that.
To find a value you need to visit every element until you find it (or use a bidirectional map which maps values back to keys, e.g. Boost.Bimap).
Based on answer from #TartanLlama (*):
Handle HandleManager::id(const std::string & name) {
auto iter = std::find_if(std::begin(pHandles), std::end(pHandles),
[& name](auto && pair) {
return pair.second == name;
});
if (it == std::end(pHandles)) {
return -1;
}
return it->first;
}
(*): Because it doesn't seem possible to format code in comments.
If I have a std::multimap<int, std::map<int, MyClass>> myMultimap how to I insert a class object MyClassA into the map with value 1 at multimap value 2?
It looks like I can do myMultimap.at(2).insert(std::pair<1,MyClassA>); in c++11 but I am using c++98 due to a library regression/incomparability out of my control.
I've also tried
myMultimap[2].insert(
std::make_pair(
myMultimap[2].end(),
myClassA
)
);
which gives: error: no match for ‘operator[]’ (operand types are ‘std::multimap<int, std::map<int, ns_namespace::MyClassType> >’ and ‘int’)| for both of the [...]'s.
I don't want to do something like myMultimap.insert(std::make_pair(2,std::make_pair(1,MyClassA)))
because if I understand correctly, this would make a new map in the multimap rather than assigning the class object to the existing map within the multimap.
It is a two stage process:
Locate the position in the outer map where you want to do something to the inber map. If necessary, insert a new element.
Update the inner map withthe appropriatevalue.
I don't know why the outer map us a multimap (they are rarely useful) so the exampke just uses the first entry:
auto it = mymultimap.lower_bound(2);
if (it == mymultimap.end() || it->first != 2) {
it = mymultimap.insert(
std::make_pair(2, std::map<int, MyClass>())).first;
}
(*it)[1] = MyClassA;
(typed on a mobile device: there are probably typos but the overall approach should work).
I'm using Google's sparsehashmap, and trying to work out if a value was inserted or looked up. The following works, but obviously it's looking it up twice. How do I do it without the double lookup?
Element newElement = Element();
bool inserted = ((*map).insert(pair<const int64, Element>(key, newElement))).second;
Element element = (*(((*map).insert(pair<const int64, Element>(key, newElement))).first)).second;
if (inserted)
puts("INSERTED");
I can't check the contents of Element (it's a struct) as I want to differentiate between a default Element being found and newElement being inserted. I couldn't work out how to assign ((*map).insert(pair<const int64, Element>(key, newElement))) to a variable as it's of a template type that includes types private to the sparse_hash_map class.
Try this:
typedef sparse_hash_map<...>::iterator sh_iterator; //you already have this, haven't you?
std::pair<sh_iterator, bool> res = map->insert(std::make_pair(key, newElement));
if (res.second)
puts("INSERTED");
If, for whatever reason you don't like the std::make_pair function, you should consider a typedef for the pair type:
typedef pair<const int64, Element> map_pair;
Anyway, the return type of insert is pair<iterator, bool>, and AFAIK iterator is a public typedef of the class.
BTW, I don't get why you do the second insert... to get to the inserted element? Probably you should declare element as a reference. In my suggested code:
Element &element = res.first->second;
Naturally, if you were using C++11, you could simply do:
auto res = ...;