Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I call clojure code from C++?

I'm writing a plugin for a program written in C++.

Plugins are placed in a specific dir and get called by the main application. I would like to write most of the plugin in Clojure (gui, calculations, etc) however the actual "plugin" needs to be written in C++. Various data needs to be passed from C++ to Clojure.

How can I do this?

JNI/JNA, sockets, system calls? (nothing I know much about)

like image 357
AnnanFay Avatar asked Dec 27 '11 23:12

AnnanFay


2 Answers

I know this question is old but maybe somebody find this useful.

#include <jni.h>       /* where everything is defined */
#include <cstring>

int main() {
    JavaVM *jvm;       /* denotes a Java VM */
    JNIEnv *env;       /* pointer to native method interface */
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=/home/raoof/.m2/repository/org/clojure/spec.alpha/0.1.143/spec.alpha-0.1.143.jar:/home/raoof/.m2/repository/org/clojure/clojure/1.9.0/clojure-1.9.0.jar";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
     * pointer in env */
    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    delete options;

    jclass Clojure = env->FindClass("clojure/java/api/Clojure");
    jmethodID var = env->GetStaticMethodID(Clojure, "var", "(Ljava/lang/Object;Ljava/lang/Object;)Lclojure/lang/IFn;");
    jobject load_string = env->CallStaticObjectMethod(Clojure, var, env->NewStringUTF("clojure.core"), env->NewStringUTF("load-string"));
    jmethodID load_string_invoke = env->GetMethodID(env->GetObjectClass(load_string), "invoke", "(Ljava/lang/Object;)Ljava/lang/Object;");
    env->CallObjectMethod(load_string, load_string_invoke, env->NewStringUTF("(prn (+ 1 2 3 4 5))"));

    jvm->DestroyJavaVM();
}

and then

g++ -I/usr/lib/jvm/java-8-openjdk-amd64/include -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux/ -L/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server -ljvm clojurejvm.cpp
LD_LIBRARY_PATH=/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server ./a.out
like image 181
raoof Avatar answered Nov 20 '22 03:11

raoof


JNI should be pretty straightforward for this.

I would approach it this way:

  1. develop your clojure code with a well defined external interface, ie, whatever set of methods/functions you need to invoke.
  2. package it as a standalone uber-jar (containing the clojure libs as well)
  3. write your c++ wrapper, which must do the following:
    • create the jvm with your uberjar on the classpath (see this link: http://java.sun.com/docs/books/jni/html/invoke.html )
    • load your clojure class
    • provide a facade that maps c++ functions to the underlying java methods (clojure functions)

You can test your uber-jar from step 2 via a simple standalone java test harness that creates the main clojure class and invokes the appropriate methods; this will let you know that you have a good java/clojure jar in case you run into any issues in the jni invocation in step 3.

As you check the jni references pay particular attention to the slight/subtle calling differences between the c and c++ jni linkages.

Good luck.

like image 2
Rob H Avatar answered Nov 20 '22 04:11

Rob H