C Runtime Library

This section contains detailed info about Legato's C Language library used for low-level routines like commonly used data structures and OS services APIs.


The C APIs' Overview has high-level info.


Basic Type and Constant Definitions
Command Line Arguments API
Atomic File Operation API
CRC32 API
Directory API
Doubly Linked List API
Dynamic Memory Allocation API
Event Loop API
File Descriptor Monitor API
File Locking API
File System service
HashMap API
Hex string API
JSON Parsing API
Logging API
Low-level Messaging API
Mutex API
Low-level Pack/Unpack API
Path API
Path Iterator API
Print APIs
Random Number API
Safe References API
Semaphore API
Signals API
Singly Linked List API
System Clock API
Thread Control API
Timer API
Unit Testing API
UTF-8 String Handling API
tty API

Overview

Here is some background info on Legato's C Language APIs.

Design

The Legato framework is constructed in an object-oriented manner.

The C programming language was created before object-oriented programming was popular so it doesn't have native support for OOP features like inheritance, private object members, member functions, and overloading. But object-oriented designs can still be implemented in C.

In the Legato C APIs, classes are hidden behind opaque "reference" data types. You can get references to objects created behind the scenes in Legato, but you can never see the structure of those objects. The implementation is hidden from view. Access to object properties is made available through accessor functions.

Types

The basic "opaque data type" offered by the C programming language is the "void pointer" (void *). The idea is that a pointer to an object of type T can be cast to point to a void type before being passed outside of the module that implements T.

This makes it impossible for anyone outside of the module that implements T to dereference the pointer or access anything about the implementation of T. This way, the module that implements T is free to change the implementation of T in any way needed without worrying about breaking code outside of that module.

The problem with the void pointer type is that it throws away type information. At compile time, this makes it impossible to detect that a variable with opaque type T has been passed into a function with some other pointer type P.

To overcome this, Legato uses "incomplete types" to implement its opaque types. For example, there are declarations similar to the following in Legato C API header files:

// Declare a reference type for referring to Foo objects.
typedef struct le_foo* le_foo_Ref_t;

But "struct le_foo" would not be defined in the API header or anywhere outside of the hypothetical "Foo" API's implementation files. This makes "struct le_foo" an "incomplete type" for all code outside of the Foo API implementation files. Incomplete types can't be used because the compiler doesn't have enough information about them to generate any code that uses them. But pointers to incomplete types can be passed around because the compiler always knows the pointer size. The compiler knows that one incomplete type is not necessarily interchangeable with another, and it won't allow a pointer to an incomplete type to be used where a pointer to another non-void type is expected.

and Event-Driven Programs

"Handler" is an alias for "callback function". Many APIs in the Legato world use callback functions to notify their clients of asynchronous events. For example, to register a function called "SigTermHandler" for notification of receipt of a TERM signal,

le_sig_SetEventHandler(SIGTERM, SigTermHandler);

Instead of using "CallbackFunction", "CallbackFunc", or some abbreviation like "CbFn" (Cub fun? Cubs fan? What?) that is difficult to read and/or remember, "Handler" is used.

The term "handler" also fits with the event-driven programming style that is favoured in the Legato world. These callback functions are essentially event handler functions. That is, the function is called when an event occurs to "handle" that event.