OOSMOS User's Guide
1. Object-Oriented + State Machine + Operating System
2. States
3. Events
4. Queues
5. Object Allocation
6. The OOSMOS Name Space
7. Lint
8. Lines of C Code

1. Object-Oriented + State Machine + Operating System

OOSMOS stands for:
  • Object-Oriented.
  • State Machine.
  • Operating System.
OOSMOS takes a novel approach to all three of these characteristics, described below.

1.1 Object-Oriented

OOSMOS conventions promote strict encapsulation and information hiding.
Example files module.h and module.c, below, show how it is done.
The important line is line 5 in module.h. This is a typedef that declares a type called module that is of incomplete structure type struct moduleTag. This line makes the type module accessible to the user but not its size, so only pointers of type module can be created.
Now refer to line 5 of file module.c. This declaration completes the type, but it is inside the implementation file module.c, not the user-facing interface file module.h. That way, all the implementation details are hidden from the user. We think this approach is superior to the way a customary C++ class is organized. See section C vs C++ Encapsulation below for an example.
1
2
3
4
5
6
7
8
9
10
 
#ifndef module_h
#define module_h
 
typedef struct moduleTag module;
 
extern module * moduleNew(void);
 
#endif
 
module.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
#include <stdlib.h>
#include "module.h"
 
struct moduleTag {
int m_Member;
};
 
extern module * moduleNew(void)
{
module * pModule = malloc(sizeof(module));
 
pModule->m_Member = 0;
 
return pModule;
}
 
module.c

1.1.1 C vs C++ Encapsulation

Here is an example of how the OOSMOS encapsulation structure is superior to the same thing written in classic C++.

1.1.1.1 C++

File module.hpp, below, is a C++ header file that defines a simple interface to a class called module. Note that the implementation of module in file module.cpp needs to retain a Windows handle in the class. Accordingly, a private member m_Handle is added in module.hpp in a private section of the class. Because we refer to type HWND, we need to include Windows.h. This is very unfortunate for at least two reasons:
  1. Because Windows.h is included in the module.h header file, anyone that includes module.h will now get their namespace polluted by all the symbols in Windows.h.
  2. Any change to the implementation that necessitates a change to the size or structure of the class will ripple into the user space, requiring a recompile of all dependent source files.
Now skip ahead and let's take a look at the same thing in OOSMOS-structured C.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
#ifndef module_hpp
#define module_hpp
 
#include "Windows.h"
 
class module
{
public:
void Init();
 
private:
HWND m_Handle;
};
 
#endif
 
module.hpp
1
2
3
4
5
6
7
8
9
 
#include <stddef.h>
#include "module.hpp"
 
void module::Init()
{
m_Handle = NULL;
}
 
module.cpp

1.1.1.2 C

Now refer to C files module.h and module.c, below.
In OOSMOS-structured C, we do not include Windows.h in the header file. There is no need because all the implementation details are maintained within module.c, so module.c includes Windows.h, not module.h.
Therefore, the user's namespace is not polluted with symbols they shouldn't see and when the implementation changes, only module.c needs to be changed and only that file needs to be recompiled.
1
2
3
4
5
6
7
8
9
10
 
#ifndef module_h
#define module_h
 
typedef struct moduleTag module;
 
extern void moduleInit(module * pModule);
 
#endif
 
module.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
#include <stddef.h>
#include "module.h"
 
#include "Windows.h"
 
struct moduleTag
{
HWND m_Handle;
};
 
extern void moduleInit(module * pModule)
{
pModule->m_Handle = NULL;
}
 
module.c

1.2 State Machine

OOSMOS defines three types of objects:
  • Passive objects are traditional objects that have no asynchronous behavior and thus do not require a state machine.
    A good illustration of a passive object is the LinearRegression program in the Examples directory.
  • Active objects have very simple asynchronous behavior requirements and don't require the features provided by a full hierarchical state machine. Instead, they register the address of a callback function to call each time through the OOSMOS control loop.
    A good illustration of an Active object is the switch class called sw in the Classes directory.
  • State Machine objects are built around the full hierarchical state machine capability of OOSMOS which gives you the ability to nest states, specify state entry and exit code and use orthogonal states also known as "and" states.
    In addition, State Machine objects support State Threads that allow you to write code much like you would in an RTOS threaded operating system. That is, as a continuous sequence of calls that perform asynchronous operations. (See the threads example.)

1.3 Operating System

All operating systems are endless loops. OOSMOS is no different in this respect. Traditionally, the main loop is implemented inside the OS. In OOSMOS, the main loop is in user space in order to provide maximum control and visibility.
OOSMOS can be used in one of two ways:
  1. As the one-and-only operating system in an environment that doesn't already have an operating system.
    This is sometimes called a "bare board." In this environment the main loop can take 100% of the CPU because it is not sharing the processor with any other entity.
    Examples are Arduino and PIC32.
  2. Cooperatively with an existing operating system.
    On these platforms, we cannot assume that we "own" the CPU, so we take this into account in our loop. Specifically, in the loop, we run all the state machines in the system and then we relinquish control for a period of time. We like to think of this as periodically "taking a sip" from the processor. The amount of time to delay will differ from milliseconds to possibly multiple seconds depending on how responsive you need your OOSMOS objects to be.
    Examples are Windows and Linux.

1.3.1 Bare Board Loop Examples

For an environment where you control the main program and own the CPU, you will call oosmos_RunStateMachines in a tight loop dedicating 100% of the CPU to OOSMOS objects. Your system will be maximally responsive in this situation.
1
2
3
4
5
6
7
8
9
10
11
12
 
extern int main(void)
{
//
// Create objects, many of which will have state machines...
//
 
for (;;) {
oosmos_RunStateMachines();
}
}
 
Has control of main()
The same loop applies to Arduino-style environments, conforming naturally to Arduino's setup and loop callbacks.
1
2
3
4
5
6
7
8
9
10
11
12
13
 
extern void setup()
{
//
// Create objects, many of which will have state machines...
//
}
 
extern void loop()
{
oosmos_RunStateMachines();
}
 
setup() and loop() environment

1.3.2 Running OOSMOS Under Another Operating System

When OOSMOS is run under another operating system like Linux or Windows, it would be impolite to consume 100% of the CPU. Instead, we periodically "take a small sip" from the processor and then issue a wait for a period of time.
1
2
3
4
5
6
7
8
9
10
11
12
13
 
extern int main(void)
{
//
// Create objects, many of which will have state machines...
//
 
for (;;) {
oosmos_RunStateMachines();
oosmos_DelayMS(1);
}
}
 
OOSMOS on Windows or Linux

2. States

States are where your objects spend time. Typically you will execute some code as you enter a state and then stay in the state waiting for an event to occur or a condition to be satisfied.
The names of your states should reflect what the object is doing while it is waiting. Good examples: Running, Initializing, Connecting; that is, words that end in ing. There are also states that will reflect a physical reality. For example, On, Moving, Latched, or Open.
An example where you might use both types of names is where you may have an outer state that is Ready and an inner state called Injecting.

2.1 State Threads

OOSMOS state threads allow you to program much like you would using an RTOS thread. That is, in a natural, sequential manner.
For example, if you wanted to toggle a few LEDs all at different rates using RTOS threads, you'd likely create a thread per LED so you could write the following natural pseudo-code:
1
2
3
4
5
6
7
8
9
 
for (;;) {
pinOn(MyPin);
delay(OnTime);
 
pinOff(MyPin);
delay(OffTime);
}
 
RTOS Threaded toggle Example (pseudo code)
Keep in mind that RTOS threads have a stack per thread. That's a lot of overhead just to toggle a few LEDs.
Alternatively, using OOSMOS object threads, you can program in a similarly natural way but without the memory and processor overhead that comes with using RTOS threads and their stacks. Take a look at lines 44-58 below (from the toggle class) to see how you can do the same thing in OOSMOS with an object thread.
1
22
23
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
[GPLv2]
 
[...]
 
static void ToggleThread(const toggle * pToggle, oosmos_sState * pState)
{
oosmos_POINTER_GUARD(pToggle);
oosmos_POINTER_GUARD(pState);
 
oosmos_ThreadBegin();
for (;;) {
pinOn(pToggle->m_pPin);
oosmos_ThreadDelayMS(pToggle->m_TimeOnMS);
 
pinOff(pToggle->m_pPin);
oosmos_ThreadDelayMS(pToggle->m_TimeOffMS);
}
oosmos_ThreadEnd();
}
 
 
toggle's use of oosmos_ThreadDelayMS.
Note that the code reads about the same as in the RTOS-threaded example, but only with the modest overhead of the memory used by the state.
The underlying structure of state threads is based on protothreads by Adam Dunkels.
The full set of OOSMOS State Thread APIs are: See threads on the Examples page to see an example usage of each one.

2.2 State Thread API Rules and Restrictions

  • The pState argument from the event handling function must be passed to the thread and it must be called pState.
  • All State Thread APIs must be bracketed by oosmos_ThreadBegin and oosmos_ThreadEnd calls.
  • There may be only one thread per state.
  • Local variables have a limited lifetime. Any control variables (like loop counters) cannot be on the stack, they must reside in the object. For an example, see the implementation of the matrix class.
  • No switch statements can be used inside the oosmos_ThreadBegin and oosmos_ThreadEnd calls. Use if/then/else instead.

2.3 History States

Both shallow and deep history are supported by OOSMOS.
When a shallow history state is entered, it immediately enters the last active state at the same level where the shallow history symbol is drawn. When a deep history state is entered, it enters the last active leaf state of the last active state at the same level.
History State Chart Example (from oosmos/Tests/History.uxf)
On Windows, go to the oosmos/Tests directory and then build and run the History.exe executable. Following the state chart above, press and release the s key to exercise the shallow history logic. Press and release the d key to run the deep history logic. Pressing the q key ends the program.

3. Events

Events allow your states to react. There are a small number of events that are predefined and generated internally from within OOSMOS, like oosmos_TIMEOUT and oosmos_ENTER. Then there are events that you create as you draw transitions in your state charts. These events may be generated internally (e.g. oosmos_PushEventCode(pObject, evCode), or generated by event publishers that you may have subscribed to. For example, you can subscribe to the Open and/or Close events of a switch object. (See the switch example).

3.1 Built-In Events

OOSMOS predefines six events that it generates internally. The following sections explain what each of them does, along with an example.

3.1.1 oosmos_COMPLETE

This event code is generated when all inner states have entered a Final state.
Here is an example usage. First the Statechart...
Figure 1. Completion State Chart
And then the code snippet...
1
22
23
71
72
73
74
75
76
77
78
79
80
81
82
83
[GPLv2]
 
[...]
static bool Ortho_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent)
{
test * pTest = (test *) pObject;
 
switch (oosmos_EventCode(pEvent)) {
case oosmos_COMPLETE: {
return oosmos_Transition(pTest, pState, Complete_State);
}
}
 
return false;
}
[...]
oosmos/Examples/Ortho/Windows/Completion.c

3.1.2 oosmos_DEFAULT

This event code is generated once for the default state before the state is entered.

3.1.3 oosmos_ENTER

The oosmos_ENTER event is generated once on entry to a state.
Here is an example usage from the toggle class:
1
22
23
48
49
50
51
52
53
54
55
56
57
58
59
60
[GPLv2]
 
[...]
static bool Off_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent)
{
toggle * pToggle = (toggle *) pObject;
 
switch (oosmos_EventCode(pEvent)) {
case oosmos_ENTER: {
return oosmos_StateTimeoutMS(pState, (uint32_t) pToggle->m_TimeOffMS);
}
case oosmos_TIMEOUT: {
return oosmos_Transition(pToggle, pState, On_State);
}
}
[...]
oosmos/Classes/toggle_state.c

3.1.4 oosmos_EXIT

The oosmos_EXIT event is generated once on exit from a state.
Here is an example usage from the toggle class:
1
22
23
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
[GPLv2]
 
[...]
static bool On_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent)
{
toggle * pToggle = (toggle *) pObject;
 
switch (oosmos_EventCode(pEvent)) {
case oosmos_ENTER: {
pinOn(pToggle->m_pPin);
return oosmos_StateTimeoutMS(pState, (uint32_t) pToggle->m_TimeOnMS);
}
case oosmos_EXIT: {
pinOff(pToggle->m_pPin);
return true;
}
case oosmos_TIMEOUT: {
return oosmos_Transition(pToggle, pState, Off_State);
}
}
 
return false;
}
[...]
oosmos/Classes/toggle_state.c

3.1.5 oosmos_POLL

This event code is generated continuously for all current states in the system. You can use this event to poll for a particular condition, or you can use the powerful State Thread APIs.
Convention recommendation: If you call a function that uses State Threads, we suggest that you use a function name that ends in Thread. Otherwise, we suggest that the function name end with Poll.
1
22
23
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
[GPLv2]
 
[...]
 
static bool Ortho_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent)
{
test * pTest = (test *) pObject;
 
switch (oosmos_EventCode(pEvent)) {
case oosmos_POLL: {
OrthoPoll(pTest);
if (CheckCounts(pTest)) {
return oosmos_Transition(pTest, pState, TestNullTransitionWithPoll_State);
}
return true;
}
}
 
return false;
}
 
[...]
oosmos/Tests/Poll.c

3.1.6 oosmos_TIMEOUT

The oosmos_TIMEOUT event is generated when the state's timeout has expired. You must establish the state's timeout upon entry to the state as shown in line 54 of code Snippet 4 below.
Here is an example from the toggle class:
1
22
23
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
[GPLv2]
 
[...]
static bool Off_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent)
{
toggle * pToggle = (toggle *) pObject;
 
switch (oosmos_EventCode(pEvent)) {
case oosmos_ENTER: {
return oosmos_StateTimeoutMS(pState, (uint32_t) pToggle->m_TimeOffMS);
}
case oosmos_TIMEOUT: {
return oosmos_Transition(pToggle, pState, On_State);
}
}
 
return false;
}
[...]
Snippet 4: oosmos/Classes/toggle_state.c

3.2 Publish/Subscribe Events

Publish/Subscribe events allow an object to subscribe to the published events of another object. Event code numbers are locally owned by the subscriber (lines 37-43) and are passed to the publisher object when the subscriber subscribes to published events (lines 241-266).
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
293
294
295
296
[GPLv2]
 
#include "oosmos.h"
#include "control.h"
#include "motor.h"
#include "pump.h"
#include "pin.h"
#include "sw.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
 
 
//>>>EVENTS
enum {
evMovePressed = 1,
evOption1Pressed = 2,
evOption2Pressed = 3,
evPumpPressed = 4,
evQuitPressed = 5,
evStopPressed = 6,
evStopReleased = 7
};
 
[Debug Support...]
//<<<EVENTS
 
typedef union {
oosmos_sEvent Event;
} uEvents;
 
struct controlTag
{
motor * m_pMotor;
pump * m_pPump;
bool m_Option1;
bool m_Option2;
 
//>>>DECL
[Code Generated by OOSMOS]
//<<<DECL
};
 
static void ToggleOption1(control * pControl)
{
pControl->m_Option1 = !(pControl->m_Option1);
oosmos_DebugPrint("Option1: %d\n", pControl->m_Option1);
}
 
static void ToggleOption2(control * pControl)
{
pControl->m_Option2 = !(pControl->m_Option2);
oosmos_DebugPrint("Option2: %d\n", pControl->m_Option1);
}
 
//>>>CODE
[Code Generated by OOSMOS]
//<<<CODE
 
extern control * controlNew(void)
{
oosmos_Allocate(pControl, control, 1, NULL);
 
oosmos_sQueue * const pControlEventQueue = &pControl->m_EventQueue;
 
pin * pStopPin = pinNew('s', pinActiveHigh);
sw * pStopSwitch = swNew(pStopPin);
swSubscribeCloseEvent(pStopSwitch, pControlEventQueue, evStopPressed, NULL);
swSubscribeOpenEvent(pStopSwitch, pControlEventQueue, evStopReleased, NULL);
 
pin * pMovePin = pinNew('m', pinActiveHigh);
sw * pMoveSwitch = swNew(pMovePin);
swSubscribeCloseEvent(pMoveSwitch, pControlEventQueue, evMovePressed, NULL);
 
pin * pQuitPin = pinNew('q', pinActiveHigh);
sw * pQuitSwitch = swNew(pQuitPin);
swSubscribeCloseEvent(pQuitSwitch, pControlEventQueue, evQuitPressed, NULL);
 
pin * pPumpPin = pinNew('p', pinActiveHigh);
sw * pPumpSwitch = swNew(pPumpPin);
swSubscribeCloseEvent(pPumpSwitch, pControlEventQueue, evPumpPressed, NULL);
 
pin * pOption1Pin = pinNew('1', pinActiveHigh);
sw * pOption1Switch = swNew(pOption1Pin);
swSubscribeCloseEvent(pOption1Switch, pControlEventQueue, evOption1Pressed, NULL);
 
pin * pOption2Pin = pinNew('2', pinActiveHigh);
sw * pOption2Switch = swNew(pOption2Pin);
swSubscribeCloseEvent(pOption2Switch, pControlEventQueue, evOption2Pressed, NULL);
 
pin * pUpPin = pinNew('u', pinActiveHigh);
sw * pUpSwitch = swNew(pUpPin);
 
pin * pDownPin = pinNew('d', pinActiveHigh);
sw * pDownSwitch = swNew(pDownPin);
 
pControl->m_pMotor = motorNew();
pControl->m_pPump = pumpNew(pUpSwitch, pDownSwitch);
pControl->m_Option1 = false;
pControl->m_Option2 = false;
 
//>>>INIT
[Code Generated by OOSMOS]
//<<<INIT
 
return pControl;
}
Snippet 5. Publish/Subscribe Event Pattern

4. Queues

OOSMOS uses queues to serialize work for an object and supports two types of queues: Event queues and Work queues.

4.1 Event Queues

Event queues are used by OOSMOS to post events to an object's state machine. The state machine reacts to events based on the current state. If the current state (or any of its parents) does not handle the event, the event is thrown away. This is a fundamental difference between event queues and work queues.
Some objects, like a button object, can publish events as they occur. Other objects subscribe to that object in order to receive those events.
The picture below shows the mechanics of how subscribers interact with a publisher (a btn object).
Publish/Subscribe Event Queue Pattern

4.2 Work Queues

Work queues are not directly associated with states. Data remains in the queue until some logic within your object pops it off the queue in order to act on it. You can think of a work queue as you might think of a server. Work is queued up to be processed at a later time. OOSMOS does nothing implicitly; you push the work and pop it when your logic dictates.
An example of the use of a work queue is a UART interrupt handler pushing a received character to the associated UART object for processing once the interrupt handler returns and the OOSMOS main loop runs the UART object's state machine. (See oosmos/Classes/PIC32/uart.c.)

4.2.1 Using a Work Queue To Make An Asynchronous Call

The following code shows how to create an asynchronous call. Note that this example shows a state-aware call in that if pumpOn is called when the pump is in the Pumping state, the call will have no effect.
Pump State Chart
1
22
23
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
140
141
142
143
144
145
146
147
148
149
[GPLv2]
 
[...]
static bool Idle_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent)
{
pump * pPump = (pump *) pObject;
 
switch (oosmos_EventCode(pEvent)) {
case evStart: {
return oosmos_Transition(pPump, pState, Pumping_State);
}
}
 
return false;
}
 
static bool Pumping_State_Code(void * pObject, oosmos_sState * pState, const oosmos_sEvent * pEvent)
{
pump * pPump = (pump *) pObject;
 
switch (oosmos_EventCode(pEvent)) {
case oosmos_POLL: {
Thread(pPump, pState);
return true;
}
case evUpPressed: {
pPump->PumpSpeed = oosmos_Min(10, pPump->PumpSpeed+1);
return true;
}
case evDownPressed: {
pPump->PumpSpeed = oosmos_Max(1, pPump->PumpSpeed-1);
return true;
}
case evStop: {
return oosmos_Transition(pPump, pState, Idle_State);
}
}
 
return false;
}
{...}
 
extern void pumpOn(const pump * pPump)
{
oosmos_PushEventCode(pPump, evStart);
}
 
extern void pumpOff(const pump * pPump)
{
oosmos_PushEventCode(pPump, evStop);
}
Snippet 6. Asynchronous Call Pattern

5. Object Allocation

Commonly, dynamic memory allocation is done using malloc and free in C programs and new and delete in C++ programs. There are a number of problems with using these standard library functions, namely:
  1. Sudden and unpredictable memory exhaustion.
  2. Unpredictable allocation and deallocation times due to memory fragmentation.
  3. Reuse of memory blocks could lead to security concerns unless freed memory is cleared.
  4. Careless coding can lead to memory leaks or references to freed memory.
  5. In memory constrained environments:
    1. Non-trivial functions like malloc and free take up precious code space.
    2. Managing variable-sized heap space requires additional housekeeping memory.
    3. To avoid sudden memory exhaustion, typically much more memory is allocated to the heap than is actually required which is wasteful.

5.1 OOSMOS Object Allocation

OOSMOS uses a different dynamic memory allocation scheme which is ideal for memory constrained environments. It has the following attributes and benefits:
  1. All allocation is done at the beginning of the program. There are no calls to "free" memory.
  2. Allows you to allocate exactly the number of objects required. No wasted space.
  3. Allocation is done in a one-line macro.
  4. Hides the space allocation within the scope of the object's special "New" function.
  5. Extremely fast, constant-time allocation.

5.1.1 Example

The following code snippet contains a typical OOSMOS object allocation. In this example, we implement the "New" function for the toggle object. It behaves very much like the new function in C++, except that we will orchestrate the allocation of memory; in C++ that is handled automatically by the language and run-time.
Per our naming conventions, the name of this external function is toggleNew. Its job is to allocate an object of type toggle, then initialize the members of the object.
The allocation macro is called oosmos_Allocate, which takes four arguments:
  1. pToggle is the name of pointer variable that will be set to point to the allocated object.
  2. toggle is the type of the object to be allocated.
  3. toggleMAX specifies the number of objects to pre-allocate.
  4. OutOfMemory which is set to NULL in this example, is the address of a subroutine to call if memory is exhausted. NULL specifies that oosmos_Allocate should loop forever when memory is exhausted.
1
2
5
6
7
8
9
10
15
16
19
20
21
22
23
 
[Object Allocation Configuration]...
 
extern toggle * toggleNew(pin * pPin, uint32_t TimeOnMS, uint32_t TimeOffMS)
{
[Object Allocation]...
 
[State Machine Initialization]...
 
[Member Initialization]...
 
[Return Object Pointer]...
}
 
 
Object Allocation and Initialization

5.1.2 Alternative Allocation

If you are not in a memory-constrained environment where every byte of RAM matters, then you can replace the oosmos_Allocate with a call to malloc instead. Then you would also want to create a toggleDelete function in your object's interface that accepts the object pointer as the only argument and then calls free.

6. The OOSMOS Name Space

OOSMOS is a library; possibly one of many libraries in an application. Therefore it is important that OOSMOS not pollute the global namespace. Accordingly, we prefix each externally visible name with either oosmos_ or OOSMOS_. Names that start with oosmos_ are part of the formal user-facing API. Names that begin with OOSMOS_ are names that are part of the internal private API and should not be used directly by your applications, as they may someday change.

7. Lint

OOSMOS has been fully linted using Gimpel's PC-Lint.
In directory oosmos\LINT, run lint-oosmos.py to LINT only OOSMOS, and lint-all.py to LINT OOSMOS as well as the examples and tests.
You can use the oosmos-user.lnt file to run against your code to limit LINT messages to only your code and not OOSMOS.

8. Lines of C Code

The core of OOSMOS is small — only two source files, oosmos.h and oosmos.c, and is around 1750 lines of code combined.
1
2
3
4
5
6
7
8
9
10
11
12
13
 
> cloc oosmos.h oosmos.c
 
github.com/AlDanial/cloc v 1.80 T=0.50 s (4.0 files/s, 4928.0 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
C 1 362 107 1243
C/C++ Header 1 140 114 498
-------------------------------------------------------------------------------
SUM: 2 502 221 1741
-------------------------------------------------------------------------------
 
C Lines of Code
Copyright © 2014-2019  OOSMOS, LLC