OOSMOS Coding Conventions
1. Naming Conventions
2. Enumerators
3. Constants vs. Variables
4. Program Organization
5. OOSMOS Encapsulation
6. Perspective On Encapsulation
The following are the coding conventions used to develop OOSMOS and the examples. We strongly recommend that you use them as well, even for non-OOSMOS projects, where applicable.

1. Naming Conventions

1.1 Class Member Prefix

The name of each member of a class is prefixed with 'm_'.
1
2
3
4
5
6
7
8
 
struct pinTag
{
pin_eLogic m_Logic;
unsigned m_MemberA;
unsigned m_MemberB;
};
 
Member Name Examples

1.2 Pointer Prefix

Names of pointer variables are prefixed with a lowercase 'p'.
1
2
3
4
5
 
char * pString = "abc";
 
char Char = *pString;
 
Pointer Prefix Example

1.3 Enumeration Type Prefix

Names of enumeration types are prefixed with a lowercase 'e'.
1
2
3
4
5
6
7
 
typedef enum
{
evPressed = 1,
evReleased
} eStates;
 
Enumeration Type

1.4 External Name Prefixes and Filenames

We follow these important rules for externally visible names:
  1. Carefully choose short, descriptive filenames.
  2. Prefix every externally visible name with the root of that filename.
  3. The filename also becomes the type name of the object.
1
2
3
4
5
6
7
 
//
// The filenames are 'pin.h' and 'pin.c', therefore the
// main type of the object is 'pin'.
//
typedef struct pinTag pin;
 
Interface in pin.h File
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
struct pinTag
{
// Member declarations...
};
 
extern pin * pinNew()
{
static pin Pins[1];
pin * pPin = Pins;
 
return pPin;
}
 
Implementation in pin.c File

1.5 Explicit Linkage Specification

We always specify the linkage for file scope names, using static for internal linkage and extern for external linkage, even though extern is the default linkage, we still specify it for consistency and clarity.
Note that using the static keyword at file scope keeps a name local to the file and thus prevents polluting the global namespace. Also note that because static symbols are local to the file, they don't need the filename root prefix.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
//
// The name 'Internal' has internal linkage and does not
// require the filename prefix.
//
static pin * Internal()
{
static pin PinList[1];
return PinList;
}
 
//
// The name 'pinNew' has external linkage, thus requires the
// filename prefix.
//
extern pin * pinNew()
{
return Internal();
}
 
Implementation in pin.c File

1.6 State Thread and Polling Function Names

While you are in a state, an oosmos_POLL event is called as fast as the OOSMOS control loop can call it. You can either do work at that rate, or call a state thread function that does sequential work. If you do call a state thread function, we recommend suffixing the state thread name with the string Thread. For example:
1
82
83
84
85
86
87
88
89
90
91
92
93
[...]
 
static void FlashingThread(oosmos_sState * pState)
{
oosmos_ThreadBegin();
for (;;) {
printf("Flashing...\n");
oosmos_ThreadDelayMS(50);
}
oosmos_ThreadEnd();
}
 
[...]
NestedStateThreads.c
If you are not going to call a state thread during an oosmos_POLL event, then we recommend calling a function that ends with the string Poll instead.

2. Enumerators

Unless there is a compelling reason to do otherwise, we do not let the first enumerator default to zero. This is a defensive programming technique.
1
2
3
4
5
6
7
 
typedef enum
{
pinActiveHigh = 1,
pinActiveLow
} pin_eLogic;
 
Safe Enumerations

3. Constants vs. Variables

We distinguish between variables and constants. Variables, as the name indicates, can vary (be changed). Names that are declared const cannot (once they are initially assigned). We feel that it is good programming practice to make the distinction as it tells your reader something they would have to discover by reading the rest of the code. Further, depending on the compiler, local names that are declared const may not actually be materialized and thus may take no memory space.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
static void InterrogateColumns(matrix * pMatrix)
{
const unsigned RowIndex = pMatrix->m_CurrentRowIndex;
const unsigned Columns = pMatrix->m_Columns;
 
for (unsigned ColumnIndex = 0; ColumnIndex < Columns; ColumnIndex++) {
sw * pSwitch = pMatrix->m_pSwitch[RowIndex][ColumnIndex];
 
if (pSwitch == NULL) {
continue;
}
 
swRunStateMachine(pSwitch);
}
}
 
Use of the const keyword

4. Program Organization

We use the following program structure for OOSMOS programs.

4.1 File Structure

An OOSMOS object should have two files: an interface file in a .h file and an implementation file in either a .c or .cpp file depending on your environment.
Refer to the following two code blocks and expand them to view the underlying code.
1
22
23
25
26
28
29
30
31
32
33
[GPLv2]
 
[Multiple Inclusion Check Begin]...
 
[#includes]...
 
[Class Type Declaration]...
 
[Interface Prototypes]...
 
[Multiple Inclusion Check End]...
Interface (toggle.h)
1
22
23
29
30
33
34
46
47
48
84
85
86
[GPLv2]
 
[#includes]...
 
[Object Allocation Configuration]...
 
[Class Type Definition]...
 
//>>>CODE
[Generated Functions]...
//<<<CODE
 
[External Functions]...
Implementation (toggle_state.c or toggle_state.cpp)

4.2 Object Allocation Structure

The next two code blocks show in more detail how to organize your code in order to declare, allocate and initialize an object. In this case, a toggle object.
1
2
3
6
7
10
struct toggleTag
{
[State Machine Declaration]...
 
[Member Declarations]...
};
Type Declaration Structure
1
2
3
4
5
10
11
14
15
16
extern toggle * toggleNew(pin * pPin, const unsigned TimeOnMS, const unsigned TimeOffMS)
{
[Object Allocation]...
 
[State Machine Initialization]...
 
[Member Initialization]...
 
[Return Object Pointer]...
}
Object Allocation and Initialization

5. OOSMOS Encapsulation

OOSMOS is written in C, a non-object-oriented language, yet OOSMOS promotes one of the most important elements of object-oriented programming: encapsulation. In fact, we contend that what is presented here is a form of encapsulation that is superior to what can be achieved by classically programmed C++. We call it the OOSMOS encapsulation pattern.
Refer to Figure 1 as we cover the following four areas of the pattern:
  • File naming conventions.
  • Type declarations and information hiding.
  • Names and name visibility.
  • Object allocation.

5.1 File Naming Conventions

An OOSMOS object is always made up of a .h file that holds the interface and a .c file that holds the implementation. We use short yet descriptive names for these files. The names must be all lowercase. It is important to choose these names wisely because they have a greater role to play in the pattern — they are also used to form all external names of the object. See the section below on Names and Name Visibility.

5.2 Type Declarations and Information Hiding

The object's type name must be the root filename of the object. In this case, sample.
To implement this pattern, we use the incomplete type capability of C, declaring sample like this:
1
2
3
 
typedef struct sampleTag sample;
 
Snippet 1. sample.h
Because the content of struct sample is not yet defined, its size is not known, so users of the sample interface can only work with pointers to sample returned from sampleNew.
The completion of type sample is hidden in the implementation file sample.c. (See Snippet 2.)
1
2
3
4
5
6
7
8
 
struct sampleTag
{
// Declare class members.
 
unsigned m_Temperature;
};
 
Snippet 2. sample.c

5.3 Names and Name Visibility

Every external name that you declare in the object's header file must be prefixed with the root of the object's filename. For example, from Figure 1, we see that sampleGetTemp and sampleNew both begin with the filename root sample. Additionally, the name of the object's type must be the filename root. In this example, sample.

5.4 Object Allocation

Because only an incomplete type is declared in the interface, the user cannot allocate an object directly. Instead, they must call the object's "New" function. In this case sampleNew. This will return a pointer to a sample object that the user can then pass as the "this" pointer to methods that require access to the object. See sampleGetTemp on line 18 as an example.
1
2
3
4
5
11
12
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "sample.h"
#include "oosmos.h"
#include <stdint.h>
 
struct sampleTag {...}
 
[File-local symbols]...
 
extern int sampleGetTemp(sample * pSample)
{
LocalFunc(pSample, 10);
return pSample->m_Temperature;
}
 
extern sample * sampleNew(void)
{
oosmos_Allocate(pSample, sample, 3, NULL)
 
// Initialize members of the allocated object...
 
pSample->m_Temperature = 0;
return pSample;
}
sample.c

6. Perspective On Encapsulation

Figure 1. Perspective on Encapsulation
Copyright © 2014-2023  OOSMOS, LLC