Share

Object Oriented Programming in C

Embedded software development is slowly moving towards object oriented analysis, design and programming. The introduction of object oriented technologies in some systems has not happened due to lack of C++ support on some platforms.

This article focuses on platforms where C++ compilers are not available. The developers working on these platforms should still be able to use object oriented analysis and design. When it comes to final code development they can use C.

The following sections cover an example of C++ code and its implementation in C.

  • C++ Source Code: C++ source files implementing TerminalManager and Terminal classes.
  • C Source Code: TerminalManager and Terminal implemented in C for a platform that does not support C++.

C++ Source Code

Terminal Manager and Terminal C++ header and source files are shown below: 

Terminal Manager

TerminalManager.hpp

// TerminalManager header file. We will be using this class
// as an example for Object Oriented Programming in C.
#include "Terminal.hpp"
 
class TerminalManager
{
private:
    enum {MAX_TERMINALS=500};
 
    Terminal terminals[MAX_TERMINALS];
 
    Terminal *FindTerminal(int terminalId);
 
public:
    TerminalManager();
    ~TerminalManager();
    void HandleMessage(Msg* pMsg);
};

TerminalManager.cpp

// TerminalManager source file. We will be using this class
// as an example for Object Oriented Programming in C.
 
#include <stdio.h>
#include "TerminalManager.hpp"
#include "Msg.hpp"
 
TerminalManager::TerminalManager()
 
{
   //...
}
 
TerminalManager::~TerminalManager()
{
}
 
void TerminalManager::HandleMessage(Msg* pMsg)
{
    int status, status1;
 
    int terminalId = pMsg->GetTerminalId();
 
    Terminal *pTerm = FindTerminal(terminalId);
    Terminal *pOtherTerm = NULL;
 
    if (pTerm != NULL)
    {
        switch (pMsg->GetType())
        {
        case CREATE_TERMINAL:
            pTerm->Activate((const TerminalCreateMsg *)pMsg);
            break;
        case DELETE_TERMINAL:
            pTerm->Deactivate((const TerminalDeleteMsg *) pMsg);
            break;
        case RUN_DIAGNOSTICS:
            status = pTerm->HandleRunDiagnostics((const RunDiagnosticsMsg *) pMsg);
            break;
        case PERFORM_SWITCHOVER:
            pOtherTerm = FindTerminal(pMsg->GetOtherTerminalId());
            status = pTerm->HandleOutOfService();
            status1 = pOtherTerm->HandleInService();
            break;
        }
    }
    delete pMsg;
}
 
Terminal *TerminalManager::FindTerminal(int terminalId)
{
   if (terminalId < MAX_TERMINALS)
   {
       return (&terminals[terminalId]);
   }
   else
   {
       return NULL;
   }
}

Terminal

Terminal.hpp

// Terminal class header file.
 
// Forward declaration for messages
class TerminalCreateMsg;
class TerminalDeleteMsg;
class RunDiagnosticsMsg;
class Msg;
 
// Terminal class
class Terminal
{
    enum { UNKNOWN = 0 };
    enum {OUT_OF_SERVICE=1, INSERVICE=2};
    //...
    int terminalId;
    int terminalType;
    int terminalStatus;
    void SendMessage(Msg *pMsg);
 
public:
    void Activate(const TerminalCreateMsg *pMsg);
    void Deactivate(const TerminalDeleteMsg *pMsg);
    int HandleRunDiagnostics(const RunDiagnosticsMsg *pMsg);
    int HandleOutOfService();
    int HandleInService();
    Terminal();
    ~Terminal();
};

Terminal.cpp

// Terminal class source file.
 
#include "Terminal.hpp"
#include "Msg.hpp"
void Terminal::SendMessage(Msg *pMsg)
{
   //...
}
 
Terminal::Terminal()
{  
    terminalId = UNKNOWN;
    terminalType = UNKNOWN;
    terminalStatus = UNKNOWN;
}
 
Terminal::~Terminal()
{
    //...
}
 
int Terminal::HandleRunDiagnostics(const RunDiagnosticsMsg *pMsg)
{
    int status = 1;
    //...
   return status;
}
 
int Terminal::HandleOutOfService()
{
    int status = 1;
    terminalStatus = OUT_OF_SERVICE;
    //...
    return status;
}
 
int Terminal::HandleInService()
{
   int status = 1;
   terminalStatus = INSERVICE;
   //...
   return status;
}
 
void Terminal::Activate(const TerminalCreateMsg *pMsg)
{
    terminalId = pMsg->GetTerminalId();
    terminalType = pMsg->GetTerminalType();
    terminalStatus = pMsg->GetTerminalStatus();
    //...
 
    TerminalCreateAck *pAck = new TerminalCreateAck(terminalId, terminalStatus);
    SendMessage(pAck);
}
 
void Terminal::Deactivate(const TerminalDeleteMsg *pMsg)
{
    //...
    terminalId = UNKNOWN;
    terminalType = UNKNOWN;
    terminalStatus = UNKNOWN;
    //...
}

Msg.hpp

// Messages used by the Terminal and TerminalManager classes.
 
enum MsgType
{
    CREATE_TERMINAL,
    DELETE_TERMINAL,
    RUN_DIAGNOSTICS,
    PERFORM_SWITCHOVER
};
 
 
class Msg
{
    //...
    int msgType;
    int terminalType;
    int terminalId;
    int otherTerminalId;
    int terminalStatus;
 
public:
    MsgType GetType() const;
    int GetTerminalId() const;
    int GetOtherTerminalId() const;
    int GetTerminalType() const;
};
 
// Message used to create a terminal
class TerminalCreateMsg : public Msg
{
public:
    int GetTerminalStatus() const;
};
 
// Acknowledgement to Terminal Create message.
class TerminalCreateAck : public Msg
{
public:
    TerminalCreateAck(int terminalId, int terminalStatus);
};
 
// Terminal Delete message
class TerminalDeleteMsg : public Msg
{
};

C Source Code

Terminal Manager and Terminal C header and source files are shown below. The important points to note are:

  • Data members of a class map to C structures
  • Methods of a class map to C functions with the "data member" structure pointer as the first parameter.

TerminalManager

TerminalManager.h

/*
TerminalManager header file. We will be using this class
as an example for Object Oriented Programming in C.
*/
 
#include "Terminal.h"
 
#define MAX_TERMINALS 500
 
/* Structure contains all data used by the Terminal Manager. */
typedef struct
{
    Terminal terminals[MAX_TERMINALS];
} TerminalManager;
 
/*
ANSI C Function prototypes of all functions that operate
on the TerminalManager structure.
*/
void TerminalManager_Construct(TerminalManager *pMgr);
void TerminalManager_Destroy(TerminalManager *pMgr);
void TerminalManager_HandleMessage(TerminalManager *pMgr, Msg* pMsg);

TerminalManager.c

/*
TerminalManager source file. We will be using this class
as an example for Object Oriented Programming in C.
*/
 
#include <stdio.h>
#include "TerminalManager.h"
#include "Msg.h"
#include <stdlib.h>
 
Terminal *TerminalManager_FindTerminal(TerminalManager *pMgr, int terminalId)
{
   if (terminalId < MAX_TERMINALS)
   {
       return (&(pMgr->terminals[terminalId]));
   }
   else
   {
       return NULL;
   }
}
 
void TerminalManager_Construct(TerminalManager *pMgr)
{
   int i;
   /*
   C will not call construction functions, so loop through all call the
   construction functions for all terminals.
   */
   for (i=0; i < MAX_TERMINALS; i++)
   {
      Terminal_Construct(&(pMgr->terminals[i]));
   }
}
 
void TerminalManager_Destroy(TerminalManager *pMgr)
{
   int i;
   /*
   C will not call destruction functions, so loop through all call the
   destruction functions for all terminals.
   */
  for (i=0; i < MAX_TERMINALS; i++)
   {
      Terminal_Destroy(&(pMgr->terminals[i]));
   }
}
 
void TerminalManager_HandleMessage(TerminalManager *pMgr, Msg* pMsg)
{
    int status, status1;
 
    int terminalId = pMsg->terminalId;
 
    Terminal *pTerm = TerminalManager_FindTerminal(pMgr, terminalId);
    Terminal *pOtherTerm = NULL;
 
    /*
    Switch on the message type and invoke the Terminal's message handlers for
    the terminal specified in the message. Here the terminal manager takes
    care of indexing into the terminal structure and it passes the pointer
    to the terminal handler functions. Due to this design, implementation
    of the terminal handler functions just focus on handling the specified
    terminal.
    */
    if (pTerm != NULL)
    {
        switch (pMsg->msgType)
        {
        case CREATE_TERMINAL:
            Terminal_Activate(pTerm, (const TerminalCreateMsg *)pMsg);
            break;
        case DELETE_TERMINAL:
            Terminal_Deactivate(pTerm, (const TerminalDeleteMsg *) pMsg);
            break;
        case RUN_DIAGNOSTICS:
            status = Terminal_HandleRunDiagnostics(pTerm, (const RunDiagnosticsMsg *) pMsg);
            break;
        case PERFORM_SWITCHOVER:
            pOtherTerm = TerminalManager_FindTerminal(pMgr, pMsg->otherTerminalId);
            status = Terminal_HandleOutOfService(pTerm);
            status1 = Terminal_HandleInService(pOtherTerm);
            break;
        }
    }
    free(pMsg);
}

Terminal

Terminal.h

/* Terminal struct header file. */
 
#include "Msg.h"
 
#define UNKNOWN 0
#define OUT_OF_SERVICE 1
#define INSERVICE 2
 
/* Terminal struct */
typedef struct
{
    /*...*/
    int terminalId;
    int terminalType;
    int terminalStatus;
} Terminal;
 
/*
Prototypes for Terminal structure related functions. Helper
functions needed by these functions are marked static are not
included here.
*/
 
void Terminal_Activate(Terminal *pTerm, const TerminalCreateMsg *pMsg);
void Terminal_Deactivate(Terminal *pTerm, const TerminalDeleteMsg *pMsg);
int Terminal_HandleRunDiagnostics(Terminal *pTerm, const RunDiagnosticsMsg *pMsg);
int Terminal_HandleOutOfService(Terminal *pTerm);
int Terminal_HandleInService(Terminal *pTerm);
void Terminal_Construct(Terminal *pTerm);
void Terminal_Destroy(Terminal *pTerm);

Terminal.c

/* Terminal struct source file. */
 
#include "Terminal.h"
#include "Msg.h"
#include <stdlib.h>
 
/*
Terminal_SendMessage is not visible to outside the Terminal.c file.
This is equivalent to a private method in C++.
*/
 
static void Terminal_SendMessage(Terminal *pTerm, Msg *pMsg)
{
    /*...*/
}
 
void Terminal_Construct(Terminal *pTerm)
{  
    pTerm->terminalId = UNKNOWN;
    pTerm->terminalType = UNKNOWN;
    pTerm->terminalStatus = UNKNOWN;
}
 
void Terminal_Destroy(Terminal *pTerm)
{
    /*...*/
}
 
int Terminal_HandleRunDiagnostics(Terminal *pTerm, const RunDiagnosticsMsg *pMsg)
{
    int status = 1;
    /*...*/
   return status;
}
 
int Terminal_HandleOutOfService(Terminal *pTerm)
{
    int status = 1;
    pTerm->terminalStatus = OUT_OF_SERVICE;
    /*...*/
    return status;
}
 
int Terminal_HandleInService(Terminal *pTerm)
{
   int status = 1;
   pTerm->terminalStatus = INSERVICE;
   /*...*/
   return status;
}
 
void Terminal_Activate(Terminal *pTerm, const TerminalCreateMsg *pMsg)
{
    TerminalCreateAck *pAck;
    pTerm->terminalId = pMsg->header.terminalId;
    pTerm->terminalType = pMsg->header.terminalType;
    pTerm->terminalStatus = pMsg->header.terminalStatus;
    /*...*/
 
    pAck = (TerminalCreateAck *)malloc(sizeof(TerminalCreateAck));
    pAck->header.terminalId = pTerm->terminalId;
    pAck->header.terminalStatus = pTerm->terminalStatus;
 
    Terminal_SendMessage(pTerm, (Msg *)pAck);
}
 
void Terminal_Deactivate(Terminal *pTerm, const TerminalDeleteMsg *pMsg)
{
    /*...*/
    pTerm->terminalId = UNKNOWN;
    pTerm->terminalType = UNKNOWN;
    pTerm->terminalStatus = UNKNOWN;
    /*...*/
}

Msg.h

/* Messages used by the Terminal and TerminalManager classes. */
 
#ifndef MSG_H
#define MSG_H
 
enum MsgType
{
    CREATE_TERMINAL,
    DELETE_TERMINAL,
    RUN_DIAGNOSTICS,
    PERFORM_SWITCHOVER
};
 
typedef struct
{
    /*...*/
    int msgType;
    int terminalType;
    int terminalId;
    int otherTerminalId;
    int terminalStatus;
} Msg;
 
/* Message used to create a terminal. */
typedef struct
{
    Msg header;
}TerminalCreateMsg;
 
/* Acknowledgement to Terminal Create message. */
typedef struct
{
    Msg header;
} TerminalCreateAck;
 
/* Terminal Delete message */
typedef struct
{
    Msg header;
} TerminalDeleteMsg;
 
 
typedef struct
{
    Msg header;
} RunDiagnosticsMsg;
#endif

Explore More