Pages: [1]   Go Down
Author Topic: Implementing a singleton class.  (Read 9046 times)
0 Members and 1 Guest are viewing this topic.
UK
Offline Offline
Jr. Member
**
Karma: 2
Posts: 90
It was like it when I found it
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.....
Code:
#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
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 216
Posts: 13676
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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 - http://en.wikipedia.org/wiki/Singleton_pattern
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

UK
Offline Offline
Jr. Member
**
Karma: 2
Posts: 90
It was like it when I found it
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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….
Code:
#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
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 611
Posts: 49101
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.

Quote
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.
Logged

UK
Offline Offline
God Member
*****
Karma: 13
Posts: 903
Twitter: @simonmonk2
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

--
My New Arduino Book: http://www.arduinobook.com

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 611
Posts: 49101
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Its Serial.begin(9600) not Serial.instance.begin()
But, Serial IS an instance of the HardwareSerial class.
Logged

UK
Offline Offline
God Member
*****
Karma: 13
Posts: 903
Twitter: @simonmonk2
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: January 20, 2011, 08:22:05 am by simon.monk » Logged

--
My New Arduino Book: http://www.arduinobook.com

UK
Offline Offline
God Member
*****
Karma: 13
Posts: 903
Twitter: @simonmonk2
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Ignore everything I have said.
Logged

--
My New Arduino Book: http://www.arduinobook.com

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 216
Posts: 13676
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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

Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

UK
Offline Offline
Full Member
***
Karma: 2
Posts: 110
Kittens eat Arduinos
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is my sort of singlton in your code//
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
Code:
#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

Logged

Pages: [1]   Go Up
Jump to: