-1

I'm recently programming a very simple logger class in Visual C++ 2010, but I have a problem. Everytime I run the program, a debug assertion failure appears.

Expression: _CrtIsValidHeapPointer(pUserData)

This is how my class looks like (basically it's only a little modified from an answer here C++ Singleton design pattern):

class Logger
{
public:
    // Returns static instance of Logger.
    static Logger& getInstance()
    {
        static Logger logger; // This is where the assertion raises.
        return logger;
    }

    void logError(std::string errorText);

    // Destructor.
    ~Logger();

private:
    std::ofstream logFileStream;

    // The constructor is private to prevent class instantiating.
    Logger();
    // The copy constructor and '=' operator need to be disabled.
    Logger(Logger const&) { };
    Logger& operator=(Logger other) { };
};

And the constructor is:

Logger::Logger()
    : logFileStream(Globals::logFileName, std::ios_base::trunc)
{
    // (Tries to open the file specified in Globals for (re)writing.)
}

I found out that I can solve it by using static variables or methods somehow, but I don't understand what's wrong with this code. Does anyone know, where the problem is?

EDIT: FYI, the failure raises when this code is called (for the first time):

Logger::getInstance().logError("hello");

EDIT 2: This is the definition of logFileName in Globals:

static const std::string logFileName = "errorLog.log";
Community
  • 1
  • 1
Miroslav Mares
  • 2,292
  • 3
  • 22
  • 27
  • Does `getInstance()` execute properly? Do you get to `logError()`? – Attila Jun 22 '12 at 10:03
  • Right when the instantiation should happen in the static method - I have added a comment to the code. – Miroslav Mares Jun 22 '12 at 10:05
  • The assertion seems to happen in the constructor. That's the code you're not showing... – Bo Persson Jun 22 '12 at 10:18
  • @BoPersson: The ctor is right there towards the end. – dirkgently Jun 22 '12 at 10:20
  • how is `Globals::logFileName` initialized? Does it have a proper value when the logger is instantiated? – Attila Jun 22 '12 at 10:21
  • @Attila I have appended the definition at the end of my question. – Miroslav Mares Jun 22 '12 at 10:27
  • 1
    That seems all right (as long as it is initialized before the Logger constructor) -- is the `getInstance()` called from a static block by any chance (e.g. when calculating the value of a static variable?) – Attila Jun 22 '12 at 10:33
  • I've compiled your code and it runs without a problem. Do you use DLLs or link several modules in your project? Could you also provide a stack trace to the assertion point. – Dmitry Egorenkov Jun 22 '12 at 10:33
  • 1
    There are some dependencies between the global variables (e.g. logger singleton and the `std::string logFileName`. Are you sure they are instantiated in the right order? (Remember, if each lives in different compilation unit, the order of construction is not defined in C++.) – mity Jun 22 '12 at 10:36
  • I have just inspected the content of all variables in Globals namespace and I see that none of the strings has any value! It might be a problem similar to this: http://stackoverflow.com/questions/1467919/static-const-string-wont-get-initialized I will have a look at it and post an answer if successful. – Miroslav Mares Jun 22 '12 at 10:42
  • So, the empty string wasn't the problem (however, it would be problem later). The wierd assertion failure still appears. – Miroslav Mares Jun 22 '12 at 11:49

2 Answers2

1

My guess is that you're calling getInstance() from the constructor of another global variable, and encountering the infamous initialisation order fiasco - it's unspecified whether or not Globals::logFileName has been initialised before any globals in other translation units.

One fix is to use an old-school C string, which will be statically initialised before any global constructor is called:

static const char * logFileName = "errorLog.log";

Another possibility is to access it via a function:

static std::string logFileName() {return "errorLog.log";}

My favoured solution would be to remove the global instance altogether, and pass a reference to whatever needs it; but some might find that rather tedious, especially if you already have a large amount of code that uses the global.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • It's called from a constructor, but the constructor's object is created at the beginning of the main() function, so it's not global. Thanks for the tip, but it haven't solved the problem. – Miroslav Mares Jun 22 '12 at 12:46
0

C++/CLI is not standard C++ and plays by slightly different rules. Are you using C++/CLI managed code at all? (/clr compiler option?) This looks like a common problem when mixing C++ (unmanaged) code with C++/CLI (managed) code. It has to do with the way managed and unmanaged construction and destruction happens at program initialization and program exit. Removing the destructor works for me - can you do that in your Logger class?

For more details and possible workarounds:

http://www.codeproject.com/Articles/442784/Best-gotchas-of-Cplusplus-CLI

http://social.msdn.microsoft.com/Forums/vstudio/en-US/fa0e9340-619a-4b07-a86b-894358d415f6/crtisvalidheappointer-fails-on-globally-created-object-within-a-static-llibrary?forum=vcgeneral

http://forums.codeguru.com/showthread.php?534537-Memory-leaks-when-mixing-managed-amp-native-code&p=2105565

Brian
  • 73
  • 1
  • 5