gnu C++ hash_map: erasing a key: defined? - c++

Consider the following program, which is a minimal example trying to reproduce a problem with some legacy code:
#include <iostream>
#include <ext/hash_map>
// Define a hash for std::string class so we can use it as keys
// in hash_map below.
namespace __gnu_cxx {
template <>
struct hash<std::string> {
size_t operator() (const std::string& x) const {
return hash<const char*>()(x.c_str());
}
};
}
// Data class contains a string
class Data {
public:
std::string s;
Data() { s = "foobar"; }
Data(std::string s_) : s(s_) {}
};
// Map keyed by string. Values are Data instances
typedef __gnu_cxx::hash_map<std::string, Data> DataMap;
int main()
{
DataMap m;
std::string key = "test";
// I am storing a "Data" instance d, for "key". d.s is the same as key.
Data d = Data(key);
m[key] = d;
DataMap::iterator it = m.find(key);
if (it == m.end()) {
std::cerr << "not there " << std::endl;
return 1;
}
Data *dp = &it->second;
// Question about the following line. Is the behavior well-defined?
m.erase(dp->s);
return 0;
}
I am storing my class Data instances in a hash_map. I search for a particular data member using a key, and then erase that value using m.erase(dp->s). m.erase(dp->s) will delete the object pointed to by dp. Am I allowed to use dp->s in the call to erase(), or must I first make a copy and then erase():
std::string key_to_delete = dp->s;
m.erase(key_to_delete);

Looking at the implementation, it seems as if even after the node (the pair pointed to by it) is deleted, the key passed to the erase function is still referenced. If dp is deleted, then the reference to dp->s becomes invalid. Yet the implementation of hash_map is still trying to dereference it. Fail.
You would need to pass something that is guaranteed to stay valid for the call to erase.
You could
m.erase(key);
Or you can use the iterator returned by find to do the erase:
m.erase(it);

Related

How could return map<int, variant>'s value by a function?

I want to have a map to save different types of value, so I have a map like
std::map<int, std::variant<int, std::string>> m ={{1,1},{2,"asd"}};
And Now I want to design a function to get the value by its key like
auto get(int key) {
...
return value;
}
That's to say what I want is like
get(1) -> 1
get(2) -> "asd"
So is it possible? If so what's the exact solution?
ADDED:
Yes, as for the purpose of the design, I want to save config datas reading from config files.
And after reading I need to make some type changing or data transform.
Such as in config files like
a=1
b=asd
And after reading it, I want to save it as
m["a"]=int(3) ---->refers a=1 and needs to plus 2 after reading its actual data 1
m["b"]=std::string("asdasd") ---->refers b=asd and needs to double it
So I think it will be easier if there is an interface function to get the exact value by the key
You cannot exactly design a function with this syntax. Just imagine this:
int key = 0;
if (rand() % 2) {
key = 1;
} else {
key = 2;
}
auto value = get(key);
Here's the riddle:
Please tell me the return type of get and the type of value. There can be only one answer and you can only answer with a single type that will remain unchanging no matter how much time I ask you this question.
If you can't answer, well, the compiler cannot either.
However, it doesn't mean you can't design something similar that will do what you need.
You could just return the variant. This might not be exactly what you're looking for, but it's worth noting since it doesn't change any of the input of the get function.
You can also just send the expected type:
get<int>(key)
The implementation would look like this:
template<typename T>
auto get(int key) -> T {
return std::get<int>(m[key]);
}
If you cannot know the expected type, then you could just send a visitor:
get(key, [](auto value) -> std::size_t {
if constexpr (std::same_as<int, decltype(value)>) {
// do stuff with value as an int since it's a int here
return value + 1;
} else {
// do stuff with value as a string since it's a string here
return value.size();
}
});
The implementation would look like this:
auto get(int key, auto visitor) -> decltype(auto) {
return std::visit(visitor, m[key]);
}
If I understand the requirements, you could return a proxy object from get that keeps a reference to the variant and depending on what you do with the proxy object, it can adapt. Example:
#include <iostream>
#include <variant>
#include <map>
#include <string>
struct Foo {
using variant = std::variant<int, std::string>;
// the proxy object
struct proxy {
// assignment:
template<class T>
variant& operator=(T&& value) {
*data = std::forward<T>(value);
return *data;
}
// for static_cast to the wanted type
explicit operator int& () { return std::get<int>(*data); }
explicit operator std::string& () { return std::get<std::string>(*data); }
variant* data;
};
proxy get(int key) {
// return the proxy object
return proxy{&m[key]};
}
std::map<int, std::variant<int, std::string>> m = {{1,1}, {2,"asd"}};
};
And it could be used like this:
int main() {
Foo f;
// you need to know what it stores:
std::cout << static_cast<int>(f.get(1)) << '\n';
std::cout << static_cast<std::string>(f.get(2)) << '\n';
// assigning is simple:
f.get(1) = "hello world"; // was int, now std::string
f.get(2) = 2; // was std::string, now int
std::cout << static_cast<std::string>(f.get(1)) << '\n';
std::cout << static_cast<int>(f.get(2)) << '\n';
}
Two possibilities:
A) You want to return either int or std::string depending on the index passed to the function.
Not possible. A function has one return type. Also auto is not magic. If you cannot write the actual type, then the compiler can't either.
B) You want to return the mapped_type of the map.
You can return std::variant<int, std::string> from the function.
Your get can't do much "better" than std::variant::get. With std::variant::get you need to specify at compile time what type you want to retrieve. And there is no way around that if you want to get either int or std::string.
There might be better ways to solve your actual issue, the one for which you thought auto get(int key) was the solution. A comment (by Eljay) mentions std::visit. There you can see a couple of examples of how to deal with variants.

Moving keys out of std::map<> &&

I'd like to have let's say getKeys() function getting not-copiable keys out of a map:
class MyObj {
// ... complex, abstract class...
};
struct Comparator { bool operator()(std::unique_ptr<MyObj> const &a, std::unique_ptr<MyObj> const &b); };
std::vector<std::unique_ptr<MyObj>> getKeys(std::map<std::unique_ptr<MyObj>, int, Comparator> &&map) {
std::vector<std::unique_ptr<MyObj>> res;
for (auto &it : map) {
res.push_back(std::move(it.first));
}
return res;
}
But it is not working because the key in it (.first) is const. Any tips how to solve it? Note: In our environment I'm not allowed to use C++17 function std::map::extract().
Is it somehow ok to use const_cast because map will be destructed anyway?
res.push_back(std::move(const_cast<std::unique_ptr<MyObj> &>(it.first)));
I want to avoid cloning MyObj.
I know why keys of a std::map container cannot be modified but is it still disallowed for a map that is going to be destructed immediately after the key modification?
Note: In our environment I'm not allowed to use C++17 function std::map::extract().
Shame - it was introduced to solve this problem.
Is it somehow ok to use const_cast because map will be destructed anyway?
No.
I want to avoid cloning MyObj.
Sorry; you'll need to clone the keys at least.
I know why keys of a std::map container cannot be modified but is it still disallowed for a map that is going to be destructed immediately after the key modification?
Yes.
The map's internal machinery has no way of knowing that its destiny awaits.
Yes, it's still disallowed. Non-const access to keys is probably safe if you're just going to destroy the map afterwards, but it's not guaranteed to be safe by the standard, and the std::map interface doesn't offer any sort of relaxation of the rules which applies to rvalue references.
What std::map does have since C++17, though, is extract(), which rips a key-value pair out of the map entirely and returns it as a "node handle". This node handle provides non-const access to the key. So if you were to move the pointer out of that node handle, the eventual destruction would happen to an empty pointer.
Example:
#include <utility>
#include <memory>
#include <vector>
#include <map>
template <typename K, typename V>
std::vector<K> extractKeys(std::map<K, V> && map)
{
std::vector<K> res;
while(!map.empty())
{
auto handle = map.extract(map.begin());
res.emplace_back(std::move(handle.key()));
}
return std::move(res);
}
int main()
{
std::map<std::unique_ptr<int>, int> map;
map.emplace(std::make_pair(std::make_unique<int>(3), 4));
auto vec = extractKeys(std::move(map));
return *vec[0];
}
Answers persuaded me that I should avoid const_cast-ing. After some analysis I realized that usage of my map is quite isolated in the code so I could do a small refactor to avoid the const-issue.
Here is the result:
class MyObj {
// ... complex, abstract class...
};
struct Comparator { bool operator()(MyObj const *a, MyObj const *b); };
// key is a pointer only, value holds the key object and the effective "value"
struct KeyAndVal { std::unique_ptr<MyObj> key; int val; };
using MyMap = std::map<MyObj *, KeyAndVal, Comparator>;
// Example how emplace should be done
auto myEmplace(MyMap &map, std::unique_ptr<MyObj> key, int val) {
auto *keyRef = key.get(); // to avoid .get() and move in one expr below
return map.emplace(keyRef, KeyAndVal{ std::move(key), val });
}
std::vector<std::unique_ptr<MyObj>> getKeys(MyMap map) {
std::vector<std::unique_ptr<MyObj>> res;
for (auto &it : map) {
res.push_back(std::move(it.second.key));
}
// here 'map' is destroyed but key references are still valid
// (moved into return value).
return res;
}

c++ Reliable way to remove all entries with UUID as the key

So I have a bunch of std::unordered_maps that all have a common key but different datatypes for the data. I basically made a structure similar to a component based entity system, with the key being a entity class that contains a boost::uuid.
The adding works well but I am struggling with removing entire entities from the program. I can't find a reliable way to delete them without impairing running systems (I don't want to allow a delete in the middle of a frame to avoid one frame being glitched due to values being desynced or replaced with some default values) or that's terribly slow.
Right now I have a bool in the entity class and every frame the program goes through EVERY entity and checking if its set to true or not. IF yes the datapoint is removed from the map.
However I am not satisfied with it.
This is what the average entity looks like:
class Entity{
private:
boost::uuids::uuid uuid;
bool protect = false;
bool status = ENTITY_ALIVE;
public:
Entity()
:uuid(boost::uuids::random_generator()()){
}
const boost::uuids::uuid &operator ()()const{
return this->uuid;
}
bool operator ==(const Entity &othr)const{
return this->uuid==othr.uuid;
}
void copyComponents(const Entity &source,ComponentBase** mgrs, int c){
std::cout<<"copying...\n";
for(int i = 0; i < c; i ++){
std::cout<<"-";
mgrs[i]->copyTo(source,*this);
}
}
void kill(){
if(this->protect==false){
this->status = ENTITY_DEAD;
}
}
bool getStatus()const{
return this->status;
}
void setProtection(bool status){
this->protect = status;
}
bool getProtection(){
return this->protect;
}
~Entity(){
// std::cout<<"ENTITY WAS DELETED!!!!!!!!!!!"<<"\n";
}
};
struct EntityHasher{
size_t operator()(const Entity& obj)const{
return boost::uuids::hash_value((obj)());
}
};
Ok and this is what the maps look like:
typedef typename std::unordered_map<std::reference_wrapper<const Entity>,List<std::shared_ptr<t>>,EntityHasher,std::equal_to<const Entity>> mapType;
The list is a custom class I made that simply stores things in a doubly linked list. Nothing to worry about. I made this since accessing data was easier than using unordered_multi map.
What I have tried:
I have tried to store links to the classes containing the maps and simply directly having them delete it without much success.
I have tried to have is so when a "dead" entity is accessed its being added to a vector in the class storing the unordered map and then later cleared in a separate function called "removeCorpses". However that lead to lesser used components being left on for way too long and it was generally just really unstable (maybe I made a mistake implementing it)
Please suggest some good ways of going about deleting a Entity in all maps having it as a key without messing with the active frame etc.
You can use Boost.MultiIndex to construct a map-like structure that keeps corpses arranged first in a secondary sequenced index so that they can be erased when appropriate:
Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/member.hpp>
template<typename Key,typename Value>
struct delayed_erasure_unordered_map_value
{
Key first;
mutable Value second;
};
template<typename Key,typename Value>
using delayed_erasure_unordered_map_base=boost::multi_index_container<
delayed_erasure_unordered_map_value<Key,Value>,
boost::multi_index::indexed_by<
boost::multi_index::hashed_unique<
boost::multi_index::member<
delayed_erasure_unordered_map_value<Key,Value>,
Key,
&delayed_erasure_unordered_map_value<Key,Value>::first
>
>,
boost::multi_index::sequenced<>
>
>;
template<typename Key,typename Value>
class delayed_erasure_unordered_map:
public delayed_erasure_unordered_map_base<Key,Value>
{
using base=delayed_erasure_unordered_map_base<Key,Value>;
public:
using iterator=typename base::iterator;
using base::base;
void mark_for_erasure(iterator it) // marking same element twice is UB
{
auto pit=this->template project<1>(it);
auto& index=this->template get<1>();
index.relocate(index.begin(),pit);
++num_corpses;
}
void erase_marked()
{
auto& index=this->template get<1>();
for(;num_corpses;--num_corpses)index.erase(index.begin());
}
private:
std::size_t num_corpses=0;
};
#include <iostream>
int main()
{
delayed_erasure_unordered_map<int,int> m=
{{0,0},{1,1},{2,2},{3,3},{4,4},{5,5},{6,6},{7,7},{8,8},{9,9}};
for(auto it=m.begin(),end=m.end();it!=end;++it){
if(it->first%2)m.mark_for_erasure(it);
}
m.erase_marked();
for(const auto& x:m){
std::cout<<"{"<<x.first<<","<<x.second<<"} ";
}
}
Output
{0,0} {2,2} {4,4} {6,6} {8,8}

access key in constructor of struct in a map

Use case: a buffer of records. Here is the basic idea.
Making it work requires that the constructor of the record struct knows the key, which is used as recordnumber, when an element is added to the map.
Of course, this can be done with more code, but this looks most elegant to me.
Minimally coded:
#include <whatever>
struct record
{
string foo;
record(unsigned irec) { foo=readrecord(irec); }
};
map<unsigned,record>recbuf;
int main()
{
// if the element is not yet in the map, it should be read.
string foo_ten=recbuf[10].foo;
// do something with the result
printf("foo_ten: %s\n",foo_ten.c_str());
return 0;
}
Edit1: code above will not work.
Any ideas how to get this to work?
Edit2:
I derived a mapplus class adding another map::operator[]:
template<class _Kty, class _Ty, class _Pr = less<_Kty>, class _Alloc = allocator<pair<const _Kty, _Ty> > >class mapplus :public map<_Kty, _Ty, _Pr, _Alloc>
{
public:
mapped_type& operator[](const _Kty &_Keyval)
{ // find element matching _Keyval or insert with default mapped
iterator _Where = _Mybase::lower_bound(_Keyval);
if (_Where == _Mybase::end()
|| _Mybase::_Getcomp()(_Keyval, _Mybase::_Key(_Where._Mynode())))
_Where = _Mybase::emplace_hint(_Where,
_Keyval,
_Ty(_Keyval));
return (_Where->second);
}
};
This does work. I am still interested in comments pointing out to me that I did this in a needlessly complex etc. way. Did I? Can it be done with less ado?
So, you want record objects to be constructed using your record(unsigned) constructor, rather than the default constructor.
Unfortunately, there's no way to do this with operator[] (reference):
If k matches the key of an element in the container, the function
returns a reference to its mapped value.
If k does not match the key of any element in the container, the
function inserts a new element with that key and returns a reference
to its mapped value. Notice that this always increases the container
size by one, even if no mapped value is assigned to the element (the
element is constructed using its default constructor).
I would not recommend overloading operator[] for std::map, for me it seems like a bad design.
However, you can do it with other methods, like insert or emplace (C++11).
See, for example, this answer: Using std::map<K,V> where V has no usable default constructor
Tested example:
#include <map>
#include <sstream>
#include <iostream>
std::string readRecord(int id)
{
std::stringstream stream;
stream << id;
return stream.str();
}
struct Record
{
Record(int id)
: foo(readRecord(id))
{
}
std::string foo;
};
int main()
{
std::map<int, Record> m;
for (int i = 0; i < 10; ++i)
m.insert(std::make_pair(i, Record(i)));
std::cout << m.at(5).foo << std::endl;
// std::cout << m[7].foo << std::endl; // error 'Record::Record': no appropriate default constructor available
return 0;
}

Map with multiple keys in C++

I want to store data by both, their name and their index. In other words, I want to map string names to objects and also give them a custom order.
What I came up with first is a std::vector of pairs of the string key and the object. The order was given by the position in the vector.
std::vector<std::pair<std::string, object> >
But this approach seems to be suboptimal since it doesn't automatically check for the uniqueness of string names. Moreover it feels wrong to group the objects by their order first, because logically their first order distinction is the name.
I need a data structure that allows access by both name and index.
std::magic<std::string, unsigned int, object> collection;
// access by either string or unsigned int key
collection.insert("name", 42, new object());
collection["name"]
collection[42]
Is there a data structure for this use case already? If not, how can I put one together, preferably using the standard library? Also I would like a way to insert new elements at the position after a given element without moving all further elements around.
Boost provides a set of containers just for this purpose, see: boost::multiindex
I'm trying to write a solution using std library, as requested in the original post. I will not have access to boost in my resource-constrained system. I've drafted a two-map system mentioned in the comments.
A big downside is that you'll have to wrap each public function that a std::map or std::vector normally offers, and my wrapper might not be as optimal.
But, this is a start. Let me know of improvements to my answer in the comment, and I'll edit the response when I can
#include <unordered_map>
#include <string>
#include <iostream>
struct Object
{
int val;
};
template <typename T>
class MultiKeyMap
{
std::unordered_map<std::string, uint> nameToIdMap;
std::unordered_map<uint, T> idToValMap;
public:
T& find(const std::string& name)
{
return find(nameToIdMap[name]);
}
T& find(const uint id)
{
return idToValMap[id];
}
T& operator[](const std::string& name) { return find(name); }
T& operator[](const uint id) { return find(id); }
void insert(uint id, const std::string& name, T&& val)
{
nameToIdMap[name] = id;
idToValMap[id] = val;
}
};
int main()
{
MultiKeyMap<Object> mkmap;
mkmap.insert(1, "one", Object{11});
mkmap.insert(2, "two", Object{22});
std::cout << "key=1: val=" << mkmap[1].val << "\n";
std::cout << "key='one': val=" << mkmap["one"].val << "\n";
std::cout << "key=2: val=" << mkmap[2].val << "\n";
std::cout << "key='two': val=" << mkmap["two"].val << "\n";
}