\( % Arcus cosine. \def\acos{\cos^{-1}} % Vector projection. \def\projection#1#2{{proj_{#1}\left(#2\right)}} % Vector rejection. \def\rejection#1#2{{rej_{#1}\left(#2\right)}} % Norm. \def\norm#1{{\left\|#1\right\|}} % Cross product. \def\cross#1#2{\mathit{cross}\left(#1,#2\right)} % Dot product. \def\dot#1#2{{#1 \cdot #2}} % Magnitude. \def\mag#1{{\left|#1\right}} \def\group#1{\left(#1\right)}} \def\sbgrp#1{\left\{#1\right\}} \)

Introduction

The programming language C does not provide relevant reifications for structuring programs into modular pieces of which each can be understood, maintained, and consumed on its own. For our pieces of software, it was decided to introduce the concept of modules to give our libraries and the programs building upon these libraries a modular structure. A *module* provides a compile-time and run-time protocol for its consumption by a program consuming the module. The foremost goal of these modules are that each of them can be understood, maintained, and consumed on its own.

Names of modules

A module has a canonical name which is of the form [A-Z]([A-Z]|[a-z]|[0-9])* and should follow pascal case conventions. This canonical name gives raise to the naming the libraries, files, and folders of the module as well as the names of functions, types, and macros provided by the module.

Consuming a module at compile-time

A module always provides a single header file Mkx/[name].h* and a static library file Mkx-[name].lib. To consume a module, you need to make sure both files are found by the compiler of the consuming program. Next step is to include the header file where required and link the consuming program with the static library.

Consuming a module at run-time

A module provides declarations of functions, definitions of types, and macros - in short everything a normal C library offers. There is a restriction though: Functions of a module may only be used while a module handle is held. That is, before functions of a module are called, at least one module handle must be acquired by the process. When all calls have terminated and the handle is no longer required the module handle must be relinquished. You can acquire anothe module handle at any later point of time again.

Acquire a module handle for a module [name] by calling the function Mkx_[name]_ModuleHandle Mkx_[name]_ModuleHandle_acquire() and relinquish the module handle by calling void Mkx_[name]_ModuleHandle_relinquish(Mkx_[name]_ModuleHandle). Both functions are provided by every module. Acquisition might fail: In that case Mkx_[name]_ModuleHandle_acquire returns Mkx_[name]_ModuleHandle_Invalid. Mkx_[name]_ModuleHandle_relinquish silently ignores an invalid module handle.

Example: The filesystem module

The following example demonstrates how to acquire and relinquish a handle for the FileSystem module.

#incude "Mkx/FileSystem.h"
#include <stdlib.h>

int
main
  (
    int argc,
    char **argv
  )
{
  Mkx_FileSystem_ModuleHandle handle = Mkx_FileSystem_ModuleHandle_acquire();
  if (!handle) /* Or equivalently: if (Mkx_FileSystem_ModuleHandle_Invalid == handle) */
  {
    return EXIT_FAILURE;
  }
  Mkx_FileSystem_ModuleHandle_relinquish(handle);
  return EXIT_SUCCESS;
}