Solved

CMap problems: cannot convert to 'unsigned long'

Posted on 1998-11-25
15
723 Views
Last Modified: 2013-11-20
I've never used CMap before and I'm having trouble getting it to work.

I have
 CMap<CString, CString &, CString, CString &> mapElementNameToSymbol;
. It causes
 error C2440: 'abstract declarator' : cannot convert from 'class CString' to 'unsigned long'
. I tried to track that down. It was in D:\VC++\MFC\INCLUDE\afxtempl.h(128):
 inline UINT AFXAPI HashKey(ARG_KEY key)
 ...
  return ((UINT)(void*)(DWORD)key) >> 4;
Why does it need to convert to unsigned long?

Am I using the right types for my template arguments? I want to map chemical element names (which would be strings) to element symbols (which also would be strings). For example, "oxygen" to "O".
0
Comment
Question by:TylerRick
  • 9
  • 5
15 Comments
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1325306
I think your declaration should be:

 CMap<CString, CString *, CString, CString &> mapElementNameToSymbol;

Cheers,

Raymond.

0
 

Author Comment

by:TylerRick
ID: 1325307
When I tried that, it gave me
 
 D:\VC++\MFC\INCLUDE\afxtempl.h(1448) : error C2664: 'SetAt' : cannot convert parameter 1 from 'class CString' to 'class CString *'

 At afxtempl.h(1448), it is

 SetAt(newKey, newValue);

where

 KEY newKey;
 VALUE newValue;

KEY and VALUE are CString so I don't know why it wants to convert to class CString *.

So that doesn't seem to work.
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1325308
1. rwilson is NOT correct, you can happily declare CMap's as you do.  

2. However you must overload the HashKey function.  The idea of this function is that it should return as unique as possible a UINT for each member of the map (doesn't have to be totally unique - but map will be faster with a better hashing algorithm)

3. To fix your program, add this to a header before it meets any CMaps (stdafx.h is a good place for most MFC project's) :-

UINT AFXAPI HashKey( CString& key ) ;

Then add this to any one of your .cpp files (change the algorithm if you like) :-


UINT AFXAPI HashKey( CString& key )
{
      int ll = key.GetLength() ;

      if ( ll == 0 ) return 0 ;
      UINT ui = key[0] ;

      if ( ll > 1 )
      {
            for ( int ii = 1 ; ii < ll ; ii++ )
            {
                  ui ^= ( key[ii] << ((ii+key[ii-1]) % 24) ) ;
            } // for
      } // if

      return ui ;
}

4.  Then a map such as this will work :-

CMap< CString, CString&, CString, CString&> mapNameToSymbol ;

mapNameToSymbol.SetAt( "O", "Oxygen" ) ;

etc..

CString strKey = "O" ;
CString strValue ;
BOOL bLookup = mapNameToSymbol.Lookup( strKey, strValue ) ;

if ( bLookup )
{
CString strTemp ;
strTemp.Format( "Symbol %s is element %s", (LPCTSTR)strKey, (LPCTSTR)strValue ) ;
AfxMessageBox( strTemp ) ;
}



5. If you prefer this answer to rwilson's then you should reject his, and ask me to post a dummy answer.  If you're still stuck then you should add more comments, explaining what additional information/questions you need/have.
0
 
LVL 8

Accepted Solution

by:
Answers2000 earned 100 total points
ID: 1325309
Ooops I was typing my comment, when you were reviewing rwilson's attempt.  We therefore submitted at the same time...

Please review the answer I gave in the comment date 3:05PM
0
 

Author Comment

by:TylerRick
ID: 1325310
Your answer was very helpful! But I have several questions. Could you explain further?

Most importantly, where should I put these things? I'm confused about the best place for things. I tried putting this

 CMap<CString, CString &, CString, CString &> mapElementNameToSymbol;

and the overloaded HashKey( CString& key ) definition in Chemistry.cpp (the source file for the application). That works. But does it make sense? I thought you were supposed to put variable declarations (mapElementNameToSymbol) in the header file. But when I did that in chemistry.h it had a bunch of errors about it being already declared. Do you know why I can't put it there? Where do you put stuff like that? It has global scope (I think), so I don't want it associated with any of my classes, but I have to choose some file. I thought the application source files made the most sense since I want these things to be global to the entire application.

With it in Chemistry.cpp, I tried using it in a function in ChemistryView.cpp. But it said

 error C2065: 'mapElementNameToSymbol' : undeclared identifier

 So now what do I do? I already have #include "Chemistry.h" in ChemistryView.cpp. But since I defined the variable in Chemistry.cpp and not Chemistry.h, it can't find it.

Where should I put

 mapNameToSymbol.SetAt( "O", "Oxygen" ) ;

(the part that initializes my CMap)? I don't want it in any of the functions that need that information (then I'd have to duplicate it in each one). I want it initialized when the application starts, so would my guess is that the CChemistryApp constructor is the most logical place.

I put the declaration

 UINT AFXAPI HashKey( CString& key ) ;

in stdafx.h like you suggested. But I didn't know that was an acceptable place to put declarations for functions. All I've seen there is inclusions (like #include <afxwin.h>).

I'm wondering, where did you come up with the algorithm? Do MFC's creators expect anyone who uses a CMap with CStrings to be able to think up that? I probably don't have to understand it to use it, do I? I'll just trust you that it does what it needs to.

That's more than I asked in the original question and I apologize for all these new questions. I can increase the points if you'd like...
0
 

Author Comment

by:TylerRick
ID: 1325311
Hi Answers2000! Are you going to answer my additional questions? If not, I'd be happy to give you the points that you deserve.
0
 

Author Comment

by:TylerRick
ID: 1325312
I'm waiting for a response...
0
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 8

Expert Comment

by:Answers2000
ID: 1325313
Sorry I didn't see you had a follow up question...
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1325314
Basically HashKey prototype must be declared before you declare any variables of the map type.  The reason is that when you declare you CMap it expands the CMap template code.

The CMap template code assumes the presence of the HashKey function for the type you are storing in the template.  MFC includes a default HashKey function which processes ints, etc.  For complex data types (e.g. CString) yes you do need to define the hashing function yourself.  [you can also define HashKey function for standard types, as the standard version is a template itself, and any type specific version you declare will take priority].

The reason is that a good hashing code is one that generates as unique a possible a value for the type of data you are likely to be storing.  MFC's designers can't predict that for complex data types, so they left the decision to you.

If you generate a "better" hashing algorithm (less likely identical values are generated by different strings) then the map will perform better.

As for where you declare your HashKey function, stdafx.h is as good a place as any.  Basically you should stick stuff that rarely changes in your stdafx.h (because this becomes part of the pre-compiled header).  The other reason I suggested it is that it usually included before any classes/code of your own which is likely to declare maps etc.



0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1325315
As for stdafx.h #include'ing stuff this is irrelevant.  The C++ compiler treats the #include'd files just as if they were part of the file that included them.  So if your stdafx.h #include's <afxwin.h> it's just as if you typed in all the code in afxwin.h into your stdafx.h.  No magic there.
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1325316
Anyway the conclusion is the HashKey funciton prototype needs to go before any declarations of your map types.  So either stick it in stdafx.h are stick in it a separate header, which you #include before any other headers, example:

stick HashKey prototype is HashKey.h
stick HashKey function code in HashKey.cpp

HashKey.cpp also #include's stdafx.h then #include's HashKey.h

All other files, e.g. Chemistry.cpp #include this immediately after stdafx.h, e.g.

#include "stdafx.h"
#include "HashKey.h"
#include "Chemistry.h"

OK my next comment will go thru your specific pts to try and make sure I covered everything

0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1325317
>>         Most importantly, where should I put these things? I'm confused about the best place for things. I tried putting this ...etc.

I think I've explained this in my previous comments.  Just make sure the prototype is seen by the compiler before any maps/classes-containing-maps,  the prototype is :-

         UINT AFXAPI HashKey( CString& key ) ;

>>         Where should I put
>>
>>          mapNameToSymbol.SetAt( "O", "Oxygen" ) ;
>>        (the part that initializes my CMap)? I don't want it in any of the functions that need that information (then I'd have to duplicate it in each one). I want it initialized
>>         when the application starts, so would my guess is that the CChemistryApp constructor is the most logical place.

This is as good a place as any.  I would put it the constructor of the class which contains the map.


>>         I'm wondering, where did you come up with the algorithm?
I just hacked this one up, as it works reasonably well in most cases (similar strings don't produce similar values - which is what a typical list etc. contains) and is quick to calculate.  A good computer science book can explaining hashing theory much better than I can.  A good hash algorithm is :-
1. Easy/Quick to calculate
2. Doesn't produce identical values for the strings you use.  As I don't know what strings you store in your apps, I can't recommend a specific algorithm.


>>Do MFC's creators expect anyone who uses a CMap with CStrings to be able to think up that?
Yes, because they don't know what kind of data-values you might store in a map.  This applies for any complex data type.  Personally I think this is weakness of MFC, but that's the way it.  Read the help on HashKey and you'll see I'm right that they do expect you to do this work yourself.

>> I  probably don't have to understand it to use it, do I? I'll just trust you that it does what it needs to.

OK basically I go thru the string and flip bits based on each character in the string, to generate a 24 bit number.  There is no real magic, but what I did was go from L to R and flip a pattern of 8 bits a time (which bits are flipped depend on the previous character in the string).

The reason I picked this algorithm is that highly similar strings will not produce identical results.  Neither will the same string reversed.  


0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1325318
Oops: - 2nd last para:

         OK basically I go thru the string and flip bits based on each character in the string, to generate a **32** bit number.  There is no real magic, but what I did was go

0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1325319
And the other reasons are

- quick to calculate
- not much code
- uses the 32 bits in the UINT (a lot of simple hash values only use the lowest 8 or 16 bits and so are less "unique" for maps containing a lot of items).

Incidentally I used this algorithm in my program which stores 200,000 items in a map.  This was an optimization (the other main one is to init the hash table - see function in CMap).  I got the speed to insert 200,000+ items into a map down from 10mins to < 3 secs (333 MHz PC)
0
 

Author Comment

by:TylerRick
ID: 1325320
Thanks for all your help!
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Windows Drag & Drop Location 2 85
pre4 challenge 19 88
Line meaning 9 75
wordmultiple challenge 12 89
Introduction: Finishing the grid – keyboard support for arrow keys to manoeuvre, entering the numbers.  The PreTranslateMessage function is to be used to intercept and respond to keyboard events. Continuing from the fourth article about sudoku. …
Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

17 Experts available now in Live!

Get 1:1 Help Now