Implementing a singleton class.

I'm trying to find out how to implement a singleton class. Here's the background...... I've been playing around with reading and writing to files on an SD card. I've written a very basic 'logger' class to write error messages and other stuff to a log file. So far so good, I can include the class in another class or a sketch and I can log messages to the file without problems.

I now want to include the 'logger' object in all of the classes in my arduino application, but I want all of my classes to write to the same log file on the SD card. My research so far tells me I need to implement this as a 'singleton class' so there will only ever be one copy of my 'logger' object.

At the moment my header file looks like this.....

#ifndef Logger_h     
#define Logger_h     

extern "C" {
  #include <string.h>
}

#include <stdlib.h>       
#include "WProgram.h"     
#include "Logger.h" // this file
#include <MikeSDFat.h> 
#include <Sd2Card.h>
#include <FatStructs.h>
#include <Sd2PinMap.h>
#include <SdFat.h>
#include <SdFatmainpage.h>
#include <SdFatUtil.h>
#include <SdInfo.h>


class Logger
{
  public:
    Logger();                        // Constructor
    void writeLine(char * line);     // Write a line of data to the log file
    void close();                    // Close the file
  private:
    // Member variables
    MySDFat logFile;                  // needed to allow access to the logger file.
    int state;                           // used to determin the state of the file (open, closed etc)
    int lineCount;

};

How do I convert it to a singleton?

Regards

Mike

As an Arduino (normally) has only one thread, does your class really need to be a singleton? It is no multitasking environment where different objects run in different threads. so ...

wrt the class,

  • add the filename to the constructor That enables you to have different loggings (different sketches) on the same SDcard.
  • add a flag to writeline indicating type of log; use an enum LEVEL { DEBUG, INFO, WARNING, ERROR, MAYDAY }
  • add a timestamp before every string (prefered from a RTC if available)

reader - Singleton pattern - Wikipedia

an Arduino (normally) has only one thread

Does this mean an Arduino can be multithreaded? if so how? I thought arduino was single threaded only?

With respect to the original question, firstly, when a class is intrinsically linked to a single physical device it makes sense to have only a single instance of that class. Secondly, I have created multiple instances of my logger object, and although it does compile and run without error, only the first instance created actually manages to write to the file. This code snippet illustrates the problem?.

#include <Logger.h>

Logger My1stLogFile;
Logger My2ndLogFile;

void setup() 
{
  Serial.begin(9600);
  Serial.println("\n\rStarting......");
  
  My1stLogFile.writeLine("Line one");
  My2ndLogFile.writeLine("Line AAA");
  My1stLogFile.writeLine("Line two");
  My2ndLogFile.writeLine("Line BBB");
  My1stLogFile.writeLine("Line three");
  My2ndLogFile.writeLine("Line CCC");
  My1stLogFile.writeLine("Line four");
  My2ndLogFile.writeLine("Line DDD");
  My1stLogFile.close();
  
}

This runs without error but the resulting log file contains the lines written by the first instance of the object, not the second i.e......
Line one
Line two
Line three
Line four

If I reverse the order of the declarations then the file gets the other set of lines instead.

Basically I have 2 questions....

  1. is it possible to implement a singleton class?
  2. if the answer is 'yes' how do I do it?

Cheers

Does this mean an Arduino can be multithreaded? if so how? I thought arduino was single threaded only?

No, an Arduino can not be multi-threaded. That requires an operating system to manage which thread is running. The Arduino does not have an operating system.

Basically I have 2 questions....

  1. is it possible to implement a singleton class?
  2. if the answer is 'yes' how do I do it?

Typically, a singleton class has a private constructor and a public method to get the existing (static) instance. Your class has a public constructor, no static instance, and no method that returns that static instance.

Don't bother.

Just do everything as static. You will save yourself some typing and make the code that uses the library easier to read.

Its Serial.begin(9600) not Serial.instance.begin()

Keep it simple - thats one of the joys of the Arduino.

Its Serial.begin(9600) not Serial.instance.begin()

But, Serial IS an instance of the HardwareSerial class.

Ah, ok - I just thought it was a class with a load of static methods on it (I am actually talking Java and hoping its the same in C++).

So is Serial just a global variable assigned to an instance of a class called something like 'SerialImpelementation' ?

In that case, it sounds like that would be a good thing for our original poster to copy.

Just read the original post properly and I'm talking rubbish.

Ignore everything I have said.

Does this mean an Arduino can be multithreaded? if so how? I thought arduino was single threaded only?

ALthough PaulS is right that the Arduino has no OS there are efforts in such that direction, see - http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1256745982

Here is my sort of singlton in your code//

#include <SdFat.h>
#include <SdFatmainpage.h>
#include <SdFatUtil.h>
#include <SdInfo.h>


class Logger
{
  public:
    Logger() {} ; // Constructor does nothing now as no per logger state
    void writeLine(char * line);
    void close() ;

  private:
    void open() ;

    // Static Member variables that implement the singleton
    static MySDFat logFile;
    static char state;
    static int lineCount;      

};

and the library code is

#include <Logger.h>

// initialise the static memeber variables.

MySDFat Logger::logFile ; // set constructor parameters here as/and if required.

#define CLOSED 0
#define OPENED 1
char Logger::state = CLOSED ; 

int  Logger::lineCount = 0 ;

// Ensure the single chosen file is open for logging
void Logger::open()
{
  if (state != OPENED)
  {
     // open the file :)
     state = OPENED ;
  }
}

void Logger::close()
{
  if (state == OPENED)
  {
    // close the file :)
    state = CLOSED ;
  }
}

void Logger::writeLine(char *line)
{
  open() ; // open file      
  // logFile.writeLine(line) ; 
  lineCount ++ ;
}

This pattern (adjusted for the way the code really works) should make your test example (above) log all of the lines from both Logger objects.

It will not work if you use logging in the interrupt handlers as well as in the main loop of the code.

And doing that is a MUCH bigger problem, and also not sensible IMHO.

Dave