Skip to content

Instantly share code, notes, and snippets.

@falloutphil
Last active January 14, 2024 00:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save falloutphil/ba920d394dc6f307518dc912aa19de9f to your computer and use it in GitHub Desktop.
Save falloutphil/ba920d394dc6f307518dc912aa19de9f to your computer and use it in GitHub Desktop.

Interfacing C with Emacs Lisp

Introduction

This document provides a step-by-step guide for interfacing C with Emacs Lisp, designed for readers familiar with basic Ubuntu, C, and Emacs usage.

Installing Necessary Tools

GNU Scientific Library (GSL)

  • Install GSL: sudo apt-get install libgsl-dev

Emacs

(I compiled Emacs 29.1 from source, but this should also work)

  • Install Emacs: sudo apt-get install emacs

GCC

  • Install GCC: sudo apt-get install gcc

Writing the C Code

The C program will use GSL to generate random numbers and interface with Emacs. Below is the complete C code with explanations:

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <emacs-module.h>
#include <string.h> // Include for strcmp
#include <math.h>   // Include for math functions

#ifndef M_PI
    #define M_PI 3.14159265358979323846
#endif

int plugin_is_GPL_compatible;

// Function to select the GSL random number generator type
const gsl_rng_type *select_rng_type(const char *type) {
    if (strcmp(type, "mt19937") == 0) return gsl_rng_mt19937;
    else if (strcmp(type, "taus") == 0) return gsl_rng_taus;
    return gsl_rng_default;
}

// Function to generate random numbers and return them as a list
static emacs_value Fgenerate_random_numbers(emacs_env *env, ptrdiff_t nargs, emacs_value args[], void *data) {
    char type_str[20]; // Increased buffer size
    ptrdiff_t str_len = sizeof(type_str); // Variable to hold the length of the copied string

    // Correctly using env->copy_string_contents
    env->copy_string_contents(env, args[0], type_str, &str_len);
    type_str[str_len] = '\0'; // Ensure null termination

    ptrdiff_t seed = env->extract_integer(env, args[1]);

    const gsl_rng_type *T = select_rng_type(type_str);
    gsl_rng *r = gsl_rng_alloc(T);
    gsl_rng_set(r, seed);

    ptrdiff_t size = 100;
    emacs_value list = env->intern(env, "nil");

    for (ptrdiff_t i = 0; i < size; i++) {
        double u = gsl_rng_uniform(r);
        double v = gsl_rng_uniform(r);
        double x = sqrt(-2 * log(u)) * cos(2 * M_PI * v);
        emacs_value ex = env->make_float(env, x);

        list = env->funcall(env, env->intern(env, "cons"), 2, (emacs_value[]){ex, list});
    }

    // Uncomment the next line if you need to reverse the list to maintain the generation order
    // list = env->funcall(env, env->intern(env, "nreverse"), 1, &list);

    gsl_rng_free(r);

    return list;
}

// Entry point for the Emacs module
int emacs_module_init(struct emacs_runtime *ert) {
    emacs_env *env = ert->get_environment(ert);

    emacs_value f = env->make_function(env, 2, 2, Fgenerate_random_numbers, "Generate 100 random numbers using GSL", NULL);

    emacs_value Qfset = env->intern(env, "fset");
    emacs_value Qgenerate_random_numbers = env->intern(env, "generate-random-numbers");
    emacs_value args[] = { Qgenerate_random_numbers, f };
    env->funcall(env, Qfset, 2, args);

    return 0;
}

Compiling the C Code

Compile the C code to a shared library using the commands:

  • gcc -std=c99 -fPIC -c gsl_api.c -o your_code.o
  • gcc -shared -o gsl_api.so gsl_api.o -lgsl -lgslcblas

Writing the Emacs Lisp Script

Write an Emacs Lisp script to load the shared library and call the C functions:

(module-load "/path/to/gsl_api.so")
(message "Enjoy some normally distributed random variables: %s" (generate-random-numbers "taus" 123))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment