From d7e0ced38b64c88f875e2739278409dc886ccad0 Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Thu, 7 Jul 2022 13:27:02 -0500 Subject: [PATCH] Add macros to ease creating static variable references --- CHANGELOG.md | 1 + README.md | 13 ++++++++++--- src/clj/coffi/ffi.clj | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55813bd..3042ed4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. This change ## [Unreleased] ### Added +- New macros for defining vars with values from native code - New function to allow getting the backing memory segment of a `coffi.ffi.StaticVariable`, to replace the `Addressable` implementation lost in the migration to JDK 18 ## [0.5.357] - 2022-07-07 diff --git a/README.md b/README.md index c682235..409955f 100644 --- a/README.md +++ b/README.md @@ -265,18 +265,21 @@ does not support va-list, however it is a planned feature. ### Global Variables Some libraries include global variables or constants accessible through symbols. -To start with, constant values stored in symbols can be fetched with `const` +To start with, constant values stored in symbols can be fetched with `const`, or +the parallel macro `defconst` ```clojure (def some-const (ffi/const "some_const" ::mem/int)) +(ffi/defconst some-const "some_const" ::mem/int) ``` This value is fetched once when you call `const` and is turned into a Clojure -value. If you need to refer to a global variable, then `static-variable` can be -used to create a reference to the native value. +value. If you need to refer to a global variable, then `static-variable` (or +parallel `defvar`) can be used to create a reference to the native value. ```clojure (def some-var (ffi/static-variable "some_var" ::mem/int)) +(ffi/defvar some-var "some_var" ::mem/int) ``` This variable is an `IDeref`. Each time you dereference it, the value will be @@ -297,6 +300,10 @@ value is being mutated on another thread. A parallel function `fswap!` is also provided, but it does not provide any atomic semantics either. +The memory that backs the static variable can be fetched with the function +`static-variable-segment`, which can be used to pass a pointer to the static +variable to native functions that require it. + ### Complex Wrappers Some functions require more complex code to map nicely to a Clojure function. The `defcfn` macro provides facilities to wrap the native function with some diff --git a/src/clj/coffi/ffi.clj b/src/clj/coffi/ffi.clj index b68e8b0..41b76eb 100644 --- a/src/clj/coffi/ffi.clj +++ b/src/clj/coffi/ffi.clj @@ -561,6 +561,25 @@ [symbol-or-addr type] (mem/deserialize (.address (ensure-symbol symbol-or-addr)) [::mem/pointer type])) +(s/def ::defconst-args + (s/cat :var-name simple-symbol? + :docstring (s/? string?) + :symbol-or-addr any? + :type ::mem/type)) + +(defmacro defconst + "Defines a var named by `symbol` to be the value of the given `type` from `symbol-or-addr`." + {:arglists '([symbol docstring? symbol-or-addr type])} + [& args] + (let [args (s/conform ::defconst-args args)] + `(let [symbol# (ensure-symbol ~(:symbol-or-addr args))] + (def ~(:var-name args) + ~@(when-let [doc (:docstring args)] + (list doc)) + (const symbol# ~(:type args)))))) +(s/fdef defconst + :args ::defconst-args) + (deftype StaticVariable [seg type meta] IDeref (deref [_] @@ -615,6 +634,19 @@ (mem/global-scope)) type (atom nil))) +(defmacro defvar + "Defines a var named by `symbol` to be a reference to the native memory from `symbol-or-addr`." + {:arglists '([symbol docstring? symbol-or-addr type])} + [& args] + (let [args (s/conform ::defconst-args args)] + `(let [symbol# (ensure-symbol ~(:symbol-or-addr args))] + (def ~(:var-name args) + ~@(when-let [doc (:docstring args)] + (list doc)) + (static-variable symbol#))))) +(s/fdef defvar + :args ::defconst-args) + (s/def :coffi.ffi.symbolspec/symbol string?) (s/def :coffi.ffi.symbolspec/type keyword?) (s/def ::symbolspec