Thursday, 22 January 2015

Singleton design pattern in C++ With example

Singleton design pattern in Cpp with example:

We can use single object of the class throughout the lifetime of an application using singleton design pattern.

To get a single object is by declaring a class, which contains only static methods. Then the static class is loaded into the memory when the program execution starts and still available until the application ends. If there is situation that we need only one instance of a class in a truly object oriented fashion by adhering to the basic principles of object oriented programming, the Singleton patterns are used.

The Singletons are often used to control access to resources such as database connections or sockets.

Suppose we have a license for only one connection for our database. A Singleton connection object makes sure that only one connection can be made at any time.


The class diagram of the singleton pattern 

Singleton Pattern

Let us see the working example of the singleton class

Example program for singleton class for logging the events of the application:
#ifndef _GEN_LOGGER_H_
#define _GEN_LOGGER_H_
#include <fstream>
#include <ostream>
#include <string>
#include <sstream>
#include "afxwin.h"
#include "afxcmn.h"
#include <time.h>

const int DBG_ERROR = 0;
const int DBG_WARN = 1;
const int DBG_DEBUG = 2;

#ifdef LOGGER_MULTITHREAD
#include <pthread.h>

#endif

#define DEBUG_CONF(outputFile, \
  configuration, \
  fileVerbosityLevel, \
  screenVerbosityLevel) { \
   Gen_Logger::getInstance().configure(outputFile, \
      configuration, \
      fileVerbosityLevel, \
      screenVerbosityLevel); \
  }

#define DEBUG(priority, msg) { \

 std::ostringstream __debug_stream__; \
 __debug_stream__ << msg; \
 Gen_Logger::getInstance().print(priority, __FILE__, __LINE__, \
   __debug_stream__.str()); \
 }
class Gen_Logger
{
private:
  Gen_Logger ();
 ~Gen_Logger ();
 enum loggerConf_ {L_nofile_ =  1 << 0,
    L_file_  = 1 << 1,
    L_noscreen_ = 1 << 2,
    L_screen_ = 1 << 3};

#ifdef LOGGER_MULTITHREAD
 static pthread_mutex_t lock_;
#endif
 static Gen_Logger* m_pcSingleLog;
 std::string logFile_;
 loggerConf_ configuration_;
 std::ofstream out_;
 CTime initialTime_;
 inline static void lock ();
 inline static void unlock ();
public:
 typedef loggerConf_ loggerConf;
 static const loggerConf file_on=  L_nofile_;
 static const loggerConf file_off=  L_file_;
 static const loggerConf screen_on=  L_noscreen_;
 static const loggerConf screen_off= L_screen_;
 static Gen_Logger* getInstance ();


 void print (const std::string& sourceFile, const std::string& message,CString &outfile);
 void print (const std::string& sourceFile, const std::string& message);
 void configure (const std::string& outputFile);
};
inline Gen_Logger::loggerConf operator| (Gen_Logger::loggerConf __a, Gen_Logger::loggerConf __b)
{
 return Gen_Logger::loggerConf(static_cast<int>(__a) |
 static_cast<int>(__b));
}
inline Gen_Logger::loggerConf operator& (Gen_Logger::loggerConf __a, Gen_Logger::loggerConf __b)
{
  return Gen_Logger::loggerConf(static_cast<int>(__a) &
 static_cast<int>(__b));
}

#endif //_GEN_LOGGER_H_

The implementation of the header file is
#include "stdafx.h"
#include <iostream>
#include <new>
#include <cstdlib>
#include "Gen_Logger.h"

// Definition (and initialization) of static attributes
Gen_Logger* Gen_Logger::m_pcSingleLog = NULL;
Gen_Logger ::Gen_Logger ()
  {
 }
void Gen_Logger::configure (const std::string& outputFile)
{
  if (configuration_&file_on)
   out_.close();
  if (outputFile != logFile_){
  std::ostringstream oss;
  oss << outputFile << ".log";
  logFile_ = oss.str().c_str(); }
 out_.open(logFile_.c_str(), std::ios::app);
}
Gen_Logger::~Gen_Logger()
{
 if (configuration_&file_on)
  out_.close();
 delete m_pcSingleLog;
}
Gen_Logger* Gen_Logger::getInstance()
{
 if (m_pcSingleLog == 0)
  m_pcSingleLog = new Gen_Logger;
 return m_pcSingleLog;
}
void Gen_Logger::print (const std::string& oType,
                        const std::string& message,CString &outfile)
{
 std::ofstream outFile;
   outFile.open(outfile,std::ios::app);
  CTime currentTime= CTime::GetCurrentTime ();
  outFile << "[" <<currentTime.GetDay () << "-" << currentTime.GetMonth () << "-" << currentTime.GetYear () << " "
       << currentTime.Format ("%H:%M:%S") << "]" << "[" << oType << "]" <<"\t" << message << std::endl;
}
void Gen_Logger::print (const std::string& oType, const std::string& message)
{
  CTime currentTime= CTime::GetCurrentTime ();
  out_ << "[" <<currentTime.GetDay () << "-" << currentTime.GetMonth () << "-" << currentTime.GetYear () << " "
       << currentTime.Format ("%H:%M:%S") << "]" << "[" << oType << "]" <<"\t" << message << std::endl;
}


Testing the above singleton class
Create the instance as the member of the class that where we want to use the singleton class
#include "Gen_Logger.h"
class Test ()
{
  Test ();
  Gen_Logger *m_pcLogger;
};
Test::Test ()
{
  m_pcLogger = Gen_Logger::getInstance ();
  m_ocLogPath = ocOutpath + "\\MyLogFile.log";
  m_pcLogger->print ("INFORMATION", "I am in the constructor of the class",m_ocLogPath);
}


We have to remember that the Implementation of a singleton pattern must satisfy the single instance and global access principles. It also requires the  feature that to access the singleton class member without creating a class object and a mechanism to persist the value of class members among class objects.

And while implemetion the singleton pattern for multi-threading applciations

The singleton pattern must carefully constructed in  multi-threaded applications. If two threads are to execute the creation method at the same time when a singleton does not yet exist, they both must check for an instance of the singleton and then only one should create the new one.

No comments: