How to define a map from function pointers to strings - c++

I am trying to define a map from member function pointers to strings in C++. But the the compiler complains that operator < is not defined for member function pointers. Is there a way to do this?
Example code:
#include <map>
#include <string>
class foo;
typedef void (foo::*fooFunc)();
class foo {
public:
foo() {
myMap[func]="example function";
}
void func() {};
std::map<fooFunc,std::string> myMap;
};
Resulting error message:
In file included from C:/msys64/mingw64/include/c++/6.2.0/bits/stl_tree.h:65:0,
from C:/msys64/mingw64/include/c++/6.2.0/map:60,
from grr.cpp:1:
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_function.h: In instantiation of 'constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = void (foo::*)()]':
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_map.h:501:32: required from 'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = void (foo::*)(); _Tp = std::__cxx11::basic_string<char>; _Compare = std::less<void (foo::*)()>; _Alloc = std::allocator<std::pair<void (foo::* const)(), std::__cxx11::basic_string<char> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::__cxx11::basic_string<char>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = void (foo::*)()]'
grr.cpp:9:15: required from here
C:/msys64/mingw64/include/c++/6.2.0/bits/stl_function.h:386:20: error: invalid operands of types 'void (foo::* const)()' and 'void (foo::* const)()' to binary 'operator<'
{ return __x < __y; }
~~~~^~~~~

std::map requires a comparison operator for its keys. By default, it uses operator <. Since operator < is not defined for pointer-to-member types, you are getting this error.
To fix it you'll need to define a custom comparison operator and provide it as a parameter to std::map. Alternatively, use a different container.
From cppreference:
std::map is a sorted associative container that contains key-value
pairs with unique keys. Keys are sorted by using the comparison
function Compare. Search, removal, and insertion operations have
logarithmic complexity. Maps are usually implemented as red-black
trees
If you want options on how to write such a comparison operator, it has already been answered here.

Related

How to use std::pair with classes?

so I'm experimenting with cvc5 and just wanted to keep track of the Terms in a map so I have created this:
std::map<std::string, std::pair<Term, int>> terms;
Basically, for I used the name as an index and I store the Term with other info in the map.
I have created a subtype of Term called TermStruct and I wanted to create another similar map:
std::map<std::string, std::pair<TermStruct, int>> termsStructs;
TermStruct was created roughly in the following way
class TermStruct : public Term {
public:
TermStruct(Term *t) : Term() {
this->t = t;
}
bool isNull();
Term *getTerm() { return this->t; };
std::string toString();
private:
Term *t = nullptr;
};
Now when I tried to add a new element to the termStructs map in the following way:
termsStructs[str] = std::pair(term, offset);
Note: term is of the correct type.
I have a number of compilation error such as:
/usr/include/c++/11/bits/stl_map.h:501:37: required from ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = std::__cxx11::basic_string<char>; _Tp = std::pair<TermStruct, int>; _Compare = std::less<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, std::pair<TermStruct, int> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::pair<TermStruct, int>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = std::__cxx11::basic_string<char>]’
/home/alberto/progetti/llvm/plugin/runtime/cvc5/Runtime.cpp:113:25: required from here
/usr/include/c++/11/tuple:1824:9: error: no matching function for call to ‘std::pair<TermStruct, int>::pair()’
1824 | second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
Any idea why and how to fix it?
Thanks
pair has nothing to do with this problem. It is all about map. I see two options.
Introduce a default constructor(a constructor without parameters) for TermStruct if you want to use std::map::operator[]. Here's why:
termsStructs[str] = std::pair(term, offset) does not insert the pair object right away
termsStructs[str] first creates a new key-value pair and adds it to the map(if there is no entry for str)
It is done by running std::make_pair(key, T())
As T here is std::pair, it tries to call Term() (FYI the second(integer) is zero-initialized)
However there is no Term() defined - compiler error
You may refer these links for details.
https://en.cppreference.com/w/cpp/container/map/operator_at (1)
https://en.cppreference.com/w/cpp/utility/pair/pair (1)
Or you can use emplace or insert.
These methods look uglier than the above but they behave just like what you must have expected. It does not require a default constructor.
For example,
termsStructs.emplace(str, std::pair(term, offset));
termsStructs.insert({str, std::pair(term, offset)});

Creating a Variant class and std::map<Variant, Variant>

I craeted a simple Variant class to store string, integer, double, etc. I'm trying to use a map of type std::map<Variant, Variant> but I'm getting this strange error:
In file included from /usr/include/c++/7/string:48:0,
from /home/dev/proj/cpp/common/Variant.h:3,
from /home/dev/proj/cpp/common/Event.h:3,
from /home/dev/proj/cpp/common/Event.cpp:1:
/usr/include/c++/7/bits/stl_function.h: In instantiation of 'constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Variant]':
/usr/include/c++/7/bits/stl_map.h:511:32: required from 'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = Variant; _Tp = Variant; _Compare = std::less<Variant>; _Alloc = std::allocator<std::pair<const Variant, Variant> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = Variant; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = Variant]'
/home/dev/orwell/cpp/common/Event.cpp:33:18: required from here
/usr/include/c++/7/bits/stl_function.h:386:20: error: no match for 'operator<' (operand types are 'const Variant' and 'const Variant')
{ return __x < __y; }
~~~~^~~~~
This is my Variant class:
class Variant
{
public:
enum class Type
{
Integer,
Double,
String
};
Variant()
{
}
Variant(int integer)
{
this->type = Type::Integer;
setInteger(integer);
}
Variant(std::string string)
{
this->type = Type::String;
setString(string);
}
Variant(double _double)
{
this->type = Type::Double;
setDouble(_double);
}
Type type;
This is where the error is happening:
void Event::add(std::string key, std::string value) {
this->map[key] = Variant(value); //problem here
}
std::map is a sorted array. To do that it uses the < operator.
Thus, if you want to use Variant in a map (I believe this only applies to keys), you will need to supply an operator<() for it. You can find some examples here.
Or, you'll need a comparison function. That can work too.

How to achieve const-correctness in multidimensional map in C++

I have a complex map that, in the end, stores pointers to Drawable objects. Drawable objects have a draw() member function which is declared as const. I need to call all the draw functions for all objects stored in my map that are of a certain type, and I must do it inside a const function. However I can't seem to be able to preserve the const-correctness of my function (drawSolid).
My outer map (map<int, ##>) is essentially indexing some sub-maps. Sub-maps are, in turn, indexing vectors (map<ItemType, vector<##> >). Finally, this vector keeps a set of shared_ptr<Drawable> objects.
If I remove the const qualifier from my function header, everything compiles, but I need it to be const. How may I iterate through my multidimensional map, preserving const-correctness?
void DrawableItems::drawSolid(int item_list = -1) const
{
typedef std::map<int, std::map<ItemType, std::vector<std::shared_ptr<Drawable> > > > drawablemap;
std::vector<std::shared_ptr<Drawable> > its;
for(drawablemap::const_iterator li = __items.begin(); li != __items.end(); li++) {
its = li->second[SOLID];
for(auto di = its.begin(); di != its.end(); di++) {
di->get()->draw();
}
}
}
This the error I get from the compiler (G++):
/.../dss-sim/src/graphics/DrawableItems.cpp: In member function ‘void DrawableItems::drawSolid(int) const’:
/.../dss-sim/src/graphics/DrawableItems.cpp:51:35: error: passing ‘const std::map<DrawableItems::ItemType, std::vector<std::shared_ptr<Drawable> > >’ as ‘this’ argument discards qualifiers [-fpermissive]
its = li->second[SOLID];
^
In file included from /usr/include/c++/5/map:61:0,
from /.../dss-sim/src/common/dss.hpp:11,
from /.../dss-sim/src/graphics/DrawableItems.hpp:19,
from /.../dss-sim/src/graphics/DrawableItems.cpp:15:
/usr/include/c++/5/bits/stl_map.h:494:7: note: in call to ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](std::map<_Key, _Tp, _Compare, _Alloc>::key_type&&) [with _Key = DrawableItems::ItemType; _Tp = std::vector<std::shared_ptr<Drawable> >; _Compare = std::less<DrawableItems::ItemType>; _Alloc = std::allocator<std::pair<const DrawableItems::ItemType, std::vector<std::shared_ptr<Drawable> > > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::vector<std::shared_ptr<Drawable> >; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = DrawableItems::ItemType]’
operator[](key_type&& __k)
There is no const version of operator[] in std::map. However, there is a const version of at() which you can use instead:
its = li->second.at(SOLID);
The reason is that operator[] inserts an element if there is no element yet, so there can not be a const version of operator[].
at() on the other hand throws an exception if no element exists, and this is compatible with a const std::map.

STL Unordered Map- inserting into a vector

I am not too familiar with the STL's unordered map and I'm having trouble inserting elements into it.
So I have an unordered map where the key is a string and the mapped value is a vector of integers. I have it declared as:
unordered_map<string, vector<int> > categorySearch;
How would I go about inserting elements into this map. I am currently doing
categorySearch.insert(make_pair("hello", categorySearch["hello"].push_back(5)));
This is clearly giving me compiler errors. How do I use insert for the vector portion of the map.
Here is the compiler error I am getting:
logData.h: In member function ‘void LogData::addEntry(Log)’:
logData.h:23:68: error: no match for ‘operator[]’ (operand types are ‘std::unordered_map<std::basic_string<char>, std::vector<int> >’ and ‘<unresolved overloaded function type>’)
categorySearch.insert(make_pair(add.getCategory(), categorySearch[add.getCategory].push_back(data.size()-1)));
^
logData.h:23:68: note: candidates are:
In file included from /usr/um/gcc-4.8.2/include/c++/4.8.2/unordered_map:48:0,
from logData.h:2,
from logman.cpp:5:
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/unordered_map.h:595:7: note: std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](const key_type&) [with _Key = std::basic_string<char>; _Tp = std::vector<int>; _Hash = std::hash<std::basic_string<char> >; _Pred = std::equal_to<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::vector<int> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::vector<int>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = std::basic_string<char>]
operator[](const key_type& __k)
^
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/unordered_map.h:595:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘const key_type& {aka const std::basic_string<char>&}’
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/unordered_map.h:599:7: note: std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = std::basic_string<char>; _Tp = std::vector<int>; _Hash = std::hash<std::basic_string<char> >; _Pred = std::equal_to<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::vector<int> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = std::vector<int>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = std::basic_string<char>]
operator[](key_type&& __k)
^
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/unordered_map.h:599:7: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘std::unordered_map<std::basic_string<char>, std::vector<int> >::key_type&& {aka std::basic_string<char>&&}’
make: *** [logman.o] Error 1
Thanks.
I think I'd take one of two approaches. The first possibility would be to use the map's operator[], so the code looked like:
category_search["hello"].push_back(5);
The other obvious possibility would be to recognize that what you're creating (each key having multiple mapped values) is equivalent to what an unordered_multimap provides, so you could just use:
std::unordered_multimap<std::string, int> category_search;
category_search.emplace("hello", 5);
Note that I've also used emplace to simplify the code (and as a bonus, probably run a little faster as well).
And yes, both of these support multiple values, so you could add:
category_search["hello"].push_back(10);
... or:
category_search.emplace("hello", 10);
...to the unordered_map/unordered_multimap respectively to also associate the value 10 with the key hello.
In the case of the unordered_multimap, you'd retrieve the set of values associated with a particular key using equal_range, like:
auto p = category_search.equal_range("hello");
This will put the beginning of the range in p.first and the end in p.second.
The second in the pair is a vector<int>, so you construct a vector with 1 element initialized with 5 instead of push_back.
categorySearch.insert(make_pair("hello", std::vector<int>(1,5)));
Live example: http://ideone.com/JlMUuN
If the item already exists, use the return value that unordered_map::insert gives you, which is a std::pair consisting of the iterator and a bool denoting the success of the insertion:
#include <vector>
#include <unordered_map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
typedef unordered_map<string, vector<int>> CMap;
CMap category_map;
category_map.insert(make_pair("hello", std::vector<int>(1, 5)));
auto pr = category_map.insert(make_pair("hello", std::vector<int>(1, 5)));
if (!pr.second)
pr.first->second.push_back(10);
cout << pr.first->second.size();
}
Live Example: http://ideone.com/svEqcD
You will see that the map entry for "hello" has now 2 items in the vector, since the second call to insert failed due to item previously existing.
See here: http://en.cppreference.com/w/cpp/container/unordered_map/insert

Problem with std::map and std::pair

I have a small program I want to execute to test something
#include <map>
#include <iostream>
using namespace std;
struct _pos{
float xi;
float xf;
bool operator<(_pos& other){
return this->xi < other.xi;
}
};
struct _val{
float f;
};
int main()
{
map<_pos,_val> m;
struct _pos k1 = {0,10};
struct _pos k2 = {10,15};
struct _val v1 = {5.5};
struct _val v2 = {12.3};
m.insert(std::pair<_pos,_val>(k1,v1));
m.insert(std::pair<_pos,_val>(k2,v2));
return 0;
}
The problem is that when I try to compile it, I get the following error
$ g++ m2.cpp -o mtest
In file included from /usr/include/c++/4.4/bits/stl_tree.h:64,
from /usr/include/c++/4.4/map:60,
from m2.cpp:1:
/usr/include/c++/4.4/bits/stl_function.h: In member function ‘bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = _pos]’:
/usr/include/c++/4.4/bits/stl_tree.h:1170: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = _pos, _Val = std::pair<const _pos, _val>, _KeyOfValue = std::_Select1st<std::pair<const _pos, _val> >, _Compare = std::less<_pos>, _Alloc = std::allocator<std::pair<const _pos, _val> >]’
/usr/include/c++/4.4/bits/stl_map.h:500: instantiated from ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [with _Key = _pos, _Tp = _val, _Compare = std::less<_pos>, _Alloc = std::allocator<std::pair<const _pos, _val> >]’
m2.cpp:30: instantiated from here
/usr/include/c++/4.4/bits/stl_function.h:230: error: no match for ‘operator<’ in ‘__x < __y’
m2.cpp:9: note: candidates are: bool _pos::operator<(_pos&)
$
I thought that declaring the operator< on the key would solve the problem, but its still there.
What could be wrong?
Thanks in advance.
The problem is this:
bool operator<(_pos& other)
Should be this:
bool operator<(const _pos& other) const {
// ^^^^ ^^^^^
Without the first const, the right-hand side of the comparison (b in a < b) cannot be const, since without const the function may modify its argument.
Without the second const, the left-hand side of the comparison (a in a < b) cannot be const, since without const the function may modify this.
Internally, the key's of a map are always const.
It should be noted that you should prefer to use nonmember functions. That is, better is a free-function:
bool operator<(const _pos& lhs, const _pos& rhs)
{
return lhs.xi < rhs.xi;
}
In the same namespace as your class. (For our example, just underneath it.)
By the way, in C++ there is no need to prefix the declaration of a struct type variable with struct. This is perfect, and preferred:
_pos k1 = {0,10};
_pos k2 = {10,15};
_val v1 = {5.5};
_val v2 = {12.3};
(Though your type names are admittedly named in an unorthodox manner. :P)
Lastly, you should prefer the make_pair utility function for making pairs:
m.insert(std::make_pair(k1,v1));
m.insert(std::make_pair(k2,v2));
It saves you from having to write out the types for the pair, and is generally easier to read. (Especially when longer type names come along.)
Signature of the less than operator needs to be bool operator<(const _pos& other) const, otherwise map can not use this operator in const functions since this member function is declared as non-const.
I think that your definition of operator< is wrong - the right hand side (argument in this case) should be marked const and it should be a const member function, e.g.
bool operator<(const _pos& other) const{
return this->xi < other.xi;
}