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;
}
Related
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.
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;
}
I am writing some C++ code which wraps the std::unordered_map type, where I want to hide the underlying type and present it as another type. More specifically, I want to wrap the std::pair from the std::unordered_map with another type. For the sake of argument, lets suppose the wrapper looks like this...
template <typename ActualT >
class wrapper final
{
private:
ActualT actual_;
public:
//Some constructors...
typename ActualT::first_type & get_first()
{
return actual_.first;
}
typename ActualT::second_type & get_second()
{
return actual_.second;
}
};
My reasoning is that since the wrapper class only has a member which is the exact type which it is wrapping, converting a reference from the original type to the wrapper type should be fine, but the type compatibility for structs states that the members should have the same type and name for the types to be compatible. Would using type-punning in this fashion potentially cause undefined behaviour or alignment issues?
using my_map = std::unordered_map < int, int >;
my_map m;
//Do some inserts...
reinterpret_cast<wrapper<typename my_map::value_type>&>(*m.find(10)).get_second() = 1.0;
I want client code to be allowed to access the entries of a map without knowing about the pair which is returned by the map. I also want to write a custom forward iterator, hence I need to return a reference to the entry. Would converting the reference to the pair to a reference to a class which act as a wrapper be considered dangerous?
Is there perhaps a better approach to accomplishing this?
This absolutely is undefined behaviour.
Seriously rethink your priorities.
Some free functions of the form
const my_map::key_type & MeaningfulNameHere(my_map::reference)
will go a long way to giving you meaningful names.
If you must wrap the standard library with different names, just use a non-explicit constructor, and store references.
template <typename Map>
class entry final
{
private:
typename Map::reference ref;
public:
entry(Map::reference ref) : ref(ref) {}
const typename Map::key_type & key()
{
return ref.first;
}
typename Map::mapped_type & value()
{
return ref.second;
}
};
If you really need the iterator to dereference to entry you can. But you can just implicitly instantiate entrys from the Map::references returned by Map::iterator::operator*, you don't need a custom iterator.
template <typename Map>
class entry_iterator
{
private:
typename Map::iterator it;
entry<Map> entry;
public:
entry<Map>& operator*() { return entry; }
entry_iterator operator++() { ++it; entry = *it; return *this; }
// etc
}
So you could clean this up, but I wouldn't suggest it:
#include <unordered_map>
#include <iostream>
using namespace std;
template <class Key, class Value>
class wrapper
{
public:
explicit wrapper(std::pair<const Key, Value>& kvp)
: _key{kvp.first}
, _value{kvp.second}
{}
const Key& key() const { return _key; }
Value& value() { return _value; }
private:
const Key& _key;
Value& _value;
};
int main()
{
unordered_map<int,int> m;
m[1] = 1;
m[3] = 3;
auto it = m.find(1);
wrapper w{*it};
w.value() = 30;
std::cout << w.key() << " -> " << w.value() << '\n';
}
The above effectively hides the pair from users of your class. It doesn't deal with exceptions (find() returning end() for example), and makes no guarantees about lifetimes. It's marginally better than what you have because it doesn't require a reinterpret_cast to an unrelated type.
However, map, unordered_map, set, etc. storing returning iterators as pairs is just part of library -- it's the canonical form and I don't see the benefit of shielding people from it.
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}
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);