Modular C

Synopsis:
#pragma CMOD snippet [ abbrev = ] constraint

Using identifiers from other modules makes the declaration of these identifiers visible to the current module, but this feature import doesn't allow to share code between modules.

Such code sharing is done with a snippet directive: all the code that comes after such a directive can be inserted directly into the importing module.

#pragma CMOD module proj∷allocation
#pragma CMOD snippet T = complete
T* alloc(void) {
T* ret = C∷lib∷malloc(sizeof *T);
if (ret) {
*ret = (T){ 0 };
}
return ret;
}

Such a simple "snippet" module that has no code before the snippet directive doesn't export any symbols. It only becomes relevant when it is directly imported by another module.

#pragma module pair = proj∷arith∷pair
#pragma import proj∷allocation
#pragma CMOD declaration
struct pair {
double v[2];
pair* next;
};

Here the module proj∷arith∷pair defines the type proj∷arith∷pair and the import automatically generates a new function proj∷arith∷pair∷alloc. Concerning visibility by other modules, this is then analogous to

typedef struct proj∷arith∷pair proj∷arith∷pair;
struct proj∷arith∷pair {
double v[2];
proj∷arith∷pair* next;
};
extern proj∷arith∷pair* proj∷arith∷pair∷alloc(void);

The code that is compiled is as if we had written

typedef struct proj∷arith∷pair proj∷arith∷pair;
struct proj∷arith∷pair {
double v[2];
proj∷arith∷pair* next;
};
proj∷arith∷pair* alloc(void) {
proj∷arith∷pair* ret = C∷lib∷malloc(sizeof *proj∷arith∷pair);
if (ret) {
*ret = (proj∷arith∷pair){ 0 };
}
return ret;
}

That is, all occurrences of T in the snippet are replaced by the name of the importing module, proj∷arith∷pair, and the function is integrated textually into the the rest of the module.

Disambiguating names imported from snippets

If we have several such imports into the same module this can easily lead to naming conflicts if two imported modules declare the same identifiers in their snippets. An easy way to cope with such situations is to import a module by additionally giving it an abbreviation.

#pragma module pair = proj∷arith∷pair
#pragma import two = proj∷allocation
#pragma CMOD declaration
struct pair {
double v[2];
pair* next;
};

Now, all local identifiers from the snippet are prefixed by two—, where is the composer character. So instead of

extern proj∷arith∷pair* proj∷arith∷pair∷alloc(void);

this import now declares

extern proj∷arith∷pair* proj∷arith∷pair∷two—alloc(void);

Since all abbreviations must be unique, this strategy provides unique names for the whole module.

Remarks
Snippets are only inserted if their module is imported explicitly through an import directive, see Importing and abbreviating.
Per default a snippet constitutes a definition section.
Snippet code can be distinguished into declaration and definition sections, just as the main code of a module.