Skip to content

4. Standards: Dynamically Installable Plugins In C

Ulrond edited this page Jul 11, 2024 · 1 revision

Here are some ideas on how to implement a modular C program that can load optional submodules or plugins at runtime or compile-time. The approach will ensure the main module can call these plugins conditionally based on their presence.

Step-by-Step Guide

  1. Define Plugin Interface: Define a common interface that all plugins must implement. This could be a set of function pointers within a struct.
// plugin.h
#ifndef PLUGIN_H
#define PLUGIN_H

typedef struct {
    void (*initialize)(void);
    void (*perform_action)(void);
    void (*cleanup)(void);
} PluginInterface;

#endif // PLUGIN_H
  1. Implement Plugin: Implement a sample plugin following the defined interface.
// plugin_impl.c
#include "plugin.h"
#include <stdio.h>

void plugin_initialize(void) {
    printf("Plugin initialized.\n");
}

void plugin_perform_action(void) {
    printf("Plugin action performed.\n");
}

void plugin_cleanup(void) {
    printf("Plugin cleaned up.\n");
}

PluginInterface plugin = {
    .initialize = plugin_initialize,
    .perform_action = plugin_perform_action,
    .cleanup = plugin_cleanup
};
  1. Dynamic Loading (Runtime): Use dynamic loading to load the plugin at runtime. On Linux, you can use dlopen and dlsym.
// main.c
#include <stdio.h>
#include <dlfcn.h>
#include "plugin.h"

int main(void) {
    void *handle;
    PluginInterface *plugin = NULL;

    // Attempt to load the plugin dynamically
    handle = dlopen("./plugin_impl.so", RTLD_LAZY);
    if (handle) {
        plugin = (PluginInterface *)dlsym(handle, "plugin");
        if (plugin) {
            plugin->initialize();
            plugin->perform_action();
            plugin->cleanup();
        }
        dlclose(handle);
    } else {
        printf("Plugin not found. Running without plugin.\n");
    }

    // Main module logic
    printf("Main module running.\n");

    return 0;
}
  1. Compile-Time Loading: Alternatively, you can conditionally compile the plugin code based on a macro definition.
// main.c
#include <stdio.h>
#include "plugin.h"

#ifdef USE_PLUGIN
extern PluginInterface plugin;
#endif

int main(void) {
    PluginInterface *plugin_ptr = NULL;

#ifdef USE_PLUGIN
    plugin_ptr = &plugin;
#endif

    if (plugin_ptr) {
        plugin_ptr->initialize();
        plugin_ptr->perform_action();
        plugin_ptr->cleanup();
    } else {
        printf("Plugin not included. Running without plugin.\n");
    }

    // Main module logic
    printf("Main module running.\n");

    return 0;
}
  1. Compiling the Code: For dynamic loading, compile the plugin as a shared library.
gcc -shared -o plugin_impl.so -fPIC plugin_impl.c
gcc -o main main.c -ldl

For compile-time loading, compile with a macro definition.

gcc -DUSE_PLUGIN -o main main.c plugin_impl.c

Key Points:

  • Plugin Interface: Clearly define the interface the plugins must adhere to.
  • Dynamic Loading: Use dlopen and dlsym to load the plugin at runtime if it is available.
  • Compile-Time Option: Use conditional compilation to include the plugin if it is available at compile-time.
  • Null Check: Always check if the plugin function pointers are NULL before calling them to avoid crashes if the plugin is not available.

By following these steps, you can create a flexible C module that can optionally load and use additional functionality from plugins, either at runtime or compile-time, ensuring robust and modular design.

Clone this wiki locally