Saturday, January 17, 2009

Use std:atexit to kill C++ singletons

Killing singletons is not really explained completely on any one blog that I found.
There is a single template listed at another blog (SEE BELOW) that has a DestroyInstance
function. That is called in a std:;atexit function. This seems to do the trick. One note:
make sure that the singleton is not called in destructor code.

/*!
\file singleton.h
\brief Implementation of the CSingleton template class.
\author Brian van der Beek

Introduction
There are times, when you need to have a class which can be instantiated once only. The Singleton
Design Pattern provides a solution for such a situation.

There are several possible ways to implement a singleton pattern, but it all pretty much comes down
to a class that has a private constructor and a static member function to create and retrieve an
instance of the class. My implementation does not differ much from this scenario, with the exception
that I created a singleton template class.

So, why a template class?
Well I have searched the Internet for an elegant implementation of a singleton class but I did not
really find a solution to my satisfaction. Most classes I found consist of a Singleton base class that
you can use to derive your own singleton class from. The problem with most of these classes is the fact
that you still have to override the GetInstance function to return a pointer to your derived class.
A template base class does not have this limitation as I can return any type of pointer.

How it works
To prevent outside source from creating (or copying) an instance of our singleton class, we need to
shield the constructor and copy constructor of the singleton class. Further we need to provide a
method to create and retrieve a reference to the singleton object:

static T* Instance() {
if (m_instance == NULL) {
m_instance = new T;
}

ASSERT(m_instance != NULL);

return m_instance;
};

When this method is called for the first time, it creates an instance of the singleton class,
any sequential calls will return a reference to the created class instance. To get a reference
to the singleton object, all we have to do is call this method as following:

CMySingleton* mySingleton = CMySingleton::Instance();

That is almost all that there is to it. Next to shielding the constructors, I also shielded
the destructor, so the singleton class cannot be deleted by accident. Just call the DestroyInstance()
method to destroy the singleton object. However, be careful when to call this method, because after
you have called this method, all your class data will be destroyed and a sequential to the Instance()
method will create a new instance.

So how do you create a class derived from the singleton template class? Again there is nothing to it.
Just include the attached header file and create your object as following:

class CMySingleton : public CSingleton {
friend CSingleton;

private:

CMySingleton();
~CMySingleton();

...
}

Conclusion
This implementation of the Singleton Pattern makes creating your own singleton class incredibly easy.
But you do have to be careful when to destroy the singleton class instance. If you find this to be a
problem you could consider adding (automatic) reference counting.

This article has no explicit license attached to it but may contain usage terms in the article text
or the download files themselves. If in doubt please contact the author via the discussion board below.
A list of licenses authors might use can be found here

*/

#ifndef __SINGLETON_H__
#define __SINGLETON_H__

#include

//! The CSingleton class is a template class for creating singleton objects.
/*!
When the static Instance() method is called for the first time, the singleton
object is created. Every sequential call returns a reference to this instance.
The class instance can be destroyed by calling the DestroyInstance() method.
*/
template
class CSingleton {
public:

//! Gets a reference to the instance of the singleton class.
/*!
\return A reference to the instance of the singleton class.
If there is no instance of the class yet, one will be created.
*/
static T* Instance() {
if (m_instance == NULL) {
m_instance = new T;
}

// ASSERT(m_instance != NULL);

return m_instance;
};

//! Destroys the singleton class instance.
/*!
Be aware that all references to the single class instance will be
invalid after this method has been executed!
*/
static void DestroyInstance() {
delete m_instance;
m_instance = NULL;
};

protected:

// shield the constructor and destructor to prevent outside sources
// from creating or destroying a CSingleton instance.

//! Default constructor.
CSingleton(){};


//! Destructor.
virtual ~CSingleton(){};

private:

//! Copy constructor.
CSingleton(const CSingleton& source){};

static T* m_instance; //!< singleton class instance
};

//! static class member initialisation.
template T* CSingleton::m_instance = NULL;


#endif // ! defined __SINGLETON_H__

//////////////////////////////////////////////////////

class CLiTool : public CSingleton {
friend class CSingleton;
...
}

//////////////////////////////////////////////////////

void killSingletons() {
NSCLi::CLiTool::DestroyInstance(); // call the singleton template functions
NSCLi::CLiCommon::DestroyInstance();
NSCslc::CslcTool::DestroyInstance();
}

...

int main(int argc, char** argv) {

// Kill the singletons on exit
std::atexit(&killSingletons);
...
}

Emacs 22 + Java Development Environment (JDE)

Up to date emacs elisp configuration instructions.

How to install JDE on emacs 22. Complete tarball and install script for JDE including the CEDET Elisp code.

Tuesday, January 6, 2009

Use singletons instead of static member vars and functions

A developer's code used macros, defines, and statics to initialize the constant variables used in the top level of the program.
This worked on Linux. However, since the ISO/ANSI C++ standard does not guarantee the initialization sequence
of the constructors there is a 50/50 chance that a compiler on a new platform will use a different init sequence resulting in
segfaults/problems. This is exactly what happened on the Mac. So I converted his macros/statics/defines to singleton
classes and things seem to be working better on the Mac. I had to change about 3k lines of code to use the singleton refs instead of defines and statics.

Good linker article

Linker article

Linker articles

C++ FAQ

With info on static vars

Monday, January 5, 2009

nm -C

The porting job to the Mac continues. Murphy's law is in full effect.

It turns out the you need to run nm on the lib files with a -C arg to
disassemble the .a file and see the namespace that the symbol belongs
to.

I used -C and immediately saw that i had forgotten to prefix the
assignment of the static variables in the CLiTool class with the CLiTool
qualifier. Once I added the scope name
qualifier to the name things got "better".

It has been far too long since I "really" programmed in C++.


0000000000000020 B NSCLi::CLiTool::CURRENT_DIR
0000000000000010 B NSCLi::CLiTool::DIR_DELIMITER
0000000000000008 B NSCLi::CLiTool::INVALID_CHARS
00000000000001ab R NSCLi::CLiTool::CDIR_DELIMITER
0000000000000030 B NSCLi::CLiTool::END_ENV_VAR_NAME
0000000000000028 B NSCLi::CLiTool::BEGIN_ENV_VAR_NAME
0000000000000018 B NSCLi::CLiTool::BACK_DIR
0000000000000020 T NSCLi::CLiTool::CLiTool()
0000000000000000 T NSCLi::CLiTool::CLiTool()
00000000000000c0 b NSCLi::CSL_PP_
0000000000000120 b NSCLi::VER_PP_
0000000000000090 b NSCLi::VER_PLUS
0000000000000130 b NSCLi::VER_PRJ_
0000000000000198 b NSCLi::CDOM_AST_
U std::string::size() const
U std::string::operator[](unsigned long) const
U std::allocator::allocator()
U std::allocator::~allocator()
U std::basic_string,
std::allocator >::basic_string(char const*, std::allocator
const&)
U std::basic_string,
std::allocator >::~basic_string()
U std::ios_base::Init::Init()
U std::ios_base::Init::~Init()
0000000000000040 t std::__verify_grouping(char const*, unsigned long,
std::string const&)
0000000000000000 W unsigned long const& std::min(unsigned
long const&, unsigned long const&)
0000000000000038 b std::__ioinit
U __cxa_atexit
U __dso_handle
U __gcov_init
U __gcov_merge_add
U __gxx_personality_v0
0000000000000352 t __tcf_10
000000000000038e t __tcf_11
00000000000003ca t __tcf_12

Sunday, January 4, 2009

linker/C++ blogs with great info

http://blog.copton.net/articles/linker/index.html
http://www.airs.com/blog/archives/38
http://parashift.com/c++-faq-lite/

With info on static vars
http://parashift.com/c++-faq-lite/ctors.html

Saturday, January 3, 2009

Don't use defines for string constants in C++part two-linker issues

Well, the saga continues. After replacing a set of constants and enums in one include file
with a C++ class containing the constants and enums we ran into problem number 2.
The constants from include file 1 are used in include file number 2. This results in linker
errors such as the one below. Note the lazy pointers-this is on a Mac.

compile:
[cc] Starting dependency analysis for 1 files.
[cc] 1 files are up to date.
[cc] 0 files to be recompiled from dependency analysis.
[cc] 1 total files to be compiled.
[cc] Starting link
[cc] ld warning: duplicate dylib /usr/lib/libz.1.dylib
[cc] Undefined symbols:
[cc] "NSCLi::CLiTool::CSL_PRINT_IT_FILENANME_", referenced from:
[cc] __ZN5NSCLi7CLiTool23CSL_PRINT_IT_FILENANME_E$non_lazy_ptr in libSupport_Library.a(cslcCLI.o)
[cc] "NSCLi::CLiTool::INFO_", referenced from:
[cc] __ZN5NSCLi7CLiTool5INFO_E$non_lazy_ptr in libSupport_Library.a(cslcCLI.o)
[cc] "NSCLi::CLiTool::CONFIG_FILE_", referenced from:
[cc] __ZN5NSCLi7CLiTool12CONFIG_FILE_E$non_lazy_ptr in libSupport_Library.a(cslcCLI.o)
[cc] "NSCLi::CLiTool::VER_F_", referenced from:
[cc] __ZN5NSCLi7CLiTool6VER_F_E$non_lazy_ptr in libSupport_Library.a(cslcCLI.o)
[cc] "NSCLi::CLiTool::VER_V_", referenced from:
[cc] __ZN5NSCLi7CLiTool6VER_V_E$non_lazy_ptr in libSupport_Library.a(cslcCLI.o)
[cc] "NSCLi::CLiTool::VER_Y_", referenced from:

We need to add the new .o or .a files to the linker command in the ant file.

Don't use defines for string constants in C++

I wrote this one up because I could not find anything about this on the web. Someone wrote some code to
delete temporary files. But the files were not being deleted. And then we ported the code to another platform.
Well this problem turned out to be a destructor chain problem. It was not a memory leak per say or a logic
bug. Rather this was due to the ambiguity in the ISO C++ standard according to a C++ expert who has worked
on C++ debuggers.

The ISO C++ standard does not clearly spell out when the text segment is deallocated.
We had a bug which showed on Mac OS X and not on CENTOS Linux.

A destructor chain was calling a function in a object that used a global string variable.
The string variable was undefined at the time the destructor chain was executed. On
the Mac STL threw a strlen exception. Valgrind did not find the problem on Linux. Leaks
did not find the problem on the Mac. We replaced the defines with a C++ class containing
static strings (a singleton class) and the exception went away.

The way we found the problem was by running gdb and noticing that the destructor chain
contained a call to a non-destructor function. Then through a process of elimination we found
the offending var. The string had the correct value in Linux and the incorrect value in the Mac.
We then put a local var declaration and assignment in the function called by the destructor and
the bug went away.

(gdb) backtrace
#0 0x938e6b9e in __kill ()
#1 0x938e6b91 in kill$UNIX2003 ()
#2 0x9395dec2 in raise ()
#3 0x9396d47f in abort ()
#4 0x96e5e005 in __gnu_cxx::__verbose_terminate_handler ()
#5 0x96e5c10c in __gxx_personality_v0 ()
#6 0x96e5c14b in std::terminate ()
#7 0x96e5c261 in __cxa_throw ()
#8 0x96e1ccaa in std::__throw_length_error ()
#9 0x96e43f5a in std::string::_Rep::_S_create ()
#10 0x96e4429a in std::string::_Rep::_M_clone ()
#11 0x96e4540e in std::basic_string, std::allocator >::basic_string ()
#12 0x00c3cfbf in NSCLi::CLiCommon::getRoot (path=@0xbfffee2c) at cslcCLI_Support.cpp:363<<<<<<<<<#13 0x00c3d7ac in NSCLi::CLiCommon::deleteFile (fileName=@0x1cbc610) at
/opt/he_fpl_svn/fpl_repo/cslc/trunk/src/support/cli/cslcCLI_Support.cpp:227
#14 0x00002c11 in NSCslc::CSLcMain::deleteTempFile (this=0x1cbab00, fileName=@0x1cbab80) at new_cslc.cpp:199
#15 0x00002d8f in NSCslc::CSLcMain::~CSLcMain (this=0x1cbab00) at
/opt/he_fpl_svn/fpl_repo/cslc/trunk/src/cslc/new_cslc.cpp:49
#16 0x00021171 in boost::checked_delete (x=0x1cbab00) at checked_delete.hpp:34
#17 0x000265ea in boost::detail::sp_counted_impl_p::dispose (this=0x1cbb960) at
detail/sp_counted_impl.hpp:79
#18 0x0001a6e0 in boost::detail::sp_counted_base::release (this=0x1cbb960) at detail/sp_counted_base_gcc_x86.hpp:145
#19 0x0001a71c in boost::detail::shared_count::~shared_count (this=0x10578ac) at detail/shared_count.hpp:205
#20 0x0001bd18 in boost::shared_ptr::~shared_ptr (this=0x10578a8) at shared_ptr.hpp:131
#21 0x0001914d in __tcf_223 () at new_cslc.cpp:34
#22 0x938a0fdc in __cxa_finalize ()
#23 0x938a0ed0 in exit ()
#24 0x00001ec7 in start ()
(gdb)

After determining what the problem is we then changed the defines into a tool class. This required moving the defines into the class. We then prefixed the variables where they were used with the tool class scope qualifier.

creating defines for C++ function pointers

Use defines to create function pointer typedefs

// *********************************************************************
// function pointers
// *********************************************************************
// the type name is THandler

typedef CLiTool::ECLiError (CLiArgumentList::*THandler)(const RefCLiArgumentBase&);
^^^^^^^
The type is THandler

Use it like this to avoid a complex function pointer as a param.

// *********************************************************************
// CLiArgumentEmpty class
// *********************************************************************
CLiArgumentEmpty::CLiArgumentEmpty(const RefCLiArgumentList& parent,
const RefTVec_RefString& keyWordList,
const THandler& handler,
TBool multiple)


The compiler can return unhelpful error messages related to syntax errors in defines containing function pointers

[cc] cslcCLI_Typedef.h:321: error: ISO C++ forbids declaration of ‘ECLiError’ with no type
[cc] cslcCLI_Typedef.h:321: error: typedef ‘NSCLi::ECLiError’ is initialized (use __typeof__ instead)
[cc] cslcCLI_Typedef.h:321: error: expected unqualified-id before ‘*’ token
[cc] cslcCLI_Typedef.h:321: error: ‘THandler’ was not declared in this scope
[cc] cslcCLI_Typedef.h:321: error: expected ‘,’ or ‘;’ before ‘(’ token

The above was caused by the missing scope qualifier before the return value in the define.

typedef ECLiError (CLiArgumentList::*THandler)(const RefCLiArgumentBase&);

instead of

typedef CLiTool::ECLiError (CLiArgumentList::*THandler)(const RefCLiArgumentBase&);