1 ;;;; -*- Mode: lisp; indent-tabs-mode: nil -*-
3 ;;; enum.lisp --- Defining foreign constants as Lisp keywords.
5 ;;; Copyright (C) 2005-2006, James Bielman <jamesjb@jamesjb.com>
7 ;;; Permission is hereby granted, free of charge, to any person
8 ;;; obtaining a copy of this software and associated documentation
9 ;;; files (the "Software"), to deal in the Software without
10 ;;; restriction, including without limitation the rights to use, copy,
11 ;;; modify, merge, publish, distribute, sublicense, and/or sell copies
12 ;;; of the Software, and to permit persons to whom the Software is
13 ;;; furnished to do so, subject to the following conditions:
15 ;;; The above copyright notice and this permission notice shall be
16 ;;; included in all copies or substantial portions of the Software.
18 ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 ;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 ;;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 ;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 ;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 ;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 ;;; DEALINGS IN THE SOFTWARE.
30 ;;;# Foreign Constants as Lisp Keywords
32 ;;; This module defines the DEFCENUM macro, which provides an
33 ;;; interface for defining a type and associating a set of integer
34 ;;; constants with keyword symbols for that type.
36 ;;; The keywords are automatically translated to the appropriate
37 ;;; constant for the type by a type translator when passed as
38 ;;; arguments or a return value to a foreign function.
40 (defclass foreign-enum
(foreign-typedef enhanced-foreign-type
)
42 :initform
(make-hash-table :test
'eq
)
43 :reader keyword-values
)
45 :initform
(make-hash-table)
46 :reader value-keywords
))
47 (:documentation
"Describes a foreign enumerated type."))
49 (defun make-foreign-enum (type-name base-type values
)
50 "Makes a new instance of the foreign-enum class."
51 (let ((type (make-instance 'foreign-enum
:name type-name
52 :actual-type
(parse-type base-type
)))
55 (destructuring-bind (keyword &optional
(value default-value
))
57 (check-type keyword keyword
)
58 (check-type value integer
)
59 (if (gethash keyword
(keyword-values type
))
60 (error "A foreign enum cannot contain duplicate keywords: ~S."
62 (setf (gethash keyword
(keyword-values type
)) value
))
63 ;; This is completely arbitrary behaviour: we keep the last we
64 ;; value->keyword mapping. I suppose the opposite would be
65 ;; just as good (keeping the first). Returning a list with all
66 ;; the keywords might be a solution too? Suggestions
68 (setf (gethash value
(value-keywords type
)) keyword
)
69 (setq default-value
(1+ value
))))
72 (defmacro defcenum
(name-and-options &body enum-list
)
73 "Define an foreign enumerated type."
74 (discard-docstring enum-list
)
75 (destructuring-bind (name &optional
(base-type :int
))
76 (ensure-list name-and-options
)
77 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
79 ',name
(make-foreign-enum ',name
',base-type
',enum-list
)))))
81 (defun hash-keys-to-list (ht)
82 (loop for k being the hash-keys in ht collect k
))
84 (defun foreign-enum-keyword-list (enum-type)
85 "Return a list of KEYWORDS defined in ENUM-TYPE."
86 (hash-keys-to-list (keyword-values (parse-type enum-type
))))
88 ;;; These [four] functions could be good canditates for compiler macros
89 ;;; when the value or keyword is constant. I am not going to bother
90 ;;; until someone has a serious performance need to do so though. --jamesjb
91 (defun %foreign-enum-value
(type keyword
&key errorp
)
92 (check-type keyword keyword
)
93 (or (gethash keyword
(keyword-values type
))
95 (error "~S is not defined as a keyword for enum type ~S."
98 (defun foreign-enum-value (type keyword
&key
(errorp t
))
99 "Convert a KEYWORD into an integer according to the enum TYPE."
100 (let ((type-obj (parse-type type
)))
101 (if (not (typep type-obj
'foreign-enum
))
102 (error "~S is not a foreign enum type." type
)
103 (%foreign-enum-value type-obj keyword
:errorp errorp
))))
105 (defun %foreign-enum-keyword
(type value
&key errorp
)
106 (check-type value integer
)
107 (or (gethash value
(value-keywords type
))
109 (error "~S is not defined as a value for enum type ~S."
112 (defun foreign-enum-keyword (type value
&key
(errorp t
))
113 "Convert an integer VALUE into a keyword according to the enum TYPE."
114 (let ((type-obj (parse-type type
)))
115 (if (not (typep type-obj
'foreign-enum
))
116 (error "~S is not a foreign enum type." type
)
117 (%foreign-enum-keyword type-obj value
:errorp errorp
))))
119 (defmethod translate-to-foreign (value (type foreign-enum
))
121 (%foreign-enum-value type value
:errorp t
)
124 (defmethod translate-from-foreign (value (type foreign-enum
))
125 (%foreign-enum-keyword type value
:errorp t
))
127 ;;;# Foreign Bitfields as Lisp keywords
129 ;;; DEFBITFIELD is an abstraction similar to the one provided by DEFCENUM.
130 ;;; With some changes to DEFCENUM, this could certainly be implemented on
133 (defclass foreign-bitfield
(foreign-typedef enhanced-foreign-type
)
135 :initform
(make-hash-table :test
'eq
)
136 :reader symbol-values
)
138 :initform
(make-hash-table)
139 :reader value-symbols
))
140 (:documentation
"Describes a foreign bitfield type."))
142 (defun make-foreign-bitfield (type-name base-type values
)
143 "Makes a new instance of the foreign-bitfield class."
144 (let ((type (make-instance 'foreign-bitfield
:name type-name
145 :actual-type
(parse-type base-type
)))
147 (dolist (pair values
)
148 ;; bit-floor rule: find the greatest single-bit int used so far,
149 ;; and store its left-shift
150 (destructuring-bind (symbol &optional
151 (value (prog1 bit-floor
152 (setf bit-floor
(ash bit-floor
1)))
155 (check-type symbol symbol
)
157 (check-type value integer
)
158 (when (and (>= value bit-floor
) (single-bit-p value
))
159 (setf bit-floor
(ash value
1))))
160 (if (gethash symbol
(symbol-values type
))
161 (error "A foreign bitfield cannot contain duplicate symbols: ~S."
163 (setf (gethash symbol
(symbol-values type
)) value
))
164 (push symbol
(gethash value
(value-symbols type
)))))
167 (defmacro defbitfield
(name-and-options &body masks
)
168 "Define an foreign enumerated type."
169 (discard-docstring masks
)
170 (destructuring-bind (name &optional
(base-type :int
))
171 (ensure-list name-and-options
)
172 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
174 ',name
(make-foreign-bitfield ',name
',base-type
',masks
)))))
176 (defun foreign-bitfield-symbol-list (bitfield-type)
177 "Return a list of SYMBOLS defined in BITFIELD-TYPE."
178 (hash-keys-to-list (symbol-values (parse-type bitfield-type
))))
180 (defun %foreign-bitfield-value
(type symbols
)
181 (reduce #'logior symbols
182 :key
(lambda (symbol)
183 (check-type symbol symbol
)
184 (or (gethash symbol
(symbol-values type
))
185 (error "~S is not a valid symbol for bitfield type ~S."
188 (defun foreign-bitfield-value (type symbols
)
189 "Convert a list of symbols into an integer according to the TYPE bitfield."
190 (let ((type-obj (parse-type type
)))
191 (if (not (typep type-obj
'foreign-bitfield
))
192 (error "~S is not a foreign bitfield type." type
)
193 (%foreign-bitfield-value type-obj symbols
))))
195 (defun %foreign-bitfield-symbols
(type value
)
196 (check-type value integer
)
197 (loop for mask being the hash-keys in
(value-symbols type
)
198 using
(hash-value symbols
)
199 when
(= (logand value mask
) mask
)
202 (defun foreign-bitfield-symbols (type value
)
203 "Convert an integer VALUE into a list of matching symbols according to
205 (let ((type-obj (parse-type type
)))
206 (if (not (typep type-obj
'foreign-bitfield
))
207 (error "~S is not a foreign bitfield type." type
)
208 (%foreign-bitfield-symbols type-obj value
))))
210 (defmethod translate-to-foreign (value (type foreign-bitfield
))
213 (%foreign-bitfield-value type
(ensure-list value
))))
215 (defmethod translate-from-foreign (value (type foreign-bitfield
))
216 (%foreign-bitfield-symbols type value
))