This is my first time making a hash table. I'm trying to associate strings (the keys) with pointers to objects (the data) of class Strain.
// Simulation.h
#include <ext/hash_map>
using namespace __gnu_cxx;
struct eqstr
{
bool operator()(const char * s1, const char * s2) const
{
return strcmp(s1, s2) == 0;
}
};
...
hash_map< const char *, Strain *, hash< const char * >, struct eqstr > liveStrainTable;
In the Simulation.cpp file, I attempt to initialize the table:
string MRCA;
for ( int b = 0; b < SEQ_LENGTH; b++ ) {
int randBase = rgen.uniform(0,NUM_BASES);
MRCA.push_back( BASES[ randBase ] );
}
Strain * firstStrainPtr;
firstStrainPtr = new Strain( idCtr, MRCA, NUM_STEPS );
liveStrainTable[ MRCA ]= firstStrainPtr;
I get an error message that reads "no match for ‘operator[]’ in ‘((Simulation*)this)->Simulation::liveStrainTable[MRCA]’." I've also tried using "liveStrainTable.insert(...)" in different ways, to no avail.
Would really love some help on this. I'm having a difficult time understanding the syntax appropriate for SGI hash_map, and the SGI reference barely clarifies anything for me. Thanks.
Try liveStrainTable[ MRCA.c_str() ]= firstStrainPtr;. It expects const char * as type of key value, but MRCA has type string.
Another way is to change liveStrainTable to:
hash_map< string, Strain *, hash<string>, eqstr > liveStrainTable;
Others answered your direct question, but may I suggest using unordered_map instead - it's coming with the next version of the STL and is supported by all major compilers.
hash_map is not part of STL. There's no implementation provided for hash, or in other words, the hash_map can't hash strings by default. You need your own hash function. T
Try:
typedef struct {
size_t operator()( const string& str ) const {
return __gnu_cxx::__stl_hash_string( str.c_str() );
}
} strhash;
hash_map< string, Strain *, strhash, eqstr > liveStrainTable;
The hash_map is defined with const char * as the key type and you are using an std::string as the key when accessing. These are 2 different types, the template did not build an operator for the second type, so this is an error. Use std::string for the hashmap definition or use MRCA.c_str()
Right now, you have a type mis-match. You're passing MRCA (a string) where a char const * is expected. You can either use c_str() to get a char const * from the string, or (far better) change the definition of your hash table to take a string as its key type.
Related
I am converting a project written in C++ for windows. Everything is going fine (meaning I clearly see what needs to be changed to make things proper C++) until I hit this, which is my own little routine to find a keyword in along string of keyword=value pairs:
bool GetParameter(const char * haystack, const char *needle) {
char *search, *start;
int len;
len = strlen(needle) + 4; // make my own copy so I can upper case it...
search = (char *) calloc(1,len);
if (search == NULL) return false;
strcpy(search,needle);
strupr(search);
strcat(search,"="); // now it is 'KEYWORD='
start = strstr(haystack,search); <---- ERROR from compiler
g++ is telling me "Invalid conversion from const char * to char * "
(the precise location of the complaint is the argument variable 'search' )
But it would appear that g++ is dyslexic. Because I am actually going the other way. I am passing in a char * to a const char *
(so the conversion is "from char * to const char *" )
The strstr prototype is char * strstr(const char *, const char *)
There is no danger here. Nothing in any const char * is being modified.
Why is it telling me this?
What can I do to fix it?
Thanks for any help.
The background to the problem is that C defines the function strstr as:
char* strstr(const char*, const char*);
This is because C doesn't allow overloaded functions, so to allow you to use strstr with both const and non-const strings it accepts const strings and returns non-const. This introduces a weakness in C's already fragile type-system, because it removes const-ness from a string. It is the C programmer's job to not attempt to write via a pointer returned from strstr if you pased in non-modifiable strings.
In C++ the function is replaced by a pair of overloaded functions, the standard says:
7. The function signature strstr(const char*, const char*) shall be replaced by the two declarations:
const char* strstr(const char* s1, const char* s2);
char* strstr( char* s1, const char* s2);
both of which shall have the same behavior as the original declaration.
This is type-safe, if you pass in a const string you get back a const string. Your code passes in a const string, so G++ is following the standard by returning a const string. You get what you asked for.
Your code compiles on Windows because apparently the standard library you were using on Windows doesn't provide the overloads and only provides the C version. That allows you to pass in const strings and get back a non-const string. G++ provides the C++ versions, as required by the standard. The error is telling you that you're trying to convert the const return value to a non-const char*. The solution is the assign the return value to a const char* instead, which is portable and compiles everywhere.
Error is not regarding the arguments to stsrtr. Compiler is complaining about the conversion of the 'const char *' returned by strstr. You can't assign it to *start which is just char *
You can try one of these:
const char *start;
or
string start(strstr(haystack,search));
Although declaring start as const char* might suffice, what seems more appropriate to me is to use std::string objects instead:
#include <string>
#include <cctype>
#include <algorithm>
bool GetParameter(const char * haystack, const char *needle) {
std::string hstr(haystack), nstr(needle);
std::transform(nstr.begin(), nstr.end(),nstr.begin(), ::toupper);
nstr += "=";
std::size_t found = hstr.find(nstr);
if (found != std::string::npos) {
... // "NEEDLE=" found
}
else {
...
}
...
}
The conversion it is complaining about is from strstr(...) to start. Change the declaration of start to const char* start;
you can use such like:
start = const_cast<char *>(strstr( haystack, static_cast<const char *>(search) ));
From what I have deduced, the std::map::find() method searches the map by comparising pointer address instead of values. Example:
std::string aa = "asd";
const char* a = aa.c_str();
const char* b = "asd";
// m_options is a std::map<const char*, int )
m_options.insert( std::make_pair( a, 0 ) );
if( m_options.find( b ) != m_options.end() ) {
// won't reach this place
}
I am kinda surprised (because I am using primitive types instead of some class) and I think that I have done something wrong, if not then how to force it to use value instead of address?
You are using char * as a key type for the map. For the pointer types, comparison is performed by their address (as the map cannot know that these pointers are NULL-terminated 8-bit strings).
To achieve your goal, you could create the map with custom compare function, e.g.:
bool MyStringCompare(const char *s1, const char *s2) {
return strcmp(s1, s2) < 0;
}
...
std::map<const char*, int, MyStringCompare> m_options;
Or consider using std::string as the key type.
Actually, map uses a strict ordering comparison operator to look for values, not the equality operator. Anyway, you can achieve this by passing a custom functor that compares the values of the strings, or do the right thing and use std::string instead.
I wrote the following but for some reason calling InstructionVal(b) is invalid.
intellisense is spitting out:
Only () is allowed for initializer member NPPInstructionDef::InstructionVal
here is the offending code:
//Single Instruction Definition for Instruction Dictionary
typedef struct NPPInstructionDef
{
const char* InstructionName;
const unsigned char* InstructionVal[];
NPPInstructionDef(const char* a, const unsigned char* b[]): InstructionName(a), InstructionVal()
{
}
}NPPInstruction;
Any Ideas? Thanks.
First, I'm assuming your initialization is InstructionVal(
b ), rather than the InstructionVal() which you've written.
But even then, what you've written shouldn't compile.
This is the usual problem, due to the fact that C style arrays
are broken, and shouldn't be used. Your definition:
unsigned char const* InstructionVal[];
defines an array of unknown length (thus, illegal in a class
defintion) of unsigned char*. There's no way to initialize
this in an initialization list, except by () (value
initialization).
What you want is:
std::vector <unsigned char*> InstructionVal;
, and the constructor should be:
NPPInstructionDef( std::string const& a,
std::vector <unsigned char> const& b );
, or perhaps more likely:
template <typedef Iterator>
NPPInstructionDef( std::string const& a,
Iterator begin,
Iterator end )
: InstructionName( a )
, InstructionDef( begin, end )
{
}
(This supposes, of course, that InstructionName is
std::string, instead of char const*. Which will avoid any
issues of lifetime of the string, for example, and allow easy comparison, etc.)
I have this code, CBString is just a string class I use for some processing
char * scrummyconfigure::dosub(strtype input)
{
CBString tstring;
tstring = input;
uint begin;
uint end;
begin = tstring.findchr('$');
end = tstring.findchr('}',begin);
CBString k = tstring.midstr(begin+2,end-2); // this is BASE
strtype vname = (strtype) ((const unsigned char*)k);
strtype bvar = (strtype) "BASE";
assert(strcmp(bvar,vname) == 0); // this never fails
// theconf is just a struct with the map subvars
// subvars is a map<const char *, const char *>
out(theconf->subvars[bvar]); // always comes up with the value
out(theconf->subvars[vname]); // always empty
uint size = end - begin;
tstring.remove(begin, size);
return (const char *)tstring; // it's OKAY! it's got an overload that does things correctly
//inline operator const char* () const { return (const char *)data; } <-- this is how it is declared in the header of the library
}
Why is it that the strcmp always says the strings are the same, but only the variable I declared as bvar returns anything?
I'm assuming strtype is defined in the following way:
typedef char * strtype
Your issue is that you're assuming that vname and bvar have the same value, where in reality, they have different values that each point to a block of memory that contains identical data.
std::map is dumbly comparing them with ==, and I bet you'd find that if you compared them with ==, you would get false, as expected. Why exactly arent you using the std::string class?
Edit: I rewrote your method to be less scary:
// implied using namespace std;
string ScrummyConfigure::removeVariableOrSomething(string tstring)
{
uint begin; // I'll assume uint is a typedef to unsigned int
uint end;
begin = tstring.find('$', 0);
end = tstring.find('}', begin);
string vname = tstring.substr(begin + 2, end - 2); // this is supposedly BASE
assert(vname == "BASE"); // this should be true if vname actually is BASE
out(this->conf->subvars[bvar]); // wherever theconf used to be, its now a member
out(this->conf->subvars[vname]); // of ScrummyConfigure and its been renamed to conf
uint size = end - begin;
tstring.erase(begin, size);
return tstring; // no casting necessary
}
//subvars is a map<const char *, const char *>
The key of this map isn't a string per-say, but a memory address. The corresponding check would be
assert( bvar == vname);
which will probably fail. You'll need to change the key type to a string class (either std::string or CBString to meaningfully use the map.
Just because the strings are the same doesn't mean that std::map will treat them as the same key. That depends on the Compare class that is used by the std::map, which defaults to less<KeyType> - which yields the same result as applying the less-than operator.
You can define a class that defines operator() to do a proper comparison on your strtypes and pass that as your third template argument when defining your std::map. Or, as suggested, use std::string as your strtype.
This is a bit weird, but here goes.
I have many hardcoded "tables" that I'm defining as arrays of std::strings or const char *.
So for example:
const char* resp_desc[] = {
"00=Approved",
"01=Declined",
"03=Incorrect User name",
// more values
NULL
};
In some functions these are passed as the table to lookup the description:
const char* lookup(const char* code, const char** table, const char*default="") {
// lookup code is here..
}
my question is, is it possible to call the lookup function without creating the resp_desc array?
The below code was my first attempt, but I get syntax errors around the {} when trying to use it:
const char* desc = lookup("00", {"00=Approved", "01-Invalid Record", NULL})
It doesn't work with the current C++03, but C++0x will allow to initialize i.e. std::vector with
std::vector<std::string>{"00=Approved", "01-Invalid Record"}
Edit: This works with g++ --std=c++0x (gcc --version is 4.4.3)
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
bool contains(string item, vector<string> const& table) {
return find(table.begin(), table.end(), item) != table.end();
}
int main() {
cout << (contains("foo", vector<string>{"foo", "bar"}) ? "found" : "not found") << "\n";
return 0;
}
If you are willing to change your look-up function, you could alternatively use utilities like Boost.Assign:
// alternative lookup function:
std::string lookup(const std::string& code,
const std::vector<std::string>& table,
const std::string& default="");
// example:
const std::string desc = lookup("00", boost::assign::list_of
("00=Approved")("01-Invalid Record"));
or maybe simply something like this:
typedef std::map<std::string,std::string> Table;
std::string lookup(const std::string& code,
const Table& table,
const std::string& default="")
{
Table::iterator it = table.find(code);
return (it != table.end()) ? it->second : default;
}
const std::string desc = lookup("00", boost::assign::map_list_of
("00","Approved")("01","Invalid Record"));
In short, no. C++ doesn't provide array or structure literals, only array or structure initializers. That is, the { yadda, yadda, yadda } syntax only means what you want when it occurs on the right side of sometype name[] =.
The answer is no.
In C++ (and also C), array types in function parameters are automatically converted to pointer types, as your signature for lookup shows:
const char* lookup(const char* code, const char** table, const char *default);
table is a const char ** and so needs a pointer value. To have a pointer, you need an object in memory to point at.
If you had a simple function such as:
void myfunc(int foo);
you can call myfunc(1) and that's fine. The constant expression 1 is a temporary value which doesn't have a location in memory, and myfunc receives the value directly.
If however you call your lookup function:
const char* desc = lookup("00", /* array constant */);
we can ask: what could /* array constant */ possibly be? lookup needs a pointer to an array object which exists somewhere in memory; but a constant expression doesn't have a location in memory (it is not an lvalue or object) and so there can be no pointer which refers to the constant array expression. As a result, such constant expressions do not exist.
(The one exception to this rule of "no constants which decay to pointers" is the string literal: "Hello World". A string literal creates an array in memory with static duration which exists for the lifetime of the program, and its value returned is a const char * pointing to that array. Sadly, the equivalent for array literals does not exist.)
No sir! The curly bracket syntax is for array initialization only. It does not represent a literal array.