Can't access a map member from a pointer - c++

That's my first question :)
I'm storing the configuration of my program in a Group->Key->Value form, like the old INIs. I'm storing the information in a pair of structures.
First one, I'm using a std::map with string+ptr for the groups info (the group name in the string key). The second std::map value is a pointer to the sencond structure, a std::list of std::maps, with the finish Key->Value pairs.
The Key->Value pairs structure is created dynamically, so the config structure is:
std::map< std::string , std::list< std::map<std::string,std::string> >* > lv1;
Well, I'm trying to implement two methods to check the existence of data in the internal config. The first one, check the existence of a group in the structure:
bool isConfigLv1(std::string);
bool ConfigManager::isConfigLv1(std::string s) {
return !(lv1.find(s)==lv1.end());
}
The second method, is making me crazy... It check the existence for a key inside a group.
bool isConfigLv2(std::string,std::string);
bool ConfigManager::isConfigLv2(std::string s,std::string d) {
if(!isConfigLv1(s))
return false;
std::map< std::string , std::list< std::map<std::string,std::string> >* >::iterator it;
std::list< std::map<std::string,std::string> >* keyValue;
std::list< std::map<std::string,std::string> >::iterator keyValueIt;
it = lv1.find(s);
keyValue = (*it).second;
for ( keyValueIt = keyValue->begin() ; keyValueIt != keyValue->end() ; keyValueIt++ )
if(!((*keyValueIt).second.find(d)==(*keyValueIt).second.end()))
return true;
return false;
}
I don't understand what is wrong. The compiler says:
ConfigManager.cpp||In member function ‘bool ConfigManager::isConfigLv2(std::string, std::string)’:|
ConfigManager.cpp|(line over return true)|error: ‘class std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, 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::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >’ has no member named ‘second’|
But it has to have the second member, because it's a map iterator...
Any suggestion about what's happening?
Sorry for my English :P, and consider I'm doing it as a exercise, I know there are a lot of cool configuration managers.

keyValueIt is not a map iterator, it is a list iterator.
You can just do
if (keyValueIt->find(d) != keyValueIt->end())

I think Joel is correct with
if (keyValueIt->find(d) != keyValueIt->end())
However I wanted to encourage you use some typedefs to try and simplify your code. Using typedefs can help when diagnosing problems like this (and if you're lucky your compiler will give you more meaningful error messages as a result.
For instance:
typedef std::map<std::string,std::string> KeyValueMap;
typedef std::list< KeyValueMap > ConfigurationList;
typedef std::map< std::string, ConfigurationList* > ConfigurationMap;
bool isConfigLv2(std::string,std::string);
bool ConfigManager::isConfigLv2(std::string s,std::string d) {
if(!isConfigLv1(s))
return false;
ConfigurationMap::iterator it;
ConfigurationList* keyValue;
ConfigurationList::iterator keyValueIt; // <- it's not a keyValue iterator, it's a ConfigList iterator!
it = lv1.find(s);
keyValue = (*it).second;
for ( keyValueIt = keyValue->begin() ; keyValueIt != keyValue->end() ; keyValueIt++ )
if(!((*keyValueIt).second.find(d)==(*keyValueIt).second.end()))
return true;
return false;
}
Simplifying the types makes it more obvious to me that keyValueIt is probably being misued (i.e. it's actually a list iterator, and not a KeyValueMap iterator and so the '.second' access is erroneous.)

If you just want a group/key/value structure you are over-complicating it, you have one more level in your data structure then needed.
The additional list isn't needed, a map of maps is sufficient:
// typedefs for readability:
typedef std::map<std::string, std::string> Entries;
typedef std::map<std::string, Entries> Groups;
// class member:
Groups m_groups;
bool ConfigManager::hasKey(const std::string& group, const std::string& key)
{
Groups::const_iterator it = m_groups.find(group);
if(it == m_groups.end())
return false;
const Entries& entries = it->second;
return (entries.find(key) != entries.end());
}

Related

sorting a vector of std::pair<int, std::unique_ptr<const T> > depending on pair.first

I'm trying to sort a vector of pairs containing a smart pointers of a const object. I'm trying to sort only depending on the first object. Below you see (one of my numerous attempts to write) code that is supposed to do this, alongside with an excerpt from the error.
The compiler complains about the lambda parameters. I've tried to make the parameters const, non-const, references, rvalue-references, to no avail. Help please?
std::pair<int, std::unique_ptr<const std::string> > a;
auto uniq = std::make_unique<const std::string>("hurz");
a = std::make_pair(1,std::move(uniq));
std::pair<int, std::unique_ptr<const std::string> > b;
uniq = std::make_unique<const std::string>("hurz");
b = std::make_pair(2,std::move(uniq));
std::vector<std::pair<int,std::unique_ptr<const std::string> > > vec;
vec.push_back(std::move(a));
vec.push_back(std::move(b));
std::sort(std::make_move_iterator(vec.begin()),
std::make_move_iterator(vec.end()),
[]
(const std::pair<int,std::unique_ptr<const std::string> >& i1,
const std::pair<int,std::unique_ptr<const std::string> >& i2)
{ return i1.first > i2.first;});
The error message didn't help me:
error: no matching function for call to
'swap(std::move_iterator<__gnu_cxx::__normal_iterator<std::pair<int,
std::unique_ptr<const std::basic_string<char> > >*, std::vector<std::pair<int,
std::unique_ptr<const std::basic_string<char> > > > > >::value_type,
std::move_iterator<__gnu_cxx::__normal_iterator<std::pair<int,
std::unique_ptr<const std::basic_string<char> > >*, std::vector<std::pair<int,
std::unique_ptr<const std::basic_string<char> > > > > >::value_type)' swap(*__a,
*__b);
candidates are:
/usr/include/c++/4.9/bits/move.h:166:5: note: void std::swap(_Tp&, _Tp&)
[with _Tp = std::pair<int, std::unique_ptr<const std::basic_string<char> > >]
plus many more errors in the same vein
The issue here is the use of std::make_move_iterator. When you do so you turn the iterators into move_iterator's which means when you dereference them you get a T&&, instead of a T& like you do with normal iterators.
std::swap, which is used in your implementation of std::sort only takes lvalue references so it cannot bind to the dereferenced iterator. If you use
std::sort(vec.begin(),
vec.end(),
[]
(const std::pair<int,std::unique_ptr<const std::string> >& i1,
const std::pair<int,std::unique_ptr<const std::string> >& i2)
{ return i1.first > i2.first;});
instead then you will have lvalues for std::swap to bind to and std::swap will work for move only types
I add some further explanation to the answer of #NathanOliver which is too long for a comment. I guess the OP's idea of using
std::sort(std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end()),
[](const std::pair<int,std::unique_ptr<const std::string> >& i1,
const std::pair<int,std::unique_ptr<const std::string> >& i2)
{ return i1.first > i2.first;});
i.e., with a move_iterator applied to vector.begin(), is to use move assignments (and not copies) inside the sorting routine. This idea is tempting, but it's not necessary because inside std::sort, assignments are usually done with std::swap which tries to move arguments passed by reference.
On the other hand, for algorithms which use input and output iterators, such as most basically std::copy or std::copy_if, the use of std::make_move_iterator can be quite useful. Those often use constructs like *output_it = *input_it, and together with std::make_move_iterator this corresponds to *output_it = std::move(*input_it), so the move-assignment operator of the type dereferenced by *output_it could be used.

how can to use array containing c++ map

I have difficulty understanding this part of a code? Is it possible to get pictorial/diagram explanation.
//test.h
typedef std::map<std::string, std::string> mType;
static const m_Type::value_type data[] = {
m_Type::value_type("A", "B"),
m_Type::value_type("C", "D"),
m_Type::value_type("E", "F")
};
//test.cc
void test(std::map<std::string, std::string>::value_type data)
{
cout<<data[0].first<<endl;
}
//main.cc
test(data);
In main.cc I wanted to call test() to print elements but getting error
main.cc: In function 'void test(std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >)':
main.cc:10: error: no match for 'operator[]' in 'data[0]'
You have to declare the function the following way
void test( const std::map<std::string, std::string>::value_type data[] )
{
cout<<data[0].first<<endl;
}
because originally data is defined as an array that you are going to pass to the function. Otherwise you may not use the subscript operator.
Also you need to use qualifier const for the parameter because array data also is defined as a constant array.
As for this type
m_Type::value_type
then it is equivalent to std::pair<const std::string, std::string> So this declaration
static const m_Type::value_type data[] = { /*...*/ };
is equivalent to
static const std::pair<const std::string, std::string> data[] = { /*...*/ };
You are supposed to pass to test a value_type not the whole map.
A value_type corresponds to a pair(key, value) and is define like so in stdlib : typedef pair<const Key, Type> value_type;
therefore this data[0]makes no sense. Should be data.first

Why does const get added to the string from this pair extracted from an iterator?

In the following example, does anyone know why const gets added to the string giving the error below (same with gcc and VS2008)?
#include<utility>
#include<ostream>
#include<string>
#include<map>
class Foo
{
};
class Test
{
public:
Test() { myMap.insert(std::make_pair("a string", Foo())); }
std::pair<std::string, Foo>& GetPair() { return *(myMap.begin()); }
private:
std::map<std::string, Foo> myMap;
};
int main()
{
Test t;
std::pair<std::string, Foo>& myPair = t.GetPair();
return 0;
}
Error:
t.cpp: In member function 'std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Foo>& Test::GetPair()':
Line 14: error: invalid initialization of reference of type 'std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Foo>&' from expression of type 'std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Foo>'
compilation terminated due to -Wfatal-errors.
I don't understand why this would be as I'm not using cbegin(). Also why would the string be const, but not the Foo?
Because if you changed the key part of the pair, then it would suddenly be stored in the wrong location in the map's data structure (bucket, position in tree, or something else depending on the structure). The map doesn't get notified when the key part is changed, so it simply does not allow you to modify it.
On the other hand, the value part is irrelevant to where the pair is stored in the map structure, so you are allowed to change that.

C++ Sort case insensitive vector pair

I have the following vector
std::vector< std::pair<std::string, std::pair<std::string, std::string> > > vecList;
In the loop I add the entry:
vecList.push_back(std::make_pair(name.toStdString(), std::make_pair(path.toStdString(), arguments.toStdString())));
I want to call:
std::sort(vecList.begin(), vecList.end(), CompareSort);
bool SettingsData::CompareSort(const std::pair <std::string, std::pair<std::string, std::string> > &first,
const std::pair< std::string, std::pair<std::string, std::string> > &second)
{
unsigned int i=0;
/*
while ( (i < first.length()) && (i <second.length()) )
{
if (tolower(first[i]) < tolower(second[i]))
return true;
else if (tolower(first[i]) > tolower(second[i]))
return false;
++i;
}
return ( first.length() < second.length() );
*/
return true;
}
I want to sort by the first pair (name) string, but when I get a compile error:
error: no matching function for call to 'sort(std::vector<std::pair<std::basic_string<char>, std::pair<std::basic_string<char>, std::basic_string<char> > > >::iterator, std::vector<std::pair<std::basic_string<char>, std::pair<std::basic_string<char>, std::basic_string<char> > > >::iterator, <unresolved overloaded function type>)'
std::sort(vecList.begin(), vecList.end(), CompareSort);
Can anyone give a tip on what I can be doing wrong
Updated Fix
In my header file I just set the declaration to static:
static bool CompareSort(const std::pair< std::string, std::pair<std::string, std::string> > &first,
const std::pair< std::string, std::pair<std::string, std::string> > &second);
Thanks to Joachim suggestion
The most likely reason is that CompareSort is a non-static member function, which means it actually have a hidden first argument (the this pointer). If you can make it static it should work okay. Otherwise you can make the function a non-member function, or use std::bind.

Accessing a vector which is a value in a map to run the equality operator on it with another vector

I have a vector that is made up of maps which have the following type definition:
std::map< int, std::vector<int> >
Thus, the definition of the vector itself is:
std::vector< std::map< int, std::vector<int> > >
Now I am creating a vector of integers and inserting it in a map with an integer key and adding the map itself to the outer vector:
std::map<int, std::vector<int> > vDescriptorAtom;
vDescriptorAtom.insert( std::make_pair(498, vListOfIds) );
messageDescriptorVector.push_back( vDescriptorAtom );
Where vListOfIds itself is a vector of integers.
At a later stage I want to extract the inner vector and compare with another vector that I possess. I was under the impression that I can easily use the == operator between the two vectors to do the comparison. But I am having some trouble doing this.
I tried:
for ( int i = 0 ; i <= messageDescriptorVector.size(); i++ )
{
if ( current_tag_stack == (messageDescriptorVector.at(i))->second )
{
vFoundMatchingDescriptor = true;
break;
}
}
Note that current_tag_stack has the following definition:
std::vector<int> current_tag_stack;
But I am getting this error:
base operand of â->â has non-pointer type âstd::map<int, std::vector<int, std::allocator<int> >, std::less<int>, std::allocator<std::pair<const int, std::vector<int, std::allocator<int> > > > >â
What am I doing wrong ?
EDIT
As suggested by a comment, I tried to access the vector that I have stored inside the map as: messageDescriptorVector.at(i).second but it is giving me this error: âclass std::map<int, std::vector<int, std::allocator<int> >, std::less<int>, std::allocator<std::pair<const int, std::vector<int, std::allocator<int> > > > >â has no member named âsecondâ
messageDescriptorVector.at(i) is a map. You have to obtain an element of that map, in order to get it's second and compare it with your reference vector. So you need one more level of iteration.
for ( int i = 0 ; i < messageDescriptorVector.size(); i++ )
{
auto& m = messageDescriptorVector.at(i); // m is a map<int, vector<int>>
for (auto& p : m) // p is a ref to pair<int, vector<int>>
{
if ( current_tag_stack == p.second )
{
// do something ....
or just
for (auto& m : messageDescriptorVector)
{
for (auto& p : m)
{
if ( current_tag_stack == p.second )
{
// do something ....
Aside of issue that you are trying to accessing member second on a map, I would recommend to use typedefs in your code. It will not only simplify your code, but will help to change data structure if necessary. In your case all of this definitions all around your code:
std::map< int, std::vector<int> >
std::vector< std::map< int, std::vector<int> > >
std::map<int, std::vector<int> > vDescriptorAtom;
std::vector<int> current_tag_stack;
Compare it with:
typedef std::vector<int> tag_stack;
typedef std::map<int,tag_stack> tag_stack_map;
tag_stack_map vDescriptorAtom;
tag_stack current_tag_stack;
And imagine one day you would need to change your tag_stack from std::vector to something else. Where it will be easier?
function at() returns a reference to the element at specified location pos, so you should write:
(messageDescriptorVector.at(i)).second
I preferred write this code like:
std::map<int, std::vector<int>> vDescriptorAtom;
std::vector<std::map<int, std::vector<int>>> messageDescriptorVector;
vDescriptorAtom[498] = vListOfIds;
messageDescriptorVector.push_back( vDescriptorAtom );
// do something
for(auto& maps : messageDescriptorVector)
for(auto& pairs : maps)
if (current_tag_stack == pairs.second)
// do something