c++, stl map of pointers to functions vectors - c++

i seem to have some problem in creating a structure defined as the topic.
My objective is to create a sort of event handler, (it doesn't matter if it's good or bad programming,or if it is not multithread: for the moment is just for practice).
My idea is then to create a vector of pointers to functions, and place this vector in a map,where the key is a string.
i must be doing something conceptually wrong, because i get some strange errors:
My code is as following (errors are at the end):
.h file
//ptr to function
typedef int (*pt2Function)(void*);
typedef std::vector<pt2Function> fPtrVector;
class eventDispatcher
{
public:
//stuff
void addListener(std::string,pt2Function);
protected:
//stuff
std::map<std::string,fPtrVector> _listeners;
};
and here is the cpp:
.cpp file
void eventDispatcher::addListener(std::string eventName ,pt2Function function)
{
std::map<std::string,fPtrVector>::iterator it;
it=this->_listeners.find(eventName);
if(it != this->_listeners.end())
{
//do something
}
else
{
std::vector<pt2Function> tmp;
tmp.insert(function); // here occurs error 1
this->_listeners.insert(eventName,tmp); // here occurs error 2
std::cout<<"cnt: "<< this->_listeners.count();
}
}
The errors i get are:
1) no matching function for call to 'std::vector <int (*)(void*), std::allocator<int (*)(void*)> >::insert(int (*&)(void*))'
2) no matching function for call to 'std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<int (*)(void*), std::allocator<int (*)(void*)> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::vector<int (*)(void*), std::allocator<int (*)(void*)> > > > >::insert(std::string&, std::vector<int (*)(void*), std::allocator<int (*)(void*)> >&)'

If you check a reference to insert you see that it takes two arguments, an iterator and the value. To just add a value use e.g push_back instead.
For the map, you can use _listeners[eventName] = tmp; instead.

No variation of std::vector::insert() takes a single argument: all take an iterator indicating the position to which the new element(s) must be inserted before. Use std::vector::push_back() to add function instead.
std::map::insert() has several variations, but the one you are attempting to use takes the value_type of the map, which is defined as:
value_type std::pair<const Key, T>
in your case the value_type is a std::pair<std::string, fPtrVector>:
this->_listeners.insert(std::pair(eventName,tmp));

1) The insert() method of std::vector works by specifying a position using an iterator. For your case, you can use push_back().
2) Use insert(std::make_pair(eventName, tmp)) and that will create the required value type by the map.

You are looking for std::vector::push_back (see the documentation for std::vector here).
You are looking for std::map::operator [] (see the documentation for std::map here).

Related

Using accessors on reference to vector for iteration in C++

I am trying to iterate over a reference to a vector of strings inside a function. The code originally iterated over an internal copy of the vector but I would like to iterate over the original if possible. However, when I try to do so, I get type mismatch problems.
I tried to work with just portions of an iterator by setting and printing it, but I get similar type mismatching for both the initialization and printing with a %s format specifier. Inside gdb, printing the begin accessor works the same for the reference to the vector or a copy of that to its own vector.
Outside:
std::vector<std::string> foo;
foo.pushback('alpha');
foo.pushback('bravo');
func(foo);
Inside with copying:
void func(const std::vector<std::string> &bar){
std::vector<std::string> barcopy = bar;
for (std::vector<std::string>::iterator barIt = barcopy.begin(); barIt != barcopy.end(); barIt++){
//operations with the string inside barcopy
}
}
Inside without copying:
void func(const std::vector<std::string> &bar){
for (std::vector<std::string>::iterator barIt = bar.begin(); barIt != bar.end(); barIt++){
//operations with the string inside bar
}
}
I expected the reference to behave the same as the copy, but attempting this directly gets me the following when attempting to compile.
error: conversion from
'__gnu_cxx::__normal_iterator<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >'
to non-scalar type
'__gnu_cxx::__normal_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >'
requested
What type does the begin accessor return when performed on a reference to a vector? How do I go about iterating over this reference without copying?
Because you are passing bar as a const ref, you need to change:
for (std::vector<std::string>::iterator barIt = bar.begin(); barIt != bar.end(); barIt++)
to:
for (std::vector<std::string>::const_iterator barIt = bar.cbegin(); barIt != bar.cend(); barIt++)
or, better still, use a ranged for loop, if all you want is to iterate through the elements in the vector:
for (auto &elem : bar)
Alternatively, change your function signature to void func(std::vector<std::string> &bar) (without the const). The ranged for loop will work in either case.
Notice that you are taking the parameter as a const reference. The error is due to trying to take a non-const iterator from a const container. You could fix the error by changing your code to: (take note of the iterator type)
for (std::vector<std::string>::const_iterator barIt = bar.begin(); barIt != bar.end(); barIt++){
//operations with the string inside bar
}
Alternatively, you could use the auto keyword which will deduce the correct iterator type:
for (auto barIt = bar.begin(); barIt != bar.end(); barIt++){
//operations with the string inside bar
}

Indexing a Vector C++

I am new to Vectors, I think I have a syntactical error, or maybe I'm not calling .at() properly
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <string>
#include <ctype.h>
#include <vector>
using namespace std;
std::vector<string> symbols;
std::vector<int> symbolPos;
void addSymbol(string entry, int position){
for (std::vector<string>::const_iterator i = symbols.begin(); i != symbols.end(); ++i){
cout << "*i is: " << *i << endl;
// vvvvvvvvvvvvvvvvvvvvv Problematic line here vvvvvvvvvvvvvvvvvvvvv
if (symbols.at(i)==entry){
// ^^^^^^^^^^^^^^^^^^^^^ Problematic line here ^^^^^^^^^^^^^^^^^^^^^
cout << "same entry" << endl;
break;
}
else{
symbols.push_back(entry);
symbolPos.push_back(position);
}
}
}
My compiler is throwing an error, saying that .at() cannot be found. What am I doing wrong here?
assembler.cpp: In function ‘void addSymbol(std::string, int)’:
assembler.cpp:31: error: no matching function for call to ‘std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::at(__gnu_cxx::__normal_iterator<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >*, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >&)’
/usr/lib/gcc/i686-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_vector.h:650: note: candidates are: typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::reference std::vector<_Tp, _Alloc>::at(size_t) [with _Tp = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Alloc = std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >]
/usr/lib/gcc/i686-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_vector.h:668: note: typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::const_reference std::vector<_Tp, _Alloc>::at(size_t) const [with _Tp = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Alloc = std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >]
vector::at expects an argument of type vector::size_type, which is typically an alias for size_t; this is exactly what the error messages are telling you. You're trying to pass an instance of vector::iterator instead.
In your example you already have an iterator to the vector element, simply dereference it and do the equality comparison.
if (*i==entry)
You have bigger problems than that though, your entire for loop is basically undefined behavior waiting to happen. Say you pass an entry to addSymbol that doesn't match the first element in symbols. Your code causes the entry to be push_back'd into symbols, which will cause i to be invalidated if the vector need to reallocate storage. The for loop then increments i, which is undefined behavior. Moreover, do you want to keep iterating over the vector once you've added the element to the vector?
I think what you want to do is check whether the vector contains any element that matches entry, and add it if it doesn't exist. To do this, use find.
if(std::find(symbols.begin(), symbols.end(), entry) == symbols.end()) {
// entry doesn't exist in the vector
symbols.push_back(entry);
symbolPos.push_back(position);
}
You do not need to call at with i, since i is an iterator, so a pointer.like object and you can acces the corresponding value via *i. You can use at with a integer index.
change this to
if (symbols.at(i)==entry)
to
if (symbols.at(*i)==entry)
Since this function is expecting an integer not a pointer. :)
You are trying to pass an interator to the symbols vector
if (symbols.at(i)==entry)
iterators are not like looping integer variables. so the code must be
if (*i == entry)
if you want to use at then replace the for loop with integer value like
for (i = 0; i < symbols.size; ++i)
Using iterators
void addSymbol(string entry, int position){
// you can use find function instead of using your own loop
std::vector<string>::iterator i = find(symbols.begin(), symbols.end(), entry);
// if not found
if (i == symbols.end()){
symbols.push_back(entry);
symbolPos.push_back(position);
}
else
cout << "same entry" << endl;
}
There is no method "at" in vector which expects iterator as an input .
It expects size_type parameter . Since you are using an iterator you can simply do strcmp(*i,entry) .
Or another way is to simply switch from using iterator to using operator[] of vector i.e
for(int i=0;i<v.size();i++){
cout<<i<<" th element : "<<v[i]<<endl;
if(v[i]==entry){
cout<<"Entry Already Exists"<<endl;
break;
}
}
Another very easy way is to use "find" method , by which you can simply search for existence of entry in the present list

Inserting strings to vector

I am getting a compilation error. I am trying to add strings to a vector and keep them in "sorted order".
XYZ is my class. addPortEntry
class XYZ
{
public:
portListFile(string sTmp);
void addPortEntry(string sPortName, string sDirection);
private:
string sPortListFileName;
vector <string> v_input_ports;
...
};
void XYZ::addP(string sP, string sDir)
{
if(sDir == "in")
{
v_input_ports.insert(sP); // Line 42
}
...
}
Error:
XYZ.cpp: In member function ‘void XYZ::addP(std::string, std::string)’:
XYZ.cpp:42: error: no matching function for call to ‘std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::insert(const char [10])’
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/vector.tcc:93: note: candidates are: typename std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::insert(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, const _Tp&) [with _Tp = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Alloc = std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >]
/usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:657: note: void std::vector<_Tp, _Alloc>::insert(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, size_t, const _Tp&) [with _Tp = std::basic_string<char, std::char_traits<char>, std::allocator<char> >, _Alloc = std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >]
insert should be given an iterator to insert at a certain location. You need to use push_back instead (which is the same as insert with end() as parameter).
edit
You mentioned in the comments:
I do not want to use push_back since I want the strings to be sorted.
That is one of the reasons I'm using the vector
I miss the logic in that statement. If you want a sorted container you should be using either std::set or std::map. Use the "multi-" versions if you want repeating values.
Perhaps you meant push_back rather than insert?
Generally speaking, you can either append an element to a vector
(push_back), or you can insert it at a specified place (insert). To
insert an object at a specified place, you have to specify the place;
std::vector<>::insert takes two arguments, the first an iterator
specifying where, and the second the value to be inserted.
You say you want to maintain the contents in sorted order. The usual
idiom for that is to find the location using std::lower_bound, e.g.:
void
XYZ::addP( std::string const& sP, std::string const& sDir )
{
if ( sDir == "in" ) {
std::vector<std::string>::iterator pos
= std::lower_bound( v_input_ports.begin(),
v_input_ports.end(),
sP );
if ( pos != v_input_ports.end() && *pos == sDir ) {
// Object already present...
*pos = sP; // But maybe an error is more appropriate
} else {
v_input_ports.insert( pos, sP );
}
}
}
Two quick comments, however:
You should probably be passing std::string by const reference,
rather than by value. For whatever reasons, this is the almost
universal convention, and if you don't follow it, people will wonder
why.
Any time you're concerned with order in the standard, you need to
define the ordering relationship. The default is std::less<>, which by
default does <. But functions like std::lower_bound can be passed
an additional argument which defines the order in any way you want
(subject to the constraint that it is a "strict weak ordering"). I
mention this because the way < is defined on std::string is useless
in most cases where you're dealing with actual text, so you may want to
consider defining your own ordering relationship.

C++ unordered_map user defined type

I am having a class which is used as the key in the unordered_map. When I tried to compiled the code, it shows undefined reference to std::hash<typeName>::operator()(typename) const. How could I go to fix it? What additional function do I need to overload to make the user defined type to be used in an unordered_map?
I have a dateTime struct which stores the info of date and time.
The error message is as follows:
In function 'std::__detail::_Hash_code_base<DateTime, std::pair<DateTime const, int>, std::_Select1st<std::pair<DateTime const, int> >, std::equal_to<DateTime>, std::hash<DateTime>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_hash_code(DateTime const&) const':
testing.cpp:(.text._ZNKSt8__detail15_Hash_code_baseI10DateTimeSt4pairIKS1_DeESt10_Select1stIS4_ESt8equal_toIS1_ESt4hashIS1_ENS_18_Mod_range_hashingENS_20_Default_ranged_hashELb0EE12_M_hash_codeERS3_[std::__detail::_Hash_code_base<DateTime, std::pair<DateTime const, int>, std::_Select1st<std::pair<DateTime const, int> >, std::equal_to<DateTime>, std::hash<DateTime>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>::_M_hash_code(DateTime const&) const]+0x23): undefined reference to 'std::hash<DateTime>::operator()(DateTime) const'
Thanks.
you have to implement hash algorithm, otherwise standard container will not pick your type, because it has no idea how to calculate hash code for it.
namespace std
{
template <>
struct hash<DateTime> : public unary_function<DateTime, size_t>
{
size_t operator()(const DateTime& v) const
{
return /* my hash algorithm */;
}
};
}

Using a const key for unordered_map

I've been switching my code over from std::map to std::unordered_map where appropriate. With std::map, I typically write the following just to make sure the key cannot be modified:
std::map<const std::string, int>
Frankly, I never checked if this const was of any value. This has always compiled and worked with g++.
Now, with std::unordered_map, the following fails to link with g++ 4.5.1.
std::unordered_map<const std::string, std::string> m;
m["foo"] = "bar";
with this link error:
Undefined symbols:
"std::hash<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const>::operator()(std::basic_string<char, std::char_traits<char>, std::allocator<char> >) const", referenced from:
The fix is simple, to remove const, but besides that, is there even a point in STL with any of the associative container classes to use a const key type? Are there no methods that let you get a reference to the key for any associative container?
The associative containers only expose the (key,value) pair as std::pair<const key_type, mapped_type>, so the additional const on the key type is superfluous.
std::unordered_map<std::string const, std::string> uses std::hash<std::string const>, but no specialization exists for hash<std::string const>. However, there is one for std::hash<std::string>. You can provide one if you wish to use a std::string const as your key:
struct constant_string_hash {
std::size_t operator () (std::string const &s) const {
return hash<std::string>{}(s);
}
};
You can then declare your std::unordered_map like this:
std::unordered_map<std::string const, std::string, constant_string_hash> m;
m["test key"] = "test value";