;;;;Roman Klochkov, monk@slavsoft.surgut.ru ;;;; Base classes for freeable and changeable CFFI types (in-package #:cffi-objects) ;;;[ [[* Memory freeing automation *]] #| Most of new CFFI types introduced in my library will live in the dynamic memory. There are different policies of memory control in different languages and libraries. Sometimes caller should clean memory (like in GTK), sometimes callee. In any case programmer should have possibility to say, if he would like to free memory after function call. For example, in GTK it is common for callback to return a newly-allocated string or structure, but in parameters responsibility to clean memory remains to caller. Another common option for any type is a flag, that it is out-paramter, so value of it should be translated back before freeing, For uniformity with CFFI :string I chose :free-from-foreign and :free-to-foreign boolean flags to show, when we want to free memory. By default "caller frees" model is used. |# ;;;[ #| I divided freeable functional to two classes: \begin{itemize} \item [[freeable-base]] introduces all necessary fields and handlers \item [[freeable]] have ready cffi-translator methods \end{itemize} |# (define-foreign-type freeable-base () ;; Should we free after translating from foreign? ((free-from-foreign :initarg :free-from-foreign :reader fst-free-from-foreign-p :initform nil :type boolean) ;; Should we free after translating to foreign? (free-to-foreign :initarg :free-to-foreign :reader fst-free-to-foreign-p :initform t :type boolean))) #| Interface to [[freeable-base]] consists of three generics for describing, how to free particular type: [[free-ptr]], [[free-sent-ptr]] and [[free-returned-ptr]], and two functions to use in CFFI translators: [[free-returned-if-needed]] and [[free-sent-if-needed]]. |# ;;;[ #| This generic describes, how to free an object with CFFI type [[type]] and pointer [[ptr]]. As [[type]] should be a symbol, you should specialize this generic with EQL specifier if your objects shouldn't be freed with [[foreign-free]]. One can ask, why normal specializer by type of object and [[object]] as a first parameter is not used. Such strange API is developed, because [[free-ptr]] is used in [[trivial-garbage:finalize]] and in some implementation (for example, SBCL) finalizer shouldn't have reference to finalized object. If you dislike it and you will not use finalizers, simply specialize or redefine [[free-sent-ptr]] and [[free-returned-ptr]] |# (defgeneric free-ptr (type ptr) (:documentation "Called to free ptr, unless overriden free-sent-ptr or free-returned-ptr. TYPE should be symbol and be specialized with EQL") (:method (type ptr) (foreign-free ptr))) ;;;[ #| This generic describes, how to free an object with CFFI type [[type]] and pointer [[ptr]] after sending to foreign space. It has the same parameters as [[cffi:free-translated-object]]. If complex foreign type has additional conditionals or any additional actions when freeing, specialize it on you type. Please, don't call it directly. Use [[free-sent-if-needed]] instead. |# (defgeneric free-sent-ptr (cffi-type ptr param) (:documentation "Will be called in free-translated-object. CFFI-TYPE: type defined with define-foreign-type. PTR: foreign pointer PARAM: third parameter of free-translated-object == returned second value of translate-to-foreign.") (:method ((cffi-type freeable-base) ptr param) (unless (null-pointer-p ptr) (free-ptr (type-of cffi-type) ptr)))) ;;;[ #| This generic describes, how to free an object with CFFI type [[type]] and pointer [[ptr]] after receiving from foreign space. It has the same parameters as [[cffi:translate-to-foreign]]. If complex foreign type has additional conditionals or any additional actions when freeing, specialize it on you type. Please, don't call it directly. Use [[free-returned-if-needed]] instead. |# (defgeneric free-returned-ptr (cffi-type ptr) (:documentation "Will be called in translate-from-foreign after conversion. CFFI-TYPE: type defined with define-foreign-type. PTR: foreign pointer") (:method ((cffi-type freeable-base) ptr) (unless (null-pointer-p ptr) (free-ptr (type-of cffi-type) ptr)))) ;;;[ #| This is standard base class for freeable pointers. If you happy with default free algorithm, which implies, that [[free-sent-ptr]] is called after [[free-translated-object]] when type described with [[:free-to-foreign t]] and [[free-returned-ptr]] is called when type described with [[:free-from-foreign t]] after [[translate-from-foreign]]. If you need more complicated logic (for example, to free object in translate-from-foreign, not after), you should inherit your class from [[freeable-base]] and call [[free-sent-if-needed]] from [[free-translated-object]] and [[free-returned-if-needed]] from [[translate-from-foreign]]. |# (defclass freeable (freeable-base) () (:documentation "Mixing to auto-set translators")) (defmethod free-translated-object :after (ptr (type freeable) param) (free-sent-if-needed type ptr param)) (defmethod translate-from-foreign :after (ptr (type freeable)) (free-returned-if-needed type ptr)) ;;;[ #| This is standard base class for objects, that should be copied back to lisp after foreign function: so-called ``out parameters''. For every class, inherited from [[freeable-out]], you must implement method [[copy-from-foreign]]. Then user of your class may set [[:out t]] in initargs for your class and be sure, that all changed data will be copied back to the variables. When implementing [[translate-to-foreign]] you must return (values ptr value), because second value will be passed to [[free-translated-object]], and then as PLACE to [[copy-from-foreign]]. |# (define-foreign-type freeable-out (freeable) ((out :accessor object-out :initarg :out :initform nil :documentation "This is out param (for fill in foreign side)")) (:documentation "For returning data in out params. If OUT is t, then translate-to-foreign MUST return (values ptr place)")) ;;;[ #| This generic must have an implementation for every class inherited from [[freeable-out]]. |# (defgeneric copy-from-foreign (cffi-type ptr place) (:documentation "Transfers data from pointer PTR to PLACE. CFFI-TYPE: type defined with define-foreign-type. PTR: foreign pointer PLACE: third parameter of free-translated-object == returned second value of translate-to-foreign")) (defmethod free-translated-object :before (ptr (type freeable-out) place) (when (object-out type) (copy-from-foreign type ptr place)))