Solved

Defining static CMap<> member variable

Posted on 1998-12-30
13
1,324 Views
Last Modified: 2013-11-20
In class CElements in CElements.hpp I declare a static variable (just to test some it before I use a more complicated CMap<CString, CString &, CString, CString &>):
static CMap<int, int, int, int> m_mapTestToTest;

In CElements.cpp I need to define it, so I do this:
CMap<int, int, int, int> CElements::m_mapTestToTest = 0;

I got a dialog box saying "Debug Assertion Failed!"
I went to break mode and ran it again and got a Divide by Zero error.

I get these errors in my debug window:
ASSERT_VALID fails with illegal vtable pointer.
First-chance exception in Chemistry.exe: 0xC0000094: Integer Divide by Zero.

My Call stack:
CMap<int,int,int,int>::operator[](int 1) line 1327
CMap<int,int,int,int>::SetAt(int 1, int 3) line 1159 + 24 bytes
CElements::Create() line 39
CChemistryApp::CChemistryApp() line 46
...

All I did in Create() was to add a pair of ints (1 and 3) to CElements::m_mapTestToTest. Like this:
CElements::m_mapTestToTest.SetAt(1,3);

At CMap<int,int,int,int>::SetAt(int 1, int 3) line 1159 + 24 bytes:
      { (*this)[key] = newValue; }

At CMap<int,int,int,int>::operator[](int 1) line 1331 + 16 bytes:
      if ((pAssoc = GetAssocAt(key, nHash)) == NULL)

I only have this problem when m_mapTestToTest is declared as a static member variable. When I declare it locally, it words fine.

I'm pretty sure my problem is that I didn't define CElements::m_mapTestToTest correctly. How do I define it?

I tried defining it like this:
CMap<int, int, int, int> CElements::m_mapTestToTest;

That brought with it the same errors as before.

I could try not defining it at all. But that just produces a different error:
CElements.obj : error LNK2001: unresolved external symbol "private: static class CMap<int,int,int,int>  CElements::m_mapTestToTest"(?m_mapTestToTest@CElements@@0V?$CMap@HHHH@@A)

Usually when you define a static member variable, you just initialize it to 0, don't you? But since this isn't a primitive data type, I can't just set it to 0. Should I initialize it to some empty value? How?
CMap<int, int, int, int> CElements::m_mapTestToTest = CMap(); (class template name expecting parameter list)
CMap<int, int, int, int> CElements::m_mapTestToTest = CMap<int, int, int, int>(); (cannot convert parameter 1 from 'class CMap<int,int,int,int>' to 'int')
CMap<int, int, int, int> CElements::m_mapTestToTest = int(); (it wanted an int? well, I got the same "Debug Assertion Failed" message)

What I really want to do I populate CElements::m_mapTestToTest with pairs of ints, but how can you do that without using SetAt()? And how can you use SetAt() when the object isn't defined yet?
0
Comment
Question by:TylerRick
  • 10
  • 2
13 Comments
 
LVL 8

Expert Comment

by:Answers2000
ID: 1327025
Re: Defining Static, don't have =0, i.e. do

CMap<int, int, int, int> CElements::m_mapTestToTest ;

You other problems stem from the fact you are trying to fill in the map before it's created.  You can't do SetAt when the maps not created.  If you read MFC sources, you will see SetAt needs to have the map created (requires a this pointer etc).  

The best way round this to stick a wrapper class around the map, say

class CSomething
{
private:
  CMap< int, int, int, int > m_map ;

public:
   CSomething()
   {
     m_map.SetAt( 1, 3 ) ;
   }

   CMap< int, int, int, int >& GetMap() { return m_map ; }
} ;


Now if you want to make the map only exist once, make CSomething into a singleton, e.g.

class CSomething
{
private:
  CMap< int, int, int, int > m_map ;
  static CSomething *  m_pThis ;

   CSomething()
   {
     m_map.SetAt( 1, 3 ) ;
   }

   
public:
   CMap< int, int, int, int >& GetMap() { return m_map ; }

   ~CSomething()
  {
  }

  static CSomething& Get()
  {
    if ( m_pThis == NULL ) m_pThis = new CSomething ;
    return *m_pThis ;
  }
} ;

in a .cpp stick
CSomething * CSomething::m_pThis = NULL ;


You code then access the map using code like

CSomething::Get().GetMap().SetAt( ...etc... )

Whenever the map is first used, it's created (and initialized) by the wrapper class
0
 

Author Comment

by:TylerRick
ID: 1327026
>You other problems stem from the fact you are trying to fill in >the map before it's created.  You can't do SetAt when the maps >not created.  If you read MFC sources, you will see SetAt needs >to have the map created (requires a this pointer etc).  
When is the map actually created? I put it at the top of the .cpp file,
   CMap<int, int, int, int> CElements::m_map;
before the CElements::Create() function. So shouldn't it be the first thing that happens in the file? I thought a definition in a .cpp file that is outside of any function would be interpreted before any code in a function.

Or is it defined? Is it just declared, like
   int i;
only declares i, telling how much memory will be allocated, and
   int i = 2;
actually initializes and defines it, allocating the memory for it?

If I really can't define my member variable before I use my member function, then I guess I'll have to use your wrapper class idea. It looks like it would work well, but I just wonder if all that work is necessary.

How should my wrapper class relate with my CElements class? Would I just replace the plain CMap<int, int, int, int> with CSomething, like this:
class CElements
{
.
private:
   static CSomething m_map;
.
};
?
0
 

Author Comment

by:TylerRick
ID: 1327027
I tried your idea and did this:

// .hpp:

class CMapTest
{

private:
  CMap< int, int, int, int > m_map ;

public:
   CMapTest()
   {
     m_map.SetAt( 1, 3 ) ;
   }

   CMap< int, int, int, int >& GetMap() { return m_map ; }
};

class CElements
{
.
private:
      static CMapTest m_map;
.
};

// .cpp

CMapTest CElements::m_map;

void CElements::Create()
{
      ASSERT(CElements::m_map.GetMap()[1] == 3);
}

//

But I get the same old run-time errors:
   ASSERT_VALID fails with illegal vtable pointer.
   First-chance exception in Chemistry.exe: 0xC0000094: Integer Divide by Zero.

I tried this:

// .hpp:

class CMapTest
{

private:
  CMap< int, int, int, int > m_map ;
  static CMapTest*  m_pThis ;

   CMapTest()
   {
     m_map.SetAt( 1, 3 ) ;
   }

   
public:
   CMap< int, int, int, int >& GetMap() { return m_map ; }

   ~CMapTest()
  {
  }

  static CMapTest& Get()
  {
    if ( m_pThis == NULL ) m_pThis = new CMapTest;
    return *m_pThis ;
  }

};

class CElements
{
.
private:
      static CMapTest m_map;
.
};

//

And got this error:
error C2248: 'CMapTest::CMapTest' : cannot access private member declared in class 'CMapTest'
pointing to this line:
CMapTest CElements::m_map;

Do you know what I'm doing wrong?
0
 
LVL 8

Expert Comment

by:Answers2000
ID: 1327028
class CElements
{

private:
// remove static member

public:
CElements()
{
CMapTest.Get().GetMap.SetAt( ...etc... ) ;
}

void Create()
{
CMapTest.Get().GetMap.SetAt( ...etc... ) ;
}

};
0
 

Author Comment

by:TylerRick
ID: 1327029
Are you going to answer this question?

>Your other problems stem from the fact you are trying to fill in
>the map before it's created.  You can't do SetAt when the map's
>not created.  If you read MFC sources, you will see SetAt needs
>to have the map created (requires a this pointer etc).    

When is the map actually created? I put it at the top of the .cpp file,
   CMap<int, int, int, int> CElements::m_map;
before the CElements::Create() function. So shouldn't it be the first thing that happens in the file? I thought a definition in a .cpp file that is outside of any function would be interpreted before any code in a function.

Or is it defined? Is it just declared, like
   int i;
only declares i, telling how much memory will be allocated, and
   int i = 2;
actually initializes and defines it, allocating the memory for it?

0
 

Author Comment

by:TylerRick
ID: 1327030
In your last comment, you said "remove static member". You mean the static CMapTest m_map from CElements? How would I access it then?

I wasn't planning on accessing my map directly by the CMapTest class. Like
      CMapTest::Get().GetMap().SetAt( ...etc... )

I want to be required to access it through CElements. It would be good if I could do
      CElements::m_map.GetMap().SetAt(2, 4);
      CElements::m_map.GetMap()[1]
In that case, m_map needs to be static.

It looks like you're saying to populate the map in the CElements() constructor or CElements::Create(). That's good.

But how do I access the data? If m_map is not a static member, then are you saying you think I should use public member functions in CElements to access the map?

0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 

Author Comment

by:TylerRick
ID: 1327031
Here's an other problem: I'm going to want more maps than just the m_map.

I'll want maps like this:
   static CMap<CString, CString &, CString, CString &>
   static CMap<CString, CString &, float, float>
   static CMap<CString, CString &, signed int, signed int>

That means I need to either make a map wrapper class that can be used for all of the variables or make a different wrapper class for each one.

I think I'd like to re-use the same map class for all of them. But the problem is I made CMapTest into a "singleton", so it can only exist once. That doesn't make it very reusable.

I could go back to the first wrapper class you gave me (the simple non-singleton one), if only I could get it to work. I'm not having success.

Are you sure m_map gets defined before I use it even with a wrapper class? Because when I follow the instructions you gave me, I still get the error:
   ASSERT_VALID fails with illegal vtable pointer.

0
 

Author Comment

by:TylerRick
ID: 1327032
Adjusted points to 90
0
 
LVL 1

Accepted Solution

by:
hbuehler earned 90 total points
ID: 1327033
Hi TylerRick.

"When is the map actually created?"

C++ doesn't define any ordering in which static objects will
be created.
VC5 doc says:
1) First fill in simple data types
2) Don't rely on any ordering in the initialization phase.

To your original question:

I suppose your problem is that both the map and your own class
"CElement" will be created during the program initialization phase (i.e. your CElement objects are static members or global
objects, too).
In that case you'd get the problems described above as you can't gurantee that the map is been set up before your first CElement is been.
In that case, see the priority ordering above: You can be sure that a _pointer_ is set to Zero before any of your classes is been initialized.
Therefore I suggest the following definition:

// header
class CElement
{
  static CMap<int,int,int,int> *M_pMapInt;
  static int M_iMapInt;

public:
  CElement()
  {
    // create map if not exists

    if(!M_pMap)
    {
      ASSERT(M_iMapInt == 0);
      M_pMap = new CMap<int,int,int,int>;
    }
    ++M_iMapInt;   // reference counter
    M_pMap->SetAt(1,3);
  }

  virtual ~CElement
  {
    // delete map if left unused

    if(--M_iMapInt == 0)
    {
      ASSERT(M_pMapInt != NULL);
      delete M_pMapInt;
      M_pMapInt = NULL;
    }
  }
};

// impl.
CMap<int,int,int,int> *CElement::M_pMapInt = NULL;
int CElement::M_iMapInt = 0;

Now, the initialization routine first initializes the
_pointer_ M_pMapInt with zero, sets M_iMapInt to zero
and then passes through your constructors.
Since the first call to your constructor will allocate
the map you can be sure that the map is available.
You need the reference counter to free the CMap later.
[Please note that the upper solution is not thread-safe]
Of course it would be much nicer to make your own new
map class (derived from CMap) which has a private descructor
and a "delete" operator that checks for the reference counter
internally.
However, I think that the basic idea should be clear.

- Hans

0
 

Author Comment

by:TylerRick
ID: 1327034
It looks like your answer will be helpful. I just can't try it out now because the computer with my project on it is inaccessible right now. As soon as I try it, I'll reply...
0
 

Author Comment

by:TylerRick
ID: 1327035
Hans,

I'm glad you showed me how I can get rid of the problem of my map not being initialized soon enough by initializing a pointer to it to 0.

The only problem was, there are 2 non-static functions, the constructor and destructor. This may sound strange, but I'm not going to instantiate it any where, so I need it to have only static members. The way you suggested, there must be an object of type CElements in order to use the static member m_pMap, but I want it static so I could use it without any objects. The data is initialized through the constructor, but I don't plan to call the constructor.

Any way, you gave me the basic idea I needed. I modified it by putting what you had in the constructor in a static Create() and what you had in the destructor in a static Destroy(). Now I just call Create() when the application starts and Destroy() when it ends, and I don't need an object of CElements. It works!


0
 

Author Comment

by:TylerRick
ID: 1327036
I think my question is answered now.

Since both of you answered part of my question, I'll split the points, okay? Answers2000, you can go to http://www.experts-exchange.com/secure/bin/Q.10112880 .

Thanks for your help!

0
 

Author Comment

by:TylerRick
ID: 1327037
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
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. …
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.
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

758 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

18 Experts available now in Live!

Get 1:1 Help Now