C Modules (C23 Proposal)

Modern modular programming in C

📦 What are C Modules?

C Modules are a proposed feature for C23 that provides better encapsulation and faster compilation. Modules replace traditional header files with a more modern import/export system.


// math_utils.c - Module implementation
export module math_utils;

export int add(int a, int b) {
    return a + b;
}

// main.c - Using the module
import math_utils;
printf("Result: %d\n", add(5, 3));
                                    

Module Concepts

📤

export

Make functions/types public

export int func();
📥

import

Use functions from modules

import my_module;
🏷️

module

Declare module name

export module utils;
🔒

private

Internal module functions

static int helper();

🔹 Creating a Simple Module

Build modular C code by separating interface and implementation into header and source files. Header files declare functions and types publicly available; source files contain implementation details. This separation hides complexity and protects internal logic from accidental modification. Creating modules encourages code reuse across projects and promotes organized, maintainable program structure. Simple modules demonstrate fundamental principles of modern software architecture and design patterns used professionally.

// calculator.c - Module implementation
export module calculator;

#include <stdio.h>

// Private helper function (not exported)
static void log_operation(const char* op, int a, int b, int result) {
    printf("Operation: %d %s %d = %d\n", a, op, b, result);
}

// Exported functions
export int add(int a, int b) {
    int result = a + b;
    log_operation("+", a, b, result);
    return result;
}

export int subtract(int a, int b) {
    int result = a - b;
    log_operation("-", a, b, result);
    return result;
}

export int multiply(int a, int b) {
    int result = a * b;
    log_operation("*", a, b, result);
    return result;
}

// Exported constant
export const double PI = 3.14159265359;
// main.c - Using the module
import calculator;
#include <stdio.h>

int main() {
    int x = 10, y = 5;
    
    printf("Calculator Demo:\n");
    printf("Addition: %d\n", add(x, y));
    printf("Subtraction: %d\n", subtract(x, y));
    printf("Multiplication: %d\n", multiply(x, y));
    printf("PI value: %.5f\n", PI);
    
    return 0;
}

Output:

Calculator Demo:
Operation: 10 + 5 = 15
Addition: 15
Operation: 10 - 5 = 5
Subtraction: 5
Operation: 10 * 5 = 50
Multiplication: 50
PI value: 3.14159

🔹 Module with Types and Structures

Export custom types and structures from modules to create powerful abstractions and APIs. Define types in header files making them available to module users while keeping implementations private. Structure definitions enable passing complex data through module interfaces intuitively. This approach creates professional libraries providing rich functionality without exposing internal details. Advanced modules demonstrate software engineering best practices for building robust, reusable code components.

// geometry.c - Module with types
export module geometry;

#include <stdio.h>
#include <math.h>

// Exported type definitions
export typedef struct {
    double x, y;
} Point;

export typedef struct {
    Point center;
    double radius;
} Circle;

export typedef struct {
    Point top_left;
    Point bottom_right;
} Rectangle;

// Exported functions for Point
export Point create_point(double x, double y) {
    Point p = {x, y};
    return p;
}

export double distance(Point a, Point b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

// Exported functions for Circle
export Circle create_circle(Point center, double radius) {
    Circle c = {center, radius};
    return c;
}

export double circle_area(Circle c) {
    return 3.14159 * c.radius * c.radius;
}

// Exported functions for Rectangle
export Rectangle create_rectangle(Point tl, Point br) {
    Rectangle r = {tl, br};
    return r;
}

export double rectangle_area(Rectangle r) {
    double width = r.bottom_right.x - r.top_left.x;
    double height = r.top_left.y - r.bottom_right.y;
    return width * height;
}
// shapes_demo.c - Using geometry module
import geometry;
#include <stdio.h>

int main() {
    // Create points
    Point p1 = create_point(0, 0);
    Point p2 = create_point(3, 4);
    
    printf("Distance between points: %.2f\n", distance(p1, p2));
    
    // Create and use circle
    Circle circle = create_circle(p1, 5.0);
    printf("Circle area: %.2f\n", circle_area(circle));
    
    // Create and use rectangle
    Point top_left = create_point(0, 10);
    Point bottom_right = create_point(8, 2);
    Rectangle rect = create_rectangle(top_left, bottom_right);
    printf("Rectangle area: %.2f\n", rectangle_area(rect));
    
    return 0;
}

Output:

Distance between points: 5.00
Circle area: 78.54
Rectangle area: 64.00

🔹 Module Interfaces

Separate interface from implementation creating clean, maintainable, and flexible module designs. Well-designed interfaces hide implementation complexity, enabling internal changes without affecting users. Public functions form contracts between modules and users, establishing clear responsibilities. Opaque types prevent direct structure access, forcing controlled interaction through provided functions. This separation embodies encapsulation principles from object-oriented design, bringing software engineering discipline to C programs.

// string_utils.ixx - Module interface
export module string_utils;

#include <stddef.h>

// Export function declarations
export size_t string_length(const char* str);
export char* string_copy(const char* src);
export int string_compare(const char* str1, const char* str2);
export char* string_concat(const char* str1, const char* str2);
export void string_free(char* str);
// string_utils.c - Module implementation
module string_utils;

#include <stdio.h>
#include <stdlib.h>

// Private helper function
static char* allocate_string(size_t size) {
    char* str = malloc(size + 1);
    if (!str) {
        printf("Memory allocation failed\n");
        exit(1);
    }
    return str;
}

// Implement exported functions
size_t string_length(const char* str) {
    if (!str) return 0;
    size_t len = 0;
    while (str[len]) len++;
    return len;
}

char* string_copy(const char* src) {
    if (!src) return NULL;
    
    size_t len = string_length(src);
    char* dest = allocate_string(len);
    
    for (size_t i = 0; i <= len; i++) {
        dest[i] = src[i];
    }
    return dest;
}

int string_compare(const char* str1, const char* str2) {
    if (!str1 || !str2) return -1;
    
    while (*str1 && *str2 && *str1 == *str2) {
        str1++;
        str2++;
    }
    return *str1 - *str2;
}

char* string_concat(const char* str1, const char* str2) {
    if (!str1 || !str2) return NULL;
    
    size_t len1 = string_length(str1);
    size_t len2 = string_length(str2);
    char* result = allocate_string(len1 + len2);
    
    // Copy first string
    for (size_t i = 0; i < len1; i++) {
        result[i] = str1[i];
    }
    
    // Copy second string
    for (size_t i = 0; i <= len2; i++) {
        result[len1 + i] = str2[i];
    }
    
    return result;
}

void string_free(char* str) {
    if (str) {
        free(str);
    }
}
// text_processor.c - Using string utilities
import string_utils;
#include <stdio.h>

int main() {
    const char* original = "Hello";
    const char* suffix = " World!";
    
    // Test string operations
    printf("Original length: %zu\n", string_length(original));
    
    char* copy = string_copy(original);
    printf("Copy: %s\n", copy);
    
    char* combined = string_concat(original, suffix);
    printf("Combined: %s\n", combined);
    
    int cmp = string_compare(original, copy);
    printf("Compare result: %d\n", cmp);
    
    // Clean up
    string_free(copy);
    string_free(combined);
    
    return 0;
}

Output:

Original length: 5
Copy: Hello
Combined: Hello World!
Compare result: 0

🔹 Benefits of Modules

Modules provide significant advantages over traditional header files through better organization and maintenance. Modules encapsulate related functionality, reducing namespace pollution and naming conflicts. Clear interfaces document expected usage, improving code clarity and reducing integration errors. Modules enable independent testing and development of components. Information hiding protects against accidental misuse and implementation changes. These benefits transform large projects into manageable, maintainable systems with professional architecture.

Module Benefits:

  • Faster compilation: No need to reparse headers repeatedly
  • Better encapsulation: Clear public/private boundaries
  • No macro pollution: Modules don't leak macros
  • Order independence: Import order doesn't matter
  • Reduced dependencies: Only import what you need
  • Better tooling: IDEs can provide better support
// Traditional approach problems:
#include "header1.h"  // Might define conflicting macros
#include "header2.h"  // Order matters, slow compilation
#include "header3.h"  // Includes everything, even unused parts

// Module approach benefits:
import math_utils;     // Fast, only what's needed
import string_utils;   // No macro conflicts
import geometry;       // Order independent

🧠 Test Your Knowledge

What keyword is used to make a function available outside a module?