Friday, September 26, 2014

C Programming: Singleton Design Pattern

The Singleton design pattern is a common pattern used in object oriented programming.  To use the pattern, any constructors of the singleton object must be private.  The user must not be able to create new instances of the object explicitly.  In this design pattern, the class must only be able to have a single instance.  Often this instance is created the first time it is requested, but it may be created at startup time, depending on the programming language.  Subsequent requests will be provided with the already existing instance.  This design pattern is primarily useful in languages that require object orientation, as a place to collect related global variables and functions, where only one instance of the collection should ever exist.  It is less often used in languages that allow object orientation but do not enforce it.  There are some cases, however, where it is useful regardless of the language.

One place where the Singleton design pattern is useful regardless of language is the case where a single instance of a global variable is necessary, but it is also necessary to limit how the user may interact with that variable.  In 3D graphics, the main camera is one of these global variables.  The camera can be stored as a pair of vectors, one representing "up" and the other representing the direction the camera is facing.  The third vector, the facing of one of the sides, can easily be calculated from the other two.  It is essential, however, that the "up" and "facing" vectors always be perpendicular to each other.  If they ever become parallel, the third vector cannot be calculated, and the camera math starts to get zeros and infinities where they do not belong.  This makes it impossible for the computer to render graphics that make sense.  The Singleton pattern can be used to solve this problem.  A single instance of a camera class can be made where the main camera is a private variable of the class.  The setter for the camera can ensure that changes to the camera never allow invalid states.  Further, methods can be added to the Singleton that allow the user to apply specific transformations to the camera, which removes the burden (and risk) of users trying to do the math for the transforms themselves.

In most cases, the Singleton design pattern is used to hold global things where the language does not provide a better option.  In some cases though, this design pattern can be useful in its own right.  A problem occurs when the benefits of this design pattern are necessary in a language that does not support object orientation.  For example, the C programming language has no object orientation support, but embedded systems often have limited support for languages other than C (or assembly).  This may not be true of all non-object oriented languages, but the Singleton design pattern is actually possible in C.

This C Programming series is going to discuss how to use object oriented principles in the C language.  In most cases, it is probably a bad idea to use these principles if any other option is available, but in cases like embedded systems, where an object oriented language is not available, it may be necessary, or at least substantially more efficient, to use these principles.  The remainder of this article will discuss using the Singleton pattern in C and demonstrate how it can be done.

In C, encapsulation and hiding sensitive data is generally considered impossible.  Very basic encapsulation can be accomplished with structs, but the language does not have any built in mechanics for preventing a user from changing any variable that is in scope.  This means that protecting a global variable in a getter/setting fashion is impossible.  This leads to several difficulties.  The first is that it is impossible to enforce data validation.  A well designed library might offer setters and getters, but a user of the library might choose to go around them, accessing the variable directly.  This puts the burden of correctness on the user, which has proven problematic enough to justify the wide adoption of private and protected variables in object oriented languages.

There is a simple way of making private global variables in external libraries.  This is probably nothing new, and has likely been used in many C libraries that use internal state machines.  It is not, however, often taught in computer science classes.  In C, libraries are contained in separate files from the main program.  Each library has at least one source code file as well as a header file.  The header file exposes interfaces contained in the library to the program that is using the library.  Global variables are exposed with an "extern" statement.  If they are not exported, the main program does not even know they exist and thus cannot access them.  This does not mean that they do not exist though.  The library where the variables are declared can still access them.  If this library has exposed functions that can change the hidden variables, then the main program can still access them indirectly.  This technique can be used for functions as well.  Following is some example code for a C library using what amounts to the Singleton design pattern.


private.c:

// This variable is subject to strict
// requirements.
int private_variable = 5;


// private_variable must be between 5 and 10
// inclusive.  Invalid input will be ignored.
void set_private(int input) {
    if (input < 5 || input > 10)
        return;
    else
        private_variable = input;
}

// We don't want to expose the variable or its
// memory address, so we use a getter to return
// by value.
int get_private() {
    return private_variable;
}

private.h
// The hidden variable must be between 5 and 10
// inclusive.  Invalid input will be ignored.
void set_private(int input);
int get_private();

The source code is pretty straight forward.  It has a single global variable, a setter, and a getter.  For some reason, it is necessary to restrict what the variable is allowed to be, so the setter handles that by ignoring invalid input.  The header file is pretty straight forward as well.  It exposes the two functions but not the variable.  To expose the variable, "extern int private_variable;" could be added to the header file.  Notice also that the comment in the header file does not name the variable.  If this was distributed as a header and a precompiled object file, the user would not be able to figure out the name of the variable without searching though the object file for intelligible text and then guessing.  If the header reveals the name of the variable though, an injudicious user might add an "extern" statement to the header to gain access.  Of course, any user that goes to this effort deserves whatever problems it causes, but there is no reason to make it easy.  Here is a driver program to test the library with.


main.c
#include <stdio.h>
#include "private.h"

void main() {
    printf("Private = %i\n", get_private());
    printf("Setting Private to 10\n");
    set_private(10);
    printf("Private = %i\n", get_private());
    printf("Setting Private to 30\n");
    set_private(30);
    printf("Private = %i\n", get_private());
    printf("Setting Private to 0\n");
    set_private(0);
    printf("Private = %i\n", get_private());
    printf("Setting Private to 7\n");
    set_private(7);
    printf("Private = %i\n", get_private());
}
Try adding some code to access private_variable directly.  It will not compile.  The main program does not even know that variable exists!  It can still change and read the variable indirectly through the setter and getter functions though.

This is not all.  Using this same technique, it is possible to put private functions in the library (perhaps for implementation hiding, or maybe just to keep the namespace uncluttered).  Any function in the library can be called by other functions in the library, but they can only be called externally if the function prototype is included in the header file.  This makes it easy to use the object oriented ideas behind private variables and functions in C.  The library represents the object in this case, and the header file determines what is exposed and what is hidden.

This is not one of the object oriented principles that should be avoided if possible.  This method of encapsulation and data protection is very straight forward.  It is not prone to abuse or errors (and in fact, it is actually designed to reduce the potential for errors).  Most of the rest of this series will discuss less stable and manageable techniques that should be used only when absolutely necessary.

No comments:

Post a Comment