@@ -47,6 +47,31 @@ expression. If not provided, no error check is done. If it is a `String`, the st
47
47
as the message in an `error()` call, otherwise the expression is used as-is. Note that the
48
48
expression may refer to any arguments by name.
49
49
50
+ ## Special behaviors
51
+
52
+ **Library names:**
53
+
54
+ It is assumed that the HDF library names are given in global constants named `libhdf5`
55
+ and `libhdf5_hl`. The former is used for all `ccall`s, except if the C library name begins
56
+ with one of "H5DO", "H5DS", "H5L5", or "H5TB" then the latter library is used.
57
+
58
+ **Argument type:**
59
+
60
+ All arguments are automatically converted to the declared type following Julia's standard
61
+ [Calling C and Fortran Code](https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/)
62
+ rules, _except_ in the special case where the argument type is declared as `::Ref{Cvoid}`.
63
+ In this exceptional case, the argument `arg::Ref{Cvoid}` is transformed following the rule
64
+ ```julia
65
+ c_arg = arg isa Ref{<:Any} ? arg : Base.cconvert(Ref{eltype(arg)}, arg)
66
+ ```
67
+ and the `ccall` is expanded as if the original argument had been declared as
68
+ `c_arg::Ptr{Cvoid}`.
69
+ This conversion rule allows for automatically boxing scalars while passing through
70
+ explicit `Ref` values.
71
+ (`Array` arguments are wrapped with a benign `RefArray`.)
72
+
73
+ **Return types:**
74
+
50
75
The declared return type in the function-like signature must be the return type of the C
51
76
function, and the Julia return type is inferred as one of the following possibilities:
52
77
@@ -58,7 +83,9 @@ function, and the Julia return type is inferred as one of the following possibil
58
83
59
84
3. Otherwise, the C function return value is returned from the Julia function.
60
85
61
- Furthermore, the C return value is interpreted to automatically generate error checks
86
+ **Error checking:**
87
+
88
+ The C return value is also interpreted to automatically generate error checks
62
89
(only when `ErrorStringOrExpression` is provided):
63
90
64
91
1. If `ReturnType === :herr_t` or `ReturnType === :htri_t`, an error is raised when the return
@@ -71,10 +98,6 @@ Furthermore, the C return value is interpreted to automatically generate error c
71
98
equal to `C_NULL`.
72
99
73
100
3. For all other return types, it is assumed a negative value indicates error.
74
-
75
- It is assumed that the HDF library names are given in global constants named `libhdf5`
76
- and `libhdf5_hl`. The former is used for all `ccall`s, except if the C library name begins
77
- with "H5DO" or "H5TB" then the latter library is used.
78
101
"""
79
102
macro bind (sig:: Expr , err:: Union{String,Expr,Nothing} = nothing )
80
103
sig. head === :(:: ) || error (" return type required on function signature" )
@@ -90,15 +113,30 @@ macro bind(sig::Expr, err::Union{String,Expr,Nothing} = nothing)
90
113
funcargs = funcsig. args[2 : end ]
91
114
92
115
# Pull apart argument names and types
93
- args = Vector {Symbol} ()
94
- argt = Vector {Union{Expr,Symbol}} ()
116
+ args = Vector {Symbol} () # arguments in function signature
117
+ argv = Vector {Symbol} () # arguments passed in ccall
118
+ argt = Vector {Union{Expr,Symbol}} () # types of ccall arguments
119
+ argc = Vector {Expr} () # optional conversions taking args => argv
95
120
for ii in 1 : length (funcargs)
96
121
argex = funcargs[ii]
97
122
if ! isexpr (argex, :(:: )) || ! (argex. args[1 ] isa Symbol)
98
123
error (" expected `name::type` expression in argument " , ii, " , got " , funcargs[ii])
99
124
end
100
125
push! (args, argex. args[1 ])
101
- push! (argt, argex. args[2 ])
126
+ if isexpr (argex. args[2 ], :curly ) && argex. args[2 ] == :(Ref{Cvoid})
127
+ # Special case: maybe box the argument in Ref
128
+ arg = argex. args[1 ]:: Symbol
129
+ sym = Symbol (" #" , arg, " #" )
130
+ push! (argc, :($ sym = $ arg isa Ref{<: Any } ? $ arg : $ (GlobalRef (Base, :cconvert ))(Ref{eltype ($ arg)}, $ arg)))
131
+ push! (argv, sym)
132
+ push! (argt, :(Ptr{Cvoid}))
133
+ # in-place modify funcargs so that the doc generation (`string(funcsig)` below)
134
+ # shows the C argument type.
135
+ funcargs[ii]. args[2 ] = :(Ptr{Cvoid})
136
+ else
137
+ push! (argv, argex. args[1 ])
138
+ push! (argt, argex. args[2 ])
139
+ end
102
140
end
103
141
104
142
prefix, rest = split (string (jlfuncname), " _" , limit = 2 )
@@ -122,7 +160,7 @@ macro bind(sig::Expr, err::Union{String,Expr,Nothing} = nothing)
122
160
123
161
# The ccall(...) itself
124
162
cfunclib = Expr (:tuple , quot (cfuncname), lib)
125
- ccallexpr = :(ccall ($ cfunclib, $ rettype, ($ (argt... ),), $ (args ... )))
163
+ ccallexpr = :(ccall ($ cfunclib, $ rettype, ($ (argt... ),), $ (argv ... )))
126
164
127
165
# The error condition expression
128
166
errexpr = err isa String ? :(error ($ err)) : err
@@ -157,7 +195,11 @@ macro bind(sig::Expr, err::Union{String,Expr,Nothing} = nothing)
157
195
# avoids inserting the line number nodes for the macro --- the call site
158
196
# is instead explicitly injected into the function body via __source__.
159
197
jlfuncsig = Expr (:call , jlfuncname, args... )
160
- jlfuncbody = Expr (:block , __source__, :($ statsym = $ ccallexpr))
198
+ jlfuncbody = Expr (:block , __source__)
199
+ if ! isempty (argc)
200
+ append! (jlfuncbody. args, argc)
201
+ end
202
+ push! (jlfuncbody. args, :($ statsym = $ ccallexpr))
161
203
if errexpr != = nothing
162
204
push! (jlfuncbody. args, errexpr)
163
205
end
0 commit comments