C++ STL map exception. (root of nonmutable tree)

2

I have problem with my parameters class. When I try invoke getProperty in any other form except main, I get exception "access violation at 0x00459bd6: read of address 0x00000030" in xtree on this code

_Nodeptr& _Root() const
    {   // return root of nonmutable tree
    return (_Parent(_Myhead));
    }

Class initialized in main form by this code

 parameters = Parameters::getInstance();

Parameters class code:

template <typename T>
class Singleton
{
public:
    virtual ~Singleton () {};
    static T* getInstance ()
    {
        if (!singletonInstance)
        {
            singletonInstance = new T ();
        }
        return singletonInstance;
    }

protected:
    Singleton () {};
    static T* singletonInstance;
};

template <typename T>
T* Singleton<T>::singletonInstance;

class Parameter
{
public:
    string value;
    string maxValue;
    string minValue;
    string type;

    Parameter();
    Parameter(string val);

    Parameter& Parameter::operator=(Parameter& p);
};

typedef map<string, Parameter> InnerMap;
typedef map<string, InnerMap> MainMap;

class Parameters : public Singleton <Parameters>
{
private:
    MainMap params;

    void loadParametersList(string filename);

    Parameters() : Singleton<Parameters>()
    {
        loadParametersList("parameters.lst");
    }

protected:
    friend class Singleton<Parameters>;

public:
    string getProperty (string category, string name);

    void loadParametersFromIni(string filepath);
    void saveParametersToIni(string filepath);
};

Parameters *parameters;

UPD.1 Last of my function on stacktrace is getProperty. And this is code of getProperty:

template <>
std::string Parameters::getProperty (string category, string name)
{
    return params[category][name].value;
}

This code works fine in main form, but calls exception in any others form/

If you have a little time, I can provide a small program that illustrates the problem on e-mail.

c++
map
stl
c++builder
asked on Stack Overflow Feb 10, 2014 by kender11 • edited Feb 10, 2014 by kender11

1 Answer

1

It looks like declaring the globally used parameters pointer in the Parameters class header file is causing a scope problem. Think of it like this, every time the header file containing that last line is brought in with #include, another pointer instance is declared and created. Declaring the pointer instance of parameters at the bottom of the Parameters header file will do this every time. However many times Parameters.h is brought in with

#include "Parameters.h" 

another independent parameters pointer is not set to any meaningful memory, but instead points to a totally undefined garbage memory address. A better way to have a single global parameters pointer for every object's use within the application is to declare the pointer once in the respective main.cpp (maybe MainFm.cpp if you like it better there). Then the declaration of the parameters pointer at the bottom of the Parameters header is now:

extern Parameters *parameters;

The single line declaring the pointer itself is located in only one cpp file, preferably whichever cpp file the main function resides. In C++Builder, the cpp that contains the Application->Initialize(), Application->CreateForm(...), Application->Run() calls is a good candidate. Alternatively, you could also declare the global parameters pointer in the main application form cpp file, prior to any class methods, and immediately following the header file includes for your project.

Here are two different coding snippet examples to help understand resolving the scoping problem. Both examples use the slight modification for the Parameters header file at the very last line. Note that only one example should be selected for implementation use. Implementing both approaches simultaneously should result in a confusing error message from the compiler that two parameters pointers are declared (it may be the linker that catches it).

The very last Parameters header file line should become:

extern Parameters *parameters; // might want to change this to pgParameters

// 
// pgParameters means global pointer to a Parameters instance
// 

Example 1 - Project.cpp declaration

// 
// the cpp where Application->Initialize() is called
// Below the C++Builder IDE controlled comment line, immediately below 
// the last USEFORM macro call, have the #include and pointer instance declaration.  
//
#include "Parameters.h"  // the header file where the Parameters class is defined
Parameters *parameters;  // declare the single global pointer for all objects' use 

extern "C" int FormMain()
{
   //
   // the rest of the C++Builder IDE controlled content; leave as is
   //   
}

Example 2 - MainForm.cpp declaration

// 
// the cpp where the main form constructor body, event handlers, user defined 
// functions, etc. are written.
// Below the C++Builder IDE controlled declaration of the global pointer for 
// the main form, where the default declaration is: 
// 
// TForm1 *Form1;  
//
// code the #include and pointer instance declaration.
//
//------------------------------------------------------------------------
TForm1 *Form1;
#include "Parameters.h"  // the header file where Parameters class is defined
Parameters *parameters;  // declare the single global pointer for all objects' use 

//
// rest of the already existing code...
//

Note that both examples are intentionally grouping the Parameters related items.
The #include bringing in the Parameters header can be grouped with the other header file #include lines as desired. It all depends on the team coding standards.

answered on Stack Overflow Feb 17, 2014 by CPlusPlus OOA and D

User contributions licensed under CC BY-SA 3.0