Sunday, November 4, 2012

Single Class Instance (Singleton Class)

There is only going to be one instance of the Table class in the program.  I recalled from when learning C++ that there was a way to allow only one instance of a class to be created.  After a little research, it was found that this is known as a singleton class.  There are several ways to implement this.  The solution used for the Table class is described below.  The goals were (click Continue... for complete details on the implementation):
  1. Allow only one instance of the table to be created
  2. Detect table entry errors when the instance is created
  3. Return a list of any error messages of errors found in the table entries
  4. Get a reference to the table instance created

Singleton Class Implementation:

First there needed to be a pointer to the single instance, which was accomplished by adding a private static pointer to the table class:
static Table *m_instance;
And since this is a static member, it needs to be declared in the table source file.  Being a static member, it is initialized to a null pointer, so it is easy to detect if the instance has been created:
Table *Table::m_instance;
To prevent the class from being instanced more than once, the default constructor, copy constructor and the assignment operator were declared private (the third one was a little tricky as the solution found on-line showed it with an empty body like the other two, but the compiler didn't like that saying it needed a return value):
Table(void) {}
Table(Table const &) {}
Table &operator=(Table const &) {return *this;}
An additional private constructor was implemented to set the table entries array pointer member from the table entries array.  This wasn't necessary, but it can be used when the single instance is created instead of setting it later (declaration of the table entries array is also shown):
static TableEntry tableEntries[] = {...};
...

Table(TableEntry *entry) : m_entry(entry) {}
To create the single table instance, a static public function was added that verifies that an instance wasn't already created (instance pointer is null), creates the instance using the new operator with the additional private constructor with the pointer to the preinitialized table entries array, and calls the initialize function that initializes additional table entry variables and validates the table entries.

If the initialize function returns any errors, the instance is deleted and the pointer is reset to null.  The list of errors (an empty list if the initialization was successful) is returned, which the caller checks (displays the errors if any and aborts).
static QStringList create(void);
The second static public function was added to get a reference to the single table instance created.  This function first checks if the instance has been created (fatally aborts if not with a message indicating the table instance was not created), and returns a reference to the instance.  Any class needing access to the table can call this function.
static Table &instance(void);
The existing initialize function was made private and is now only called by the create function.  The entry table pointer assignment was removed since this is now set in the new constructor.  This function continues to return a list of error messages, which is empty if no errors were detected in the table entries.
QStringList initialize(void);
In the start up routine (for the moment, the main function), creates the table by calling the static create function and saves any errors detected:
QStringList errors = Table::create();
If the errors list is not empty, each message is output and the program aborts.  Once the GUI is implemented, the error messages will be put inside a dialog that will pop up before the program terminates.  (This functionality is really only needed for development.)

The parser and translator constructors were modified to take a reference to the table instance and these classes were modified to use a reference instead of a pointer (really no difference but the code is a tiny bit cleaner using the dot '.' member operator instead of the pointer '->' member operator).

The various test functions were also cleaned up by not passing the table instance pointer around (only the print small token function actually needed the table, so it now gets its own reference of the table instance).

[commit 1868158e9c]

No comments:

Post a Comment

All comments and feedback welcomed, whether positive or negative.
(Anonymous comments are allowed, but comments with URL links or unrelated comments will be removed.)