понедельник, 23 сентября 2013 г.

Использование JNA в Clojure.

Разберем использование Java Native Access на простом примере в Clojure. Для начала необходимо скачать jna.jar с github.com и добавить в новый проект clojure, созданный через утилиту lein:

(defproject jna "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.4.0"]]
  :plugins [[lein-swank "1.4.5"]]
  :resource-paths ["resources/jna-4.0.0.jar"])
После чего необходимо добавить импорт в ns:

(ns jna.core
  (:import (com.sun.jna Library Function)))
Следующий шаг это создание нативной библиотеки:

#ifndef FOO_H
#define FOO_H

extern int foo(int a, int b);

#endif

#include "foo.h"

int foo(int a, int b)
{
     return a + b;
}

gcc -c -Wall -Werror -fpic foo.c
gcc -shared -o libfoo.so foo.o
Один из вариантов вызова нативных функций это использование интерфейса:

(def foo-interface (gen-interface
                    :name foo-test
                    :extends [com.sun.jna.Library]
                    :methods [[foo [Integer Integer] Integer]]))
Второй этап этой процедуры это загрузка самой библиотеки:

(def native-lib (Native/loadLibrary "clib/libfoo.so" foo-interface))
И последний этап это определение самой функции:

(defn native-foo [a b]
  (.foo native-lib (Integer. a) (Integer. b)))
Преобразование входных переменных к типу Integer необходимо из-за того, что при вызове функции с целыми числами они являются типом Long. Еще одним вариантом вызова нативных функций - это использование класса Function. На просторах интернета я нашел достаточно интересный макрос:

(defmacro jna-call [lib func ret & args]
  `(let [library#  (name ~lib)
         function# (Function/getFunction library# ~func)]
          (.invoke function# ~ret (to-array [~@args]))))
Но опять же необходимо не забывать, что при передаче простых чисел они имеют тип Long.

Подключение локальных библиотек в Clojure

В качестве одного из вариантов подключения своих локальных jar-файлов в проект является использование опции resource-paths в project.clj . Таким образом файл проекта, сгенерированный утилитой lein, примет вид:

(defproject test-project "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.4.0"]]
  :plugins [[lein-swank "1.4.5"]]
  :resource-paths ["path-to-resource"])
Причем можно указывать как отдельные файлы, так и полностью всю директорию, если использовать wildcards: :resource-paths ["path-to-resource1/example.jar" "path-to-resource2/*"] .