Janet 1.12.2-b91fe8b Documentation
(Other Versions: 1.11.1 1.10.1 1.9.1 1.8.1 1.7.0 1.6.0 1.5.1 1.5.0 1.4.0 1.3.1 )

The Janet C API

One of the fundamental design goals of Janet is to extend it via the C programming language, as well as embed the interpreter in a larger program. To this end, Janet exposes most of it's low level functionality in well defined C API. Unlike Janet, the Janet C API is not safe — it's possible to write programs in C that will crash, use memory after freeing it, or simply cause undefined behavior. However, using the C API will let you use high performance libraries on almost any system and access native functionality that is not possible in pure Janet.

Janet's C API assumes that you will have one Janet interpreter per OS thread (if the system you are on has threads). The means that Janet must have some global, thread local state. This is a problem for some host languages, like Go, that expect all code to be fully re-entrant on any thread. This design choice, however, makes the Janet APIs more pleasant to use in languages like C and C++, as there is no need to pass around a context object to all api functions.

What defines the C API?

The C API is defined by the header janet.h, which should be included by programs to both embed Janet in a larger program, or to write a Janet module.

Using The Amalgamation

When integrating Janet into a host project (usually some larger application, but it could be a small wrapper around Janet), it is convenient to be able to add only a few source files to your project rather than a large dependency. The amalgamated source, janet.c, contains all of the source code for Janet concatenated into one file. This file can be added to the host project, along with janet.h and janetconf.h. There is also the option of including shell.c, which is used in the standalone interpretter, but not usually needed for host programs.

On a POSIX system, building the amalgamated source is straightforward. With janet.c, janet.h, janetconf.h, and shell.c in the current directory, building is as follows:

cc -std=c99 -Wall -Werror -O2 -fPIC -shared janet.c shell.c -o janet -lm -ldl -lrt -lpthread

The exact flags required will depend on the system and on the configuration of the amalgamation. For example, linking to pthreads is not needed if JANET_SINGLE_THREADED is defined in janetconf.h.

Writing a Module

The easiest way to get started with the Janet C API is to write a Janet native module. A native module is native code that can be loaded dynamically at runtime in the interpreter.

A basic skeleton for a native module in C is below.

#include <janet.h>

static Janet myfun(int32_t argc, Janet *argv) {
    janet_fixarity(argc, 0);
    printf("hello from a module!\n");
    return janet_wrap_nil();
}

static const JanetReg cfuns[] = {
    {"myfun", myfun, "(mymod/myfun)\n\nPrints a hello message."},
    {NULL, NULL, NULL}
};

JANET_MODULE_ENTRY(JanetTable *env) {
    janet_cfuns(env, "mymod", cfuns);
}

This module is a very simple module that exposes one native function called myfun. The module is called mymod, although when importing the module, the user can rename it to whatever he or she likes.

Compiling the Module

Once you have written you native code a file mymod.c, you will need to compile it with your system's C compiler. The easiest way to do this is to use jpm and write a project.janet file. jpm will handle all of the linking and flags for you so that you can write portable modules. Since project.janet is just a Janet script, you can detect what platform you are running on and do conditional compilation there if needed.

Inside project.janet:

(declare-project :name "mymod")
(declare-native :name "mymod" :source @["mymod.c"])

Once you have project.janet written, run jpm build to build the module. If all goes well, jpm should have built a file build/mymod.so. In your repl, you can now import your module and use it.

(import build/mymod :as mymod)
(mymod/myfun) # Prints the hello message

Congratulations, you have a native module.