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.
- Install GSL:
sudo apt-get install libgsl-dev
(I compiled Emacs 29.1 from source, but this should also work)
- Install Emacs:
sudo apt-get install emacs
- Install GCC:
sudo apt-get install gcc
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;
}
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
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))