C++/cookies involving mapping
Expert: Ralph McArdell - 5/20/2005
QuestionThis is actually my first time using map.
is there a way that I can pass the map values through a function?
I think it's possible using classes, but I haven't used any classes throughout my whole program, and thus don't want to use classes and cause more confusion.
this module will be used to save a login session.
if you login once, AdminCookie=1 will be set, and you won't have to login everytime you click a link.
#include <string>
#include <iostream>
#include <map>
using namespace std;
int LoadCookie();
int ViewCookie();
int LoadCookie(){
map<string, string>::const_iterator loop;
map<string, string> cookieData;
//load the cookies
char *tmpCookie = "";
string cookie, cookieName, cookieValue;
if (getenv("HTTP_COOKIE") != NULL)
tmpCookie = getenv("HTTP_COOKIE");
else
tmpCookie = "AdminCookie=1";
string cookies = tmpCookie;
cookies += ";";
while (cookies.length() > 1)
{
cookie = cookies.substr(0, cookies.find(';'));
if (cookie.substr(0,1) == " ")
cookie = cookie.substr(1, cookie.length());
cookieName = cookie.substr(0, cookie.find('='));
cookieValue = cookie.substr(cookie.find('=')+1, cookie.length());
cookieData[cookieName] = cookieValue;
cookies = cookies.substr(cookies.find(';')+1, cookies.length());
}
//end of load cookies
//write cookies
{
cout << "Content-type: text/html\n";
for (loop = cookieData.begin(); loop != cookieData.end(); loop++)
{
cout << "Set-Cookie:" << loop->first << "=" << loop->second << ";\n";
}
}
//end of write cookies
cout << "\n<HTML>";
cout << "\n<HEAD>";
cout << "\n<TITLE>" << "Dougless" << "</TITLE>";
cout << "</HEAD>";
cout << "\n<BODY>";
return 0;
}
int ViewCookie(map<string, string>::const_iterator loop, map<string, string> cookieData){
//view cookies
string inpName, inpValue;
cout << "\n<H1>Cookies:</H1>";
cout << "\n<UL>";
for (loop = cookieData.begin(); loop != cookieData.end(); loop++)
{
cout << "<LI>" << loop->first << " is " << loop->second;
}
cout << "\n</UL>";
//end of view cookies
return 0;
}
int main(){
map<string, string>::const_iterator loop;
map <string, string> cookieData;
LoadCookie(loop, cookieData);
cout << "<LI>" << loop->first << " is " << loop->second;
cout << cookieData->first << cookieData->second;
cout << "</BODY>";
cout << "</HTML>";
return 0;
}
thank you much again.
I'll promise you once I'm done with my software, I'm going to mail you something as a gift.
AnswerFirst, you cannot mail me as you do not know my address - at least I hope you do not, and if you do then I would treat it as suspicious!
Second, you _are_ using classes even if you had not thought about it, which is as it should be - std::string, std::map<std::string, std::string>, std::ostream (for std::cout) etc...
The first thing that occurs to me is what are you asking when you say “pass the map values through a function”? Now std::map types store their data as std::pair<keyType, valueType> - which is where you get the first and second members from :
cout << "<LI>" << loop->first << " is " << loop->second;
as these are the members of a std::pair.
Now I shall assume that this is what you mean. If you mean just the values – those that are associated with the key – and not both the key and value pair -then you just obtain a key,value pair from the map and pass the second member of the pair – as you do above when using an iterator, dereferencing it, and access the second member of the pair (operator ->) to be printed to std::cout. Effectively you are passing the pair's second member to the operator<<() function for std::string (the type of values in your map) when you say:
<< loop->second;
it is equivalent to:
operator<<( std::cout, loop->second );
which calls the function:
std::ostream operator<<( std::ostream & output, std::string const & value )
So for your functions all you need to do is take in a std::string – preferably by constant reference - as the value parameter and pass in the second member for the map pair item in question when you wish to call it.
Assuming you wish to pass around the whole key,value pair item: all C++ standard library containers have a typedef (type alias) for their value type - the type they contain. For std::map types it is the type of the pair that stores the map type's key and value data. This type alias is called value_type.
So for your map type (std::map<std::string, std::string> ) you need to pass std::map<std::string, std::string>::value_type into / out of a function.
Now you might think that all this is getting very long winded, and you are correct. In fact I would have thought you would have got tired of typing map<string, string> everywhere - even without the std qualifications. So most of us define type aliases for the specific types we use from the standard library - in your case maybe something like CookieMapType would be a good name - it may or may not be shorter but it is all letter characters and also describes what you wish to use the map for better. To do this you place a typedef in an appropriate place in the code - maybe near the top:
#include <map>
using namespace std;
typedef std::map<std::string, std::string> CookieMapType;
int LoadCookie();
// ...
The you can say things like:
CookieMapType::const_iterator loop;
(BTW loop is a silly name here. The iterator is _not_ a loop so do not call it such. An iterator represents a position in the collection - so maybe position or iterator or some such would be better.)
and
int ViewCookie( CookieMapType::const_iterator loop
, CookieMapType cookieData
)
(BTW: Why are you passing in an iterator to this function? It is not needed as you always start at the beginning of the collection. So define it locally in the function:
int ViewCookie( CookieMapType cookieData )
{
// ...
CookieMapType::const_iterator itemPosition;
for ( itemPosition=cookieData.begin(); // ...
}
Also, why are you returning an int which is always just 0 from ViewCookie? I would have thought that such a value was useless and that void would be an appropriate type (i.e. no returned value) in this case:
void ViewCookie( CookieMapType cookieData )
)
Anyhow, you can pass around (key, value) pair values from a map by using the map's value_type:
void ViewSingleCookie( CookieMapType::value_type aCookieInfo )
{
std::cout << "Cookie Name: " << aCookieInfo.first
<< "; Cookie Value: " << aCookieInfo.second
<< "\n"
;
}
Obviously you can modify this function to your own formatting requirements.
Note also that if you change the typedef defining CookieMapType then so long as the new type defines a value_type and uses std::pairs of key and value types that have insertion operators defined (operator<<) to work with std::ostream (i.e. std::cout) then the function will still work after rebuilding. For example, if cookies were numbers instead of names and the map key type were an integer rather than a string:
typedef std::map<int, std::string> CookieMapType;
Then the above would still compile, as would a lot of the rest of the code if it used the typedef name for your cookie map type rather than the explicit map type definition all the time - however it would display something like:
Cookie Name: 21; Cookie Value: blah, blah, blah
which may require updating.