ffi/com: add support for type descriptions on values
This commit is contained in:
parent
c321608856
commit
38e628be80
|
@ -23,4 +23,8 @@
|
||||||
com-make-event-executor com-event-executor?
|
com-make-event-executor com-event-executor?
|
||||||
|
|
||||||
com-object-get-iunknown com-iunknown?
|
com-object-get-iunknown com-iunknown?
|
||||||
com-object-get-idispatch com-idispatch?)
|
com-object-get-idispatch com-idispatch?
|
||||||
|
|
||||||
|
type-description?
|
||||||
|
type-describe type-described?
|
||||||
|
type-described-value type-described-description)
|
||||||
|
|
|
@ -59,7 +59,11 @@
|
||||||
com-unregister-event-callback
|
com-unregister-event-callback
|
||||||
|
|
||||||
com-object-get-iunknown com-iunknown?
|
com-object-get-iunknown com-iunknown?
|
||||||
com-object-get-idispatch com-idispatch?)
|
com-object-get-idispatch com-idispatch?
|
||||||
|
|
||||||
|
type-description?
|
||||||
|
type-describe type-described?
|
||||||
|
type-described-value type-described-description)
|
||||||
|
|
||||||
;; FIXME:
|
;; FIXME:
|
||||||
;; call args via var-desc (instead of func-dec)
|
;; call args via var-desc (instead of func-dec)
|
||||||
|
@ -1106,8 +1110,24 @@
|
||||||
`(array ? ,(vt-to-scheme-type (- vt VT_ARRAY)))
|
`(array ? ,(vt-to-scheme-type (- vt VT_ARRAY)))
|
||||||
(string->symbol (format "COM-0x~x" vt)))]))
|
(string->symbol (format "COM-0x~x" vt)))]))
|
||||||
|
|
||||||
(define (arg-to-type arg)
|
(define (arg-to-type arg [in-array 0])
|
||||||
(cond
|
(cond
|
||||||
|
[(type-described? arg)
|
||||||
|
(type-described-description arg)]
|
||||||
|
[(vector? arg) `(array ,(vector-length arg)
|
||||||
|
,(if (zero? (vector-length arg))
|
||||||
|
'int
|
||||||
|
(for/fold ([t (arg-to-type (vector-ref arg 0) (add1 in-array))]) ([v (in-vector arg)])
|
||||||
|
(define t2 (arg-to-type v (add1 in-array)))
|
||||||
|
(let loop ([t t] [t2 t2])
|
||||||
|
(cond
|
||||||
|
[(equal? t t2) t]
|
||||||
|
[(and (pair? t) (pair? t2)
|
||||||
|
(eq? (car t) 'array) (eq? (car t2) 'array)
|
||||||
|
(equal? (cadr t) (cadr t2)))
|
||||||
|
`(array ,(cadr t) ,(loop (caddr t) (caddr t2)))]
|
||||||
|
[else 'any])))))]
|
||||||
|
[(in-array . > . 1) 'any]
|
||||||
[(boolean? arg) 'boolean]
|
[(boolean? arg) 'boolean]
|
||||||
[(signed-int? arg 32) 'int]
|
[(signed-int? arg 32) 'int]
|
||||||
[(unsigned-int? arg 32) 'unsigned-int]
|
[(unsigned-int? arg 32) 'unsigned-int]
|
||||||
|
@ -1117,19 +1137,6 @@
|
||||||
[(real? arg) 'double]
|
[(real? arg) 'double]
|
||||||
[(com-object? arg) 'com-object]
|
[(com-object? arg) 'com-object]
|
||||||
[(IUnknown? arg) 'iunknown]
|
[(IUnknown? arg) 'iunknown]
|
||||||
[(vector? arg) `(array ,(vector-length arg)
|
|
||||||
,(if (zero? (vector-length arg))
|
|
||||||
'int
|
|
||||||
(for/fold ([t (arg-to-type (vector-ref arg 0))]) ([v (in-vector arg)])
|
|
||||||
(define t2 (arg-to-type v))
|
|
||||||
(let loop ([t t] [t2 t2])
|
|
||||||
(cond
|
|
||||||
[(equal? t t2) t]
|
|
||||||
[(and (pair? t) (pair? t2)
|
|
||||||
(eq? (car t) 'array) (eq? (car t2) 'array)
|
|
||||||
(equal? (cadr t) (cadr t2)))
|
|
||||||
`(array ,(cadr t) ,(loop (caddr t) (caddr t2)))]
|
|
||||||
[else 'any])))))]
|
|
||||||
[else (error 'com "cannot infer marshal format for value: ~e" arg)]))
|
[else (error 'com "cannot infer marshal format for value: ~e" arg)]))
|
||||||
|
|
||||||
(define (elem-desc-ref func-desc i)
|
(define (elem-desc-ref func-desc i)
|
||||||
|
@ -1342,6 +1349,8 @@
|
||||||
|
|
||||||
(define (ok-argument? arg type)
|
(define (ok-argument? arg type)
|
||||||
(cond
|
(cond
|
||||||
|
[(type-described? arg)
|
||||||
|
(ok-argument? (type-described-value arg) type)]
|
||||||
[(symbol? type)
|
[(symbol? type)
|
||||||
(case type
|
(case type
|
||||||
[(void) (void? arg)]
|
[(void) (void? arg)]
|
||||||
|
@ -1377,6 +1386,55 @@
|
||||||
(ok-argument? v (caddr type))))]
|
(ok-argument? v (caddr type))))]
|
||||||
[else #f]))
|
[else #f]))
|
||||||
|
|
||||||
|
(define (type-description? type)
|
||||||
|
(cond
|
||||||
|
[(symbol? type)
|
||||||
|
(hash-ref
|
||||||
|
#hasheq((void . #t)
|
||||||
|
(char . #t)
|
||||||
|
(unsigned-short . #t)
|
||||||
|
(unsigned-int . #t)
|
||||||
|
(unsigned-long-long . #t)
|
||||||
|
(signed-char . #t)
|
||||||
|
(short-int . #t)
|
||||||
|
(int . #t)
|
||||||
|
(long-long . #t)
|
||||||
|
(float . #t)
|
||||||
|
(double . #t)
|
||||||
|
(string . #t)
|
||||||
|
(currency . #t)
|
||||||
|
(date . #t)
|
||||||
|
(boolean . #t)
|
||||||
|
(scode . #t)
|
||||||
|
(iunknown . #t)
|
||||||
|
(com-object . #t)
|
||||||
|
(any . #t)
|
||||||
|
(com-enumeration . #t))
|
||||||
|
type
|
||||||
|
#f)]
|
||||||
|
[(and (list? type)
|
||||||
|
(pair? type))
|
||||||
|
(cond
|
||||||
|
[(eq? 'opt (car type))
|
||||||
|
(and (= (length type) 2)
|
||||||
|
(type-description? (cadr type)))]
|
||||||
|
[(eq? 'box (car type))
|
||||||
|
(and (= (length type) 2)
|
||||||
|
(type-description? (cadr type)))]
|
||||||
|
[(eq? 'array (car type))
|
||||||
|
(and (= (length type) 3)
|
||||||
|
(exact-positive-integer? (cadr type))
|
||||||
|
(type-description? (caddr type)))]
|
||||||
|
[else #f])]
|
||||||
|
[else #f]))
|
||||||
|
|
||||||
|
(struct type-described (value description))
|
||||||
|
|
||||||
|
(define (type-describe v desc)
|
||||||
|
(unless (type-description? desc)
|
||||||
|
(raise-type-error 'type-describe "type description" desc))
|
||||||
|
(type-described v desc))
|
||||||
|
|
||||||
(define (check-argument who method arg type)
|
(define (check-argument who method arg type)
|
||||||
(unless (ok-argument? arg type)
|
(unless (ok-argument? arg type)
|
||||||
(raise-type-error (string->symbol method) (format "~s" type) arg)))
|
(raise-type-error (string->symbol method) (format "~s" type) arg)))
|
||||||
|
@ -1395,6 +1453,8 @@
|
||||||
|
|
||||||
(define (scheme-to-variant! var a elem-desc scheme-type)
|
(define (scheme-to-variant! var a elem-desc scheme-type)
|
||||||
(cond
|
(cond
|
||||||
|
[(type-described? a)
|
||||||
|
(scheme-to-variant! var (type-described-value a) elem-desc scheme-type)]
|
||||||
[(eq? a com-omit)
|
[(eq? a com-omit)
|
||||||
(if (and elem-desc
|
(if (and elem-desc
|
||||||
(elem-desc-has-default? elem-desc))
|
(elem-desc-has-default? elem-desc))
|
||||||
|
@ -1538,7 +1598,7 @@
|
||||||
[else #f]))
|
[else #f]))
|
||||||
|
|
||||||
(define (to-vt type)
|
(define (to-vt type)
|
||||||
;; only used for inferred types
|
;; used for inferred or described types
|
||||||
(case type
|
(case type
|
||||||
[(void) VT_VOID]
|
[(void) VT_VOID]
|
||||||
[(char) VT_UI1]
|
[(char) VT_UI1]
|
||||||
|
|
|
@ -161,6 +161,7 @@ the object itself.}
|
||||||
Returns @racket[#t] if @racket[t1] and @racket[t2] represent the same
|
Returns @racket[#t] if @racket[t1] and @racket[t2] represent the same
|
||||||
type information, @racket[#f] otherwise.}
|
type information, @racket[#f] otherwise.}
|
||||||
|
|
||||||
|
|
||||||
@; ----------------------------------------
|
@; ----------------------------------------
|
||||||
|
|
||||||
@section{COM Methods}
|
@section{COM Methods}
|
||||||
|
@ -174,7 +175,8 @@ type information, @racket[#f] otherwise.}
|
||||||
|
|
||||||
@defproc[(com-method-type [obj/type (or/c com-object? com-type?)]
|
@defproc[(com-method-type [obj/type (or/c com-object? com-type?)]
|
||||||
[method-name string?])
|
[method-name string?])
|
||||||
(list/c '-> list? any/c)]{
|
(list/c '-> (listof type-description?)
|
||||||
|
type-description?)]{
|
||||||
|
|
||||||
Returns a list indicating the type of the specified method in
|
Returns a list indicating the type of the specified method in
|
||||||
@racket[obj/type]. The list after the @racket['->] represents the
|
@racket[obj/type]. The list after the @racket['->] represents the
|
||||||
|
@ -188,7 +190,14 @@ type information, @racket[#f] otherwise.}
|
||||||
Invokes @racket[method-name] on @racket[obj] with @racket[v]s as the
|
Invokes @racket[method-name] on @racket[obj] with @racket[v]s as the
|
||||||
arguments. The special value @racket[com-omit] may be used for
|
arguments. The special value @racket[com-omit] may be used for
|
||||||
optional arguments, which useful when values are supplied for
|
optional arguments, which useful when values are supplied for
|
||||||
arguments after the omitted argument(s).}
|
arguments after the omitted argument(s).
|
||||||
|
|
||||||
|
The types of arguments are determined via @racket[com-method-type],
|
||||||
|
if possible, and @racket[type-describe] wrappers in the @racket[v]s
|
||||||
|
are simply replaced with the values that they wrap. If the types are
|
||||||
|
not available from @racket[com-method-type], then types are inferred
|
||||||
|
for each @racket[v] with attention to descriptions in any
|
||||||
|
@racket[type-describe] wrappers in @racket[v].}
|
||||||
|
|
||||||
|
|
||||||
@defthing[com-omit any/c]{
|
@defthing[com-omit any/c]{
|
||||||
|
@ -210,7 +219,7 @@ argument.}
|
||||||
|
|
||||||
@defproc[(com-get-property-type [obj/type (or/c com-object? com-type?)]
|
@defproc[(com-get-property-type [obj/type (or/c com-object? com-type?)]
|
||||||
[property-name string?])
|
[property-name string?])
|
||||||
(list/c '-> '() any/c)]{
|
(list/c '-> '() type-description?)]{
|
||||||
|
|
||||||
Returns a type for @racket[property-name] like a result of
|
Returns a type for @racket[property-name] like a result of
|
||||||
@racket[com-method], where the result type corresponds to the
|
@racket[com-method], where the result type corresponds to the
|
||||||
|
@ -235,7 +244,7 @@ argument.}
|
||||||
|
|
||||||
@defproc[(com-set-property-type [obj/type (or/c com-object? com-type?)]
|
@defproc[(com-set-property-type [obj/type (or/c com-object? com-type?)]
|
||||||
[property-name string?])
|
[property-name string?])
|
||||||
(list/c '-> (list/c any/c) 'void)]{
|
(list/c '-> (list/c type-description?) 'void)]{
|
||||||
|
|
||||||
Returns a type for @racket[property-name] like a result of
|
Returns a type for @racket[property-name] like a result of
|
||||||
@racket[com-method], where the sole argument type corresponds to the
|
@racket[com-method], where the sole argument type corresponds to the
|
||||||
|
@ -250,7 +259,15 @@ argument.}
|
||||||
|
|
||||||
Sets the value of the final property in @racket[obj] to @racket[v]
|
Sets the value of the final property in @racket[obj] to @racket[v]
|
||||||
by following the @racket[property]s, where the value of each
|
by following the @racket[property]s, where the value of each
|
||||||
intermediate property must be a COM object.}
|
intermediate property must be a COM object.
|
||||||
|
|
||||||
|
The type of the property is determined via
|
||||||
|
@racket[com-property-type], if possible, and
|
||||||
|
@racket[type-describe] wrappers in @racket[v] are then replaced
|
||||||
|
with the values that they wrap. If the type is not available from
|
||||||
|
@racket[com-property-type], then a type is inferred for @racket[v]
|
||||||
|
with attention to the descriptions in any @racket[type-describe]
|
||||||
|
wrappers in @racket[v].}
|
||||||
|
|
||||||
@; ----------------------------------------
|
@; ----------------------------------------
|
||||||
|
|
||||||
|
@ -265,7 +282,7 @@ argument.}
|
||||||
|
|
||||||
@defproc[(com-event-type [obj/type (or/c com-object? com-type?)]
|
@defproc[(com-event-type [obj/type (or/c com-object? com-type?)]
|
||||||
[event-name string?])
|
[event-name string?])
|
||||||
(list/c '-> list? 'void)]{
|
(list/c '-> (listof type-description?) 'void)]{
|
||||||
|
|
||||||
Returns a list indicating the type of the specified events in
|
Returns a list indicating the type of the specified events in
|
||||||
@racket[obj/type]. The list after the @racket['->] represents the
|
@racket[obj/type]. The list after the @racket['->] represents the
|
||||||
|
@ -420,7 +437,7 @@ used to represent various atomic types:
|
||||||
|
|
||||||
@item{@racket['com-enumeration] --- a 32-bit signed integer}
|
@item{@racket['com-enumeration] --- a 32-bit signed integer}
|
||||||
|
|
||||||
@item{@racket['any] --- any of the above}
|
@item{@racket['any] --- any of the above, or an array when not nested in an array type}
|
||||||
|
|
||||||
@item{@racket['void] --- no value}
|
@item{@racket['void] --- no value}
|
||||||
|
|
||||||
|
@ -434,6 +451,49 @@ A type wrapped in a list with @racket['opt], such as @racket['(opt
|
||||||
(box int))], is an optional argument. The argument can be omitted or
|
(box int))], is an optional argument. The argument can be omitted or
|
||||||
replaced with @racket[com-omit].
|
replaced with @racket[com-omit].
|
||||||
|
|
||||||
|
A type wrapped in a list with @racket['array] and a positive exact
|
||||||
|
integer, such as @racket['(array 7 int)], represents a vector of
|
||||||
|
values to be used as a COM array. Array types can be nested to
|
||||||
|
specify a multidimensional array as represented by nested vectors.
|
||||||
|
|
||||||
|
When type information is not available, functions like @racket[com-invoke]
|
||||||
|
infer type descriptions from arguments. Inference chooses @racket['boolean]
|
||||||
|
for booleans; the first of @racket['int], @racket['unsigned-int],
|
||||||
|
@racket['long-long], @racket['unsigned-long-long] that fits for an exact integer;
|
||||||
|
@racket['double] for inexact real numbers; @racket['string] for a string;
|
||||||
|
@racket['com-object] and @racket['iunknown] for corresponding COM object references;
|
||||||
|
and an @racket['array] type for a vector, where the element type is inferred
|
||||||
|
from vector values, resorting to @racket['any] if any two elements have different
|
||||||
|
inferred types or if the array is multidimensional.
|
||||||
|
|
||||||
|
|
||||||
|
@defproc[(type-description? [v any/c]) boolean?]{
|
||||||
|
|
||||||
|
Return @racket[#t] if @racket[v] is a COM argument or result type
|
||||||
|
description as above, @racket[#f] otherwise.}
|
||||||
|
|
||||||
|
|
||||||
|
@deftogether[(
|
||||||
|
@defproc[(type-described? [v any/c]) boolean?]
|
||||||
|
@defproc[(type-describe [v any/c] [desc type-description?])
|
||||||
|
type-described?]
|
||||||
|
@defproc[(type-described-value [td type-described?]) any/c]
|
||||||
|
@defproc[(type-described-description [td type-described?])
|
||||||
|
type-description?]
|
||||||
|
)]{
|
||||||
|
|
||||||
|
The @racket[type-described?] predicate recognizes wrappers produced
|
||||||
|
with @racket[type-describe], and @racket[type-described-value] and
|
||||||
|
@racket[type-described-description] extract the value and description
|
||||||
|
parts of a @racket[type-describe] value.
|
||||||
|
|
||||||
|
A @racket[type-describe] wrapper combines a base value with a type
|
||||||
|
description. The description is used instead of an automatically
|
||||||
|
inferred COM argument type when no type is available for from COM
|
||||||
|
automation a method for @racket[com-invoke] or a property for
|
||||||
|
@racket[com-set-property!]. A wrapper can be placed on an immediate
|
||||||
|
value, or it can be on a value within a box or vector.}
|
||||||
|
|
||||||
@; ----------------------------------------
|
@; ----------------------------------------
|
||||||
|
|
||||||
@section{Class Display Names}
|
@section{Class Display Names}
|
||||||
|
|
|
@ -52,6 +52,8 @@
|
||||||
(test '(-> () void) (com-method-type mzcom "Reset"))
|
(test '(-> () void) (com-method-type mzcom "Reset"))
|
||||||
(test '(-> (string) string) (com-method-type mzcom "Eval"))
|
(test '(-> (string) string) (com-method-type mzcom "Eval"))
|
||||||
(test "3" (com-invoke mzcom "Eval" "(+ 1 2)"))
|
(test "3" (com-invoke mzcom "Eval" "(+ 1 2)"))
|
||||||
|
(test "4" (com-invoke mzcom "Eval" (type-describe "(+ 2 2)" 'string)))
|
||||||
|
(test "5" (com-invoke mzcom "Eval" (type-describe "(+ 3 2)" 'int))) ; description is not used
|
||||||
|
|
||||||
(test '() (com-get-properties mzcom))
|
(test '() (com-get-properties mzcom))
|
||||||
(test '() (com-get-properties (com-object-type mzcom)))
|
(test '() (com-get-properties (com-object-type mzcom)))
|
||||||
|
@ -132,6 +134,26 @@
|
||||||
|
|
||||||
(test (void) (com-release ie))
|
(test (void) (com-release ie))
|
||||||
|
|
||||||
|
(test #t (type-description? 'int))
|
||||||
|
(test #t (type-description? 'long-long))
|
||||||
|
(test #t (type-description? 'string))
|
||||||
|
(test #t (type-description? 'any))
|
||||||
|
(test #t (type-description? 'char))
|
||||||
|
(test #f (type-description? 'long))
|
||||||
|
(test #f (type-description? '(int)))
|
||||||
|
(test #t (type-description? '(opt int)))
|
||||||
|
(test #f (type-description? '(opt)))
|
||||||
|
(test #f (type-description? '(opt int int)))
|
||||||
|
(test #t (type-description? '(box int)))
|
||||||
|
(test #f (type-description? '(box)))
|
||||||
|
(test #f (type-description? '(box int int)))
|
||||||
|
(test #f (type-description? '(-> (int) int)))
|
||||||
|
(test #t (type-description? '(array 8 int)))
|
||||||
|
(test #f (type-description? '(array 8)))
|
||||||
|
(test #f (type-description? '(array)))
|
||||||
|
(test #f (type-description? '(array int)))
|
||||||
|
(test #t (type-description? '(array 8 (array 4 any))))
|
||||||
|
|
||||||
(void))
|
(void))
|
||||||
|
|
||||||
(printf "~a passed\n" count)
|
(printf "~a passed\n" count)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user