This singleton keeps crashing.

Started by
19 comments, last by Ectara 9 years, 8 months ago

<stdint.h> is part of the C standard library, not the C++ standard library. While most compilers allow you to include their C includes from C++, they are not required to do so (nor do they even have to have C includes somewhere to be standard compliant).


This is what the C++11 standard (Appendix D.5) says about it.

For compatibility with the C standard library and the C Unicode TR, the C++ standard library provides the 25 C headers, as shown in Table 154.
[table][tr][th=5]Table 154 - C headers[/th][/tr][tr][td]<assert.h>[/td][td]<inttypes.h>[/td][td]<signal.h>[/td][td]<stdio.h>[/td][td]<wchar.h>[/td][/tr][tr][td]<complex.h>[/td][td]<iso646.h>[/td][td]<stdalign.h>[/td][td]<stdlib.h>[/td][td]<wctype.h>[/td][/tr][tr][td]<ctype.h>[/td][td]<limits.h>[/td][td]<stdarg.h>[/td][td]<string.h>[/td][td][/td][/tr][tr][td]<errno.h>[/td][td]<locale.h>[/td][td]<stdbool.h>[/td][td]<tgmath.h>[/td][td][/td][/tr][tr][td]<fenv.h>[/td][td]<math.h>[/td][td]<stddef.h>[/td][td]<time.h>[/td][td][/td][/tr][tr][td]<float.h>[/td][td]<setjmp.h>[/td][td]<stdint.h>[/td][td]<uchar.h>[/td][td][/td][/tr][/table]

Advertisement
I concede the point, but I would still strongly recommend never to include C headers instead of their C++ counterpart unless an extremely compelling reason exists.

Okay, the responses *sorta* fixed the issue I've been having with the singleton. It doesn't crash anymore, but now there's something else going wrong. Whenever the singleton class actually gets used, it crashes, but somewhere else in the program (an internal crash within SDL_InitSubSystem(SDL_INIT_VIDEO)). And honestly, I have no idea what's going on here. Maybe I should have shared the debugging class while I was at it.


/******************************************************************************

  Copyright (C) 1999, 2000 NVIDIA Corporation
  This file is provided without support, instruction, or implied warranty of any
  kind.  NVIDIA makes no guarantee of its fitness for a particular purpose and is
  not liable under any circumstances for any damages or loss whatsoever arising
  from the use or inability to use this file or items derived from it.
  
    Comments:
    
	Simple debug support with stream input for complex arguments.
	Also writes to a debug log style file.

	Declare this somewhere in your .cpp file:

	NVDebug dbg(DebugLevel, "outfile.txt")
	
	// Output Like this 
	DISPDBG(0, "This" << " is " << "a useful debug class");

  cmaughan@nvidia.com
      
******************************************************************************/
#ifndef __NVDEBUG_H
#define __NVDEBUG_H

#ifdef _WIN32
#include <windows.h>
#pragma warning(disable: 4786)
#include <tchar.h>
#endif
#pragma warning(disable: 4786)
#include <iostream>
#pragma warning(disable: 4786)
#include <sstream>
#pragma warning(disable: 4786)
#include <iomanip>
#pragma warning(disable: 4786)
#include <strstream>
#pragma warning(disable: 4786)
#include <fstream>
#pragma warning(disable: 4786)
#include <assert.h>
#include "singleton.h"
#ifdef _WIN32
#include <crtdbg.h>
#endif

/* Non-Windows platforms */
#ifndef _WIN32
#define OutputDebugString printf
#define TEXT(s) s
#endif

/* Trap functionality */
#ifdef _MSC_VER
#define __trap  _asm int 3
#else
#define __tram  __asm__("int $3")
#endif


static const DWORD MaxDebugFileSize = 100000;

class NVDebug : public Singleton<NVDebug>
{
public:

	NVDebug(unsigned int GlobalDebugLevel, const char* pszFileName)
		: m_GlobalDebugLevel(GlobalDebugLevel),
		m_dbgLog(pszFileName, std::ios::out | std::ios::trunc)	// Open a log file for debug messages
	{
		OutputDebugString(TEXT("NVDebug::NVDebug\n"));
	}
	
	virtual ~NVDebug()
	{
		m_dbgLog.flush();
		m_dbgLog.close();

		m_strStream.flush();
		m_strStream.clear();
	}

	// Flush the current output
#ifdef _WIN32
	void NVDebug::EndOutput()
#else
    void EndOutput()
#endif
	{
		m_strStream << std::endl << std::ends;

		// Don't make a huge debug file.
		if (m_dbgLog.tellp() < MaxDebugFileSize)
		{
			m_dbgLog << m_strStream.str();
			FlushLog();
		}

		OutputDebugString(m_strStream.str());

		m_strStream.freeze(false);
		m_strStream.seekp(0);
	}

	unsigned int GetLevel() const { return m_GlobalDebugLevel; };
	std::ostrstream& GetStream() { return m_strStream; }
	void FlushLog() { m_dbgLog.flush(); }
	
private:
	std::ostrstream m_strStream;
	std::ofstream m_dbgLog;
	unsigned int m_GlobalDebugLevel;
};

#ifdef _DEBUG
#define DISPDBG(a, b)											\
do																\
{																\
	if (NVDebug::GetSingletonPtr() != NULL)						\
	if (a <= NVDebug::GetSingleton().GetLevel()) {	\
		NVDebug::GetSingleton().GetStream() << b;			\
		NVDebug::GetSingleton().EndOutput(); }				\
} while(0)

#define NVASSERT(a, b)														\
do																			\
{																			\
	static bool bIgnore = false;											\
	if (!bIgnore && ((int)(a) == 0))										\
	{																		\
		std::ostringstream strOut;											\
		strOut << b << "\nAt: " << __FILE__ << ", " << __LINE__;			\
		int Ret = MessageBoxEx(NULL, strOut.str().c_str(), "NVASSERT", MB_ABORTRETRYIGNORE, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ));	\
		if (Ret == IDIGNORE)			\
		{								\
			bIgnore = true;				\
		}								\
		else if (Ret == IDABORT)		\
		{								\
            __trap;                     \
		}								\
	}									\
} while (0)

#else
#define DISPDBG(a, b)
#define NVASSERT(a, b)
#endif

// Note that the cast ensures that the stream
// doesn't try to interpret pointers to objects in different ways.
// All we want is the object's address in memory.
#define BASE_DBG_PTR(a)					\
"0x" << std::hex << (DWORD)(a) << std::dec
	
#endif // __NVDEBUG_H

I hacked it up a bit so it would compile under XCode (and yeah, I know I still have to deal with the MessageBox code; SDL has a rough equivalent), and so far, I've only used the example from the top comment in this header. The code works, and the string is placed in the log file, but after that, that totally unrelated crash happens shortly after. Doesn't make any sense to me, or was a pointer corrupted somehow? I dunno what this singleton class is actually doing, or even what makes it useful. Any more ideas? Thanks.

Shogun.

EDIT: I forgot to mention that if I comment out DISPDBG in my code, no crashes.

//DISPDBG(0, "This" << " is " << "a useful debug class"); <- No crash

Mandatory reading.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Okay, here's a better set of details.

The exact message I get from XCode is this: "Thread 1 (Main thread, obviously): EXC_BAD_ACCESS (code=13, address=0x0)"

Inside of SDL_InitSubSystem( SDL_INIT_VIDEO ), there is a crash inside of a call to getMethodNoSuper_nolock(class_t*, objc_selector*). Also, this crash doesn't even happen in the same function where the debug code was initialized. Only one call to DISPDBG was called, and it was directly after initializing the class with the singleton.

I've tried this:

NVDebug dbg( 1, "debug.txt" );

And I've tried this:

NVDebug* dbg = NVDebug( 1, "debug.txt" );

Immediately after, I do this:

DISPDBG( 1, "Initialization started...\n" );

Now, the crash is actually quite far from this initialization code. There doesn't really appear to be much I can do to find out exactly whats causing it. Maybe I'm asking for too much, sorry about that. I'm just unsure why this is happening on MacOSX, and not Windows.

Shogun.

Post the complete call stack and indicate where your code begins in that stack.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

I took two screen shots of the call stack. The first one:

[attachment=22794:Screen Shot 2014-07-24 at 6.38.31 PM.png]

Not much here, because this is just one of my functions called within the main function. This is where my singleton is initialized. After I finish with this function we're in now, I leave that one (return from it), and call the next one.

[attachment=22795:Screen Shot 2014-07-24 at 6.39.30 PM.png]

This is where things no longer make sense. If it's going to crash, why there of all places? Where the code began is not in this second image, but the first one, so it's really confusing.

Shogun.

Not sure why you think this is in any way related to your singleton. It's most likely a problem in Objective-C code, specifically poking an object that doesn't exist (either null or has been released).


Also that callstack looks incomplete, although I honestly am not familiar enough with Xcode to know how to get the whole thing.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

After much tedious work, I think it's safe to say that it's not the singleton. I tried removing it and using a much less convenient way of doing things, and it still crashes. At least I have a better idea of what's going on. Here's the error:

"example03(21782,0x7fff73dd2960) malloc: *** error for object 0x10181a8e8: incorrect checksum for freed object - object was probably modified after being freed.

*** set a breakpoint in malloc_error_break to debug"

Something wrong with the STL code. There's a buffer overflow most likely.

Shogun.

EDIT: Fixed it. I looked up std::ostrstring, and it's a deprecated string type and has been since C++98. Changing to std::ostringstream fixed it. Now I feel stupid... again. I owe singletons everywhere an apology.

This singleton design is weird. Scott Bilas' article is from 2000; there are better ways to do this today. A better way (other than just not using singletons which is an even better idea):

// note that this does not attempt to be thread-safe as it is assumed you do initialization/destruction
// early in program startup and late during shutdown when no threads will be active
template <typename T>
class ManagedSingleton {
// where we allocate the singleton object - guaranteed to be big enough and aligned properly
std::aligned_storage<sizeof(T), alignof(T)>::type _storage;

// basically just a flag and a convenience to see if the singleton is engaged (has a valid object in it) or not
T* _ptr = nullptr;

public:
// disable copies of the singleton container
ManagedSingleton (ManagedSingleton const&) = delete;
void operator=(ManagedSingleton  const&) = delete;

// create a new instance of some type which is either T or a derived type of T
template <typename C, typename... Params>
C* Create(Params&&... params) {
assert(_ptr == nullptr);
new (&_storage) C(std::forward<Params>(params)...);
_ptr = reinterpret_cast<T*>(&_storage);
return static_cast<C*>(_ptr);
}

// destroy the instance
void Destroy() {
assert(_ptr != nullptr);
_ptr->~T();
_ptr = nullptr;
}

// check if we have a valid object or not
bool empty() const { return _ptr == nullptr; }

// access the object
T* get() const { assert(_ptr != nullptr); return _ptr; }
T* operator->() const { return get(); }
T& operator*() const { return &get(); }
};
// exmaple using it
class IFooSystem {
protected:
virtual ~IFooSystem() {}
};
class FooSystem : public IFooSystem {
public:
FooSystem(int arg1, float arg2, char arg3);

void DoFoo();
};

ManagedSingleton<IFooSystem> g_fooSys;

int main() {
g_fooSys.Create<FooSystem>(1, 2.0f, '3');

g_fooSys->DoFoo();

g_fooSys.Destroy();
}
(I have no idea why the forum ate the indentation; it usually doesn't do that)

This explicitly contains the storage location, doesn't require you to declare the static storage separately, lets you have managed singletons of interfaces rather than of concrete types, doesn't require that you declare a type as a singleton explicitly, has no weird pointer manipulation, and in release mode is as fast as a global pointer.

Sean Middleditch – Game Systems Engineer – Join my team!

This topic is closed to new replies.

Advertisement