Emacs Module : Test 1

Since I've heard about Emacs' Dynamic Module feature, I've wanted to try it out. This article presents a small "hello world" with this feature.

This article could have been a gist.

#include "emacs-module.h"
#include <string.h>

int plugin_is_GPL_compatible;

/* To test
   (progn
   (compile "cc -shared -o my-module.so my-module.c")
   (module-load (expand-file-name "./my-module.so"))
   (my-module/test1))

   Inspired by:
   - http://diobla.info/blog-archive/modules-tut.html
   - https://phst.eu/emacs-modules
   - https://nullprogram.com/blog/2016/11/05/
   - https://github.com/jkitchin/emacs-modules
*/
static emacs_value
Fmy_print_message(emacs_env *env, ptrdiff_t nargs, emacs_value* args, void *data) {
    const char *str = "Hello from my module";
    emacs_value string = env->make_string(env, str, strlen(str));
    emacs_value fun = env->intern(env, "message");
    emacs_value _args[] = { string };
    env->funcall(env, fun, 1, _args );
    return env->intern(env, "nil");
}

/* Bind NAME to FUN.  */
static void
bind_function (emacs_env *env, const char *name, emacs_value Sfun)
{
    /* Set the function cell of the symbol named NAME to SFUN using
       the 'fset' function.  */

    /* Convert the strings to symbols by interning them */
    emacs_value Qfset = env->intern (env, "fset");
    emacs_value Qsym = env->intern (env, name);

    /* Prepare the arguments array */
    emacs_value args[] = { Qsym, Sfun };

    /* Make the call (2 == nb of arguments) */
    env->funcall (env, Qfset, 2, args);
}

/* Provide FEATURE to Emacs.  */
static void
provide (emacs_env *env, const char *feature)
{
    /* call 'provide' with FEATURE converted to a symbol */

    emacs_value Qfeat = env->intern (env, feature);
    emacs_value Qprovide = env->intern (env, "provide");
    emacs_value args[] = { Qfeat };

    env->funcall (env, Qprovide, 1, args);
}

int emacs_module_init (struct emacs_runtime *runtime)
{
    int emacs_version;
    emacs_env *env = runtime->get_environment (runtime);
    if (env->size >= sizeof (struct emacs_env_26))
        emacs_version = 26;  /* Emacs 26 or later.  */
    else if (env->size >= sizeof (struct emacs_env_25))
        emacs_version = 25;
    else
        return 2; /* Unknown or unsupported version.  */

    /* Print a message to say when the module is loaded. */
    {
        emacs_value message = env->intern(env, "message");
        const char hi[] = "Hello, world from my module!";
        emacs_value string = env->make_string(env, hi, sizeof(hi) - 1);
        env->funcall(env, message, 1, &string);
    }

    /* create a lambda (returns an emacs_value) */
    emacs_value fun = env->make_function (env,
                                          0,            /* min. number of arguments */
                                          0,            /* max. number of arguments */
                                          Fmy_print_message,  /* actual function pointer */
                                          "doc",        /* docstring */
                                          NULL          /* user pointer of your choice (data param in Fmy_fun) */
        );

    bind_function(env, "my-module/test1", fun);
    provide(env, "my-module");

    return 0;
}

To test it, save the C code in a file my-module.c and execute the following:

(progn
  (compile "cc -shared -o my-module.so my-module.c")
  (module-load (expand-file-name "./my-module.so"))
  (my-module/test1))

Inspired by: