lakshmikarle
asked on
How to use of const char* as primary key of STL map?
Hello friends,
I am trying to use const char* as the primary key of map. But since contents pointed by pointer can change any time, map's primary key is becoming null.
The following program demonstates my problem. Is there anyway to replace assignment of primary key with strcpy? Also, I understand that this problem can be solved by writing a wrapper class for pointer. But I want to know whether STL provides any way to resolve this problem:
Program :
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<map>
using namespace std;
struct ltstr
{
bool operator()(const char* s1, const char* s2) const
{
return strcmp(s1, s2) ;
}
};
main()
{
map<const char*, int,ltstr> map_v;
char testt[20] = {"first"};
map_v.insert(map<const char*, int, ltstr>::value_type(testt, 1));
map_v.insert(map<const char*, int, ltstr>::value_type("second ", 2));
map_v.insert(map<const char*, int, ltstr>::value_type("third" , 3));
cout<< "List of Elements of map" << endl;
cout<< "size = "<< map_v.size()<<endl;
map<const char*, int, ltstr> ::iterator itr;
for(itr=map_v.begin(); itr!=map_v.end(); itr++)
{
cout<< itr->first<< ","<< itr->second<<endl;
}
cout <<endl <<endl;
cout<< "Before assigning test string to NULL" <<endl;
itr = map_v.find("first");
if(itr != map_v.end())
{
cout<< "Element found" <<endl;
cout<< itr->second<< ","<< itr->second<<endl;
}
else
{
cout<< "Element not found" <<endl;
}
cout <<endl <<endl;
memset(testt , '\0', sizeof(testt));
cout<< "After assigning test string to NULL" <<endl;
itr = map_v.find("first");
if(itr != map_v.end())
{
cout<< "Element found" <<endl;
cout<< itr->second<< ","<< itr->second<<endl;
}
else
{
cout<< "Element not found" <<endl;
}
}
Output:
List of Elements of map
size = 3
third,3
second,2
first,1
Before assigning test string to NULL
Element found
1,1
After assigning test string to NULL
Element not found
I am trying to use const char* as the primary key of map. But since contents pointed by pointer can change any time, map's primary key is becoming null.
The following program demonstates my problem. Is there anyway to replace assignment of primary key with strcpy? Also, I understand that this problem can be solved by writing a wrapper class for pointer. But I want to know whether STL provides any way to resolve this problem:
Program :
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<vector>
#include<map>
using namespace std;
struct ltstr
{
bool operator()(const char* s1, const char* s2) const
{
return strcmp(s1, s2) ;
}
};
main()
{
map<const char*, int,ltstr> map_v;
char testt[20] = {"first"};
map_v.insert(map<const char*, int, ltstr>::value_type(testt, 1));
map_v.insert(map<const char*, int, ltstr>::value_type("second
map_v.insert(map<const char*, int, ltstr>::value_type("third"
cout<< "List of Elements of map" << endl;
cout<< "size = "<< map_v.size()<<endl;
map<const char*, int, ltstr> ::iterator itr;
for(itr=map_v.begin(); itr!=map_v.end(); itr++)
{
cout<< itr->first<< ","<< itr->second<<endl;
}
cout <<endl <<endl;
cout<< "Before assigning test string to NULL" <<endl;
itr = map_v.find("first");
if(itr != map_v.end())
{
cout<< "Element found" <<endl;
cout<< itr->second<< ","<< itr->second<<endl;
}
else
{
cout<< "Element not found" <<endl;
}
cout <<endl <<endl;
memset(testt , '\0', sizeof(testt));
cout<< "After assigning test string to NULL" <<endl;
itr = map_v.find("first");
if(itr != map_v.end())
{
cout<< "Element found" <<endl;
cout<< itr->second<< ","<< itr->second<<endl;
}
else
{
cout<< "Element not found" <<endl;
}
}
Output:
List of Elements of map
size = 3
third,3
second,2
first,1
Before assigning test string to NULL
Element found
1,1
After assigning test string to NULL
Element not found
evilrix🇬🇧
Why don't you just use std::string for the key?
map<std::string, int,ltstr> map_v;
BTW: In C++ the string.h header is deprecated. You should either use <cstring> for c style string header of <string> for std::string header. Likewise, <stdio.h> should be <cstdio>
map<std::string, int,ltstr> map_v;
BTW: In C++ the string.h header is deprecated. You should either use <cstring> for c style string header of <string> for std::string header. Likewise, <stdio.h> should be <cstdio>
ASKER CERTIFIED SOLUTION
evilrix🇬🇧
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
SOLUTION
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
evilrix🇬🇧
BTW: Modifying the key my memseting testt to be null will probably invalidate and corrupt your map. The items in the map are sorted based upon the key when you insert them. If you modify the key once the items are inserted you effectively have a item int he map that is probably out of place. This is unsafe and will result in undefined behavior.
evilrix🇬🇧
>> Modifying the key my memseting testt to be null will probably invalidate and corrupt your map
Similar in principle to this...
"Whenever you access a key-value pair in an std::map, you get a pair whose first component is const, even if you did not explicitly make the key type constant in your map declaration. This is necessary because the map is internally ordered by keys. If it were possible for you to modify keys, you would be corrupting the map, rendering it inconsistent and useless."
http://www.ddj.com/cpp/184401371
Similar in principle to this...
"Whenever you access a key-value pair in an std::map, you get a pair whose first component is const, even if you did not explicitly make the key type constant in your map declaration. This is necessary because the map is internally ordered by keys. If it were possible for you to modify keys, you would be corrupting the map, rendering it inconsistent and useless."
http://www.ddj.com/cpp/184401371
lakshmikarle
ASKER
Hello evilrix and infinity08,
thanks for your reply. string is posing some problem in mult-threaded environment. So, I planned to replace all strings with constant char* in my project. But, Iam facing above problem.
Like we use this 3 argument ltstr tostrcmp instead of ==.
Is there anything using which I can replace simple assignment of map key with strcpy.
Thanks,
thanks for your reply. string is posing some problem in mult-threaded environment. So, I planned to replace all strings with constant char* in my project. But, Iam facing above problem.
Like we use this 3 argument ltstr tostrcmp instead of ==.
Is there anything using which I can replace simple assignment of map key with strcpy.
Thanks,
>> string is posing some problem in mult-threaded environment.
What kind of problem ? It's likely that the same problems will occur with char*'s.
>> Is there anything using which I can replace simple assignment of map key with strcpy.
As I said : allocate some dynamic memory, and copy the string into it. Then use the copy as primary key, not the original. That way, any changes to the original will not impact the copy, and thus won't impact the map.
What kind of problem ? It's likely that the same problems will occur with char*'s.
>> Is there anything using which I can replace simple assignment of map key with strcpy.
As I said : allocate some dynamic memory, and copy the string into it. Then use the copy as primary key, not the original. That way, any changes to the original will not impact the copy, and thus won't impact the map.
evilrix🇬🇧
>> string is posing some problem in mult-threaded environment. So, I planned to replace all strings with constant char*
String is posing MT issues that char * will resolve? I'd be interested to know what they are because current versions of std::string in both gcc and Windows are perfectly safe to use in MT providing you ensure all mutating methods are protecting with mutual exclusion.I know this to be true because the application I develop is x-platform, exclusively STL/Posix/ANSI C++ and designed (and tested) to be fully MT.
>> Is there anything using which I can replace simple assignment of map key with strcpy.
There is no reason for not using std:;string. If you think you have MT issues then maybe that is what you should be asking about. Your proposed solution is, ostensibly, dangerous and is likely to cause you much future pain!
char * and STL contains do not mix well (although there is no technical reason you can't use pointers in an STL class). You are trying to mix C and C++ idioms here -- they don't play well together!
String is posing MT issues that char * will resolve? I'd be interested to know what they are because current versions of std::string in both gcc and Windows are perfectly safe to use in MT providing you ensure all mutating methods are protecting with mutual exclusion.I know this to be true because the application I develop is x-platform, exclusively STL/Posix/ANSI C++ and designed (and tested) to be fully MT.
>> Is there anything using which I can replace simple assignment of map key with strcpy.
There is no reason for not using std:;string. If you think you have MT issues then maybe that is what you should be asking about. Your proposed solution is, ostensibly, dangerous and is likely to cause you much future pain!
char * and STL contains do not mix well (although there is no technical reason you can't use pointers in an STL class). You are trying to mix C and C++ idioms here -- they don't play well together!
evilrix🇬🇧
>> current versions of std::string in both gcc and Windows are perfectly safe to use in MT
Early Visual Studio 6.0 versions of std::string were not thread safe as they implemented a reference counting mechanism to try and optimize performance... it didn't work to well in MT code. 7.1 onwards and (AFAIK) all gcc versions of std::string are safe to use in MT.
Early Visual Studio 6.0 versions of std::string were not thread safe as they implemented a reference counting mechanism to try and optimize performance... it didn't work to well in MT code. 7.1 onwards and (AFAIK) all gcc versions of std::string are safe to use in MT.
evilrix🇬🇧
>> As I said : allocate some dynamic memory, and copy the string into it. Then use the copy as primary key, not the original. That way, any changes to the original will not impact the copy, and thus won't impact the map.
I agree... except if you use std::string this will all be done for you... so you may as well save yourself the time and effort of reinventing the wheel :)
I agree... except if you use std::string this will all be done for you... so you may as well save yourself the time and effort of reinventing the wheel :)
>> except if you use std::string this will all be done for you...
Indeed :)
Indeed :)
evilrix🇬🇧
> Indeed :)
:-D
:-D
To add to above comments.
I strongly agree to the suggestion made to change the key to std::string.
In a multi-threaded environment std::string hardly is an issue. But of course if you want to access the map concurrently in different threads you must make that access an exclusive operation, e. g. by means of a mutex or critical section.
>>>> return strcmp(s1, s2) ;
The return is wron as you have to return true if the right string is less than the right one.
bool operator() (const char*& psz1, const char*& psz2) const
{
if (psz1 == NULL) return true;
if (psz2 == NULL) return false;
return strcmp(psz1, psz2) < 0;
}
Note, if you have to *change* the key you *must* erase the old entry in the map and add the new key - value pair. The pointer shouldn't be NULL (though I tried to handle that case) as a map accepts only unique keys.
void changekeyinmap(std::map<co nst char*, int,ltstr>& map_v,
const char* psz1, const char* psz2)
{
std::map<const char*, int,ltstr>::iterator it;
it = map_v.find(psz1);
if (it != map_v.end())
{
int val = it.second;
map_v.erase(it);
map_v[psz2] = val;
}
}
I strongly agree to the suggestion made to change the key to std::string.
In a multi-threaded environment std::string hardly is an issue. But of course if you want to access the map concurrently in different threads you must make that access an exclusive operation, e. g. by means of a mutex or critical section.
>>>> return strcmp(s1, s2) ;
The return is wron as you have to return true if the right string is less than the right one.
bool operator() (const char*& psz1, const char*& psz2) const
{
if (psz1 == NULL) return true;
if (psz2 == NULL) return false;
return strcmp(psz1, psz2) < 0;
}
Note, if you have to *change* the key you *must* erase the old entry in the map and add the new key - value pair. The pointer shouldn't be NULL (though I tried to handle that case) as a map accepts only unique keys.
void changekeyinmap(std::map<co
const char* psz1, const char* psz2)
{
std::map<const char*, int,ltstr>::iterator it;
it = map_v.find(psz1);
if (it != map_v.end())
{
int val = it.second;
map_v.erase(it);
map_v[psz2] = val;
}
}
lakshmikarle
ASKER
Hello,
Thanks for your help.
Now, I am thinking whether I am facing problem because of deprecated string I am using which evilrix pointed. Thread was hanging in string assignment statement. Replacing assignment stmt alone with some hardcoding solved the problem temporarily. Problem was only on solaris 10 and thread was working fine on solaris 8. On truss, I noticed that thread was hanging while lwp_mutexthreadlock(). In the particular function which was giving me problem I replaced string with char* whereever possible and my thread started working. Also, I found the following information on net.
http://www.sgi.com/tech/stl/thread_safety.html
https://www.experts-exchange.com/questions/20462996/thread-safe-compile-on-solaris-8.html?sfQueryTermInfo=1+safe+sgi+solari+stl+string+thread+vector&anchorAnswerId=7752386#a7752386
Any suggestions??
Thanks for your help.
Now, I am thinking whether I am facing problem because of deprecated string I am using which evilrix pointed. Thread was hanging in string assignment statement. Replacing assignment stmt alone with some hardcoding solved the problem temporarily. Problem was only on solaris 10 and thread was working fine on solaris 8. On truss, I noticed that thread was hanging while lwp_mutexthreadlock(). In the particular function which was giving me problem I replaced string with char* whereever possible and my thread started working. Also, I found the following information on net.
http://www.sgi.com/tech/stl/thread_safety.html
https://www.experts-exchange.com/questions/20462996/thread-safe-compile-on-solaris-8.html?sfQueryTermInfo=1+safe+sgi+solari+stl+string+thread+vector&anchorAnswerId=7752386#a7752386
Any suggestions??
evilrix🇬🇧
>> whether I am facing problem because of deprecated string I am using which evilrix pointed.
if you mix old and new headers you can get undefined behavior.
Note... although all of the STL containers classes are safe to use in MT code for non-mutating access it isn't safe to call mutating methods unless you protect all access with mutual exclusion.
if you mix old and new headers you can get undefined behavior.
Note... although all of the STL containers classes are safe to use in MT code for non-mutating access it isn't safe to call mutating methods unless you protect all access with mutual exclusion.
lakshmikarle
ASKER
evilrix,
I have only 2 threads in my program and as soon as the second thread was created, I was returning from that for debugging purpose. But, I was still getting problem. Do you still thing the problem was likely because of mutating methods?
Should I replace string with std::string in my code?
I have only 2 threads in my program and as soon as the second thread was created, I was returning from that for debugging purpose. But, I was still getting problem. Do you still thing the problem was likely because of mutating methods?
Should I replace string with std::string in my code?
evilrix🇬🇧
>> Do you still thing the problem was likely because of mutating methods?
Without being able to review your code I can only conjecture... and I'm not sure that'd help. I know for a fact that all versions of std::string work fine MT from gcc3.2.2 and VC7.1 onwards because I work with them daily in a MT intensive application... we have no known MT issues.
>> Should I replace string with std::string in my code?
You should remove all includes of <string.h> and only use <cstring> for c style header and <string> for std::string an d you should ensure all you code uses the std::string version of string.
Without being able to review your code I can only conjecture... and I'm not sure that'd help. I know for a fact that all versions of std::string work fine MT from gcc3.2.2 and VC7.1 onwards because I work with them daily in a MT intensive application... we have no known MT issues.
>> Should I replace string with std::string in my code?
You should remove all includes of <string.h> and only use <cstring> for c style header and <string> for std::string an d you should ensure all you code uses the std::string version of string.
>>>> Thread was hanging in string assignment statement. Replacing assignment stmt alone with some hardcoding solved the problem temporarily.
I assume it is because of mixing <string> and <string.h> functionality in one project. Maybe a mix of <iostream> and <iostream.h> is involved either.
Generally, you need to get rid of *all* deprecated headers before C++ standard.
If you were using STL objects in the interface to dlls (using same headers) you *must* use the same compiler and the same threading model and alignment for both the dll and the calling executable.
I assume it is because of mixing <string> and <string.h> functionality in one project. Maybe a mix of <iostream> and <iostream.h> is involved either.
Generally, you need to get rid of *all* deprecated headers before C++ standard.
If you were using STL objects in the interface to dlls (using same headers) you *must* use the same compiler and the same threading model and alignment for both the dll and the calling executable.
>> You should remove all includes of <string.h> and only use <cstring>
That's not likely to cause this problem though, as the only real difference between the two headers is that the second defines all symbols in the std namespace only, and not in the global namespace, while the first defines the symbols in both the global and std namespace (at least if the compiler conforms to the standard).
That's not likely to cause this problem though, as the only real difference between the two headers is that the second defines all symbols in the std namespace only, and not in the global namespace, while the first defines the symbols in both the global and std namespace (at least if the compiler conforms to the standard).
>>>> That's not likely to cause this problem though
Not directly but indirectly. If using mixed headers defining the same symbols or structures the calling function may have different offsets than the called function, hence the structures were corrupted. In case of MFC and PCH (precompiled headers) the issue may be worse as the PCH was replaced in each object no matter what include statements actually were defined in a source.
But I agree that some other reasons, e. g. the usage of pointers which already were deleted or were not initialized, may give a better reason for hanging in a string assignment.
Not directly but indirectly. If using mixed headers defining the same symbols or structures the calling function may have different offsets than the called function, hence the structures were corrupted. In case of MFC and PCH (precompiled headers) the issue may be worse as the PCH was replaced in each object no matter what include statements actually were defined in a source.
But I agree that some other reasons, e. g. the usage of pointers which already were deleted or were not initialized, may give a better reason for hanging in a string assignment.
evilrix🇬🇧
>> But I agree that some other reasons
Me too. I'd suggest seeing the offending code would be useful.
Me too. I'd suggest seeing the offending code would be useful.
lakshmikarle
ASKER
Hello,
I am using workshop as debuggind tool and till the point where problem arises there are no access errors or memory leak. But mixed headers, though I feel there is no such problem. I wil recheck the same.
I am using workshop as debuggind tool and till the point where problem arises there are no access errors or memory leak. But mixed headers, though I feel there is no such problem. I wil recheck the same.
- is there any own dll involved? If yes, does it have the same threading model (single-threaded or multithreaded), same alignment, same compiler version?
- is there usage of any foreign dll? Is it a C or a C++ interface? Is STL used in the interface?
- is there usage of any foreign dll? Is it a C or a C++ interface? Is STL used in the interface?