Skip to content

A template library to make creating dynamic module functions callable from Elisp easier.

Notifications You must be signed in to change notification settings

nealsid/emacs-module-wrapper-template

Repository files navigation

emacs-module-wrapper-template

This project is motivated by recently writing an Emacs module. Writing an Emacs module requires an amount of boilerplate around the functions in C/C++ that you would like to call, which this library intends to automate the generation of. The boilerplate, which is mostly unpacking and verifying arguments provided by the ELISP caller, is amenable to being generated by a macro. For a good discussion of writing Emacs modules, see http://diobla.info/blog-archive/modules-tut.html#sec-2-2. The goal of this library is to turn something like this:

/** This is some function that encapsulates the work you need done in C++ */
emacs_value some_C_work_useful_for_Emacs(int a, const string& s, void* data) {

}

/* This is the function that actually gets called from and is registered with Emacs */
emacs_value elisp_callable(emacs_env* env, ptrdiff_t nargs, emacs_value* args, void* data) {
  // Assert nargs contains 1 or 2
  // unpack an int from args with error handling
  // check if nargs == 1 or 2, and unpack a string if it is 2 with error handling
  return some_C_work_useful_for_Emacs(i, s, data);
}

/* This is how modules are initialized, and how functions are registered for being called from elisp */
int emacs_module_init(struct emacs_runtime *runtime) noexcept {
    emacs_env* env = runtime->get_environment(runtime);
    emacs_value func = env->make_function(env,
  					  1,  // Minimum parameter count
  					  2,  // Maximum parameter count
  					  elisp_callable,
  					  "Callable function from emacs",
  					  nullptr);  // User data passed to your function
    emacs_value symbol = env->intern(env, "lisp-function-name");
    emacs_value args[] = { symbol, func };
    emacs_value defalias = env->intern(env, "defalias");
    env->funcall(env, defalias, 2, args);
}

into something like this:

/* This is your same function that encapsulates work you need to do in C++ */
emacs_value some_C_work_useful_for_Emacs(emacs_env* env, const string s, std::optional<int> i) {
  cout << s << endl;
  cout << i.value() << endl;
  return env->intern(env, "nil");
}

EmacsCallable<some_C_work_useful_for_Emacs> c;

int emacs_module_init(struct emacs_runtime *runtime) noexcept {

  c.defineInEmacs(runtime, "emwt-lisp-callable", "Test function", nullptr,
  		  elispCallableFunction<&c>);

  return 0;
}

In other words elisp_callable and emacs_module_init are automatically generated.

elispCallableFunction takes a functor address as a non-type template parameter, and when it is invoked, it invokes the functor. The functor is generated by the function you provide, and it will unpack arguments and call your function.

Perf counter analysis

This chart shows a run in Instruments, collecting "Cycles with outstanding L1 misses", "DTLB misses that incur a page walk", and "Total Cycles". I used the Signposts API to limit to time spent in user-function invocation (i.e. the wrapper function that calls the user function provided by the module). There is a flaw (or, at least one) in how I collected the numbers, which is that I do not know how to limit perf counter metrics to just time spent between signposts. I think it requires a custom instrument or more advanced collection. Therefore, I had to analyze perf counter data from the first sign post to the last one, which includes time the process is context switched out.

PerfCounters

Some future optimizations planned are:

  • Use a statically-allocated buffer for strings to reduce the need to allocate heap memory during the function invocation.
  • When unpacking parameters, figure out a way to unpack them directly into the arguments tuple (similar to emplace functions), instead of returning them and relying on copies being made.

Instruments screen shot

InstrumentsScreenshot

About

A template library to make creating dynamic module functions callable from Elisp easier.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published