Reputation: 874
I am having trouble with calling a C function from Julia. This may be generally useful question, but I'll describe it here in the concrete setting I am struggling with. I am trying to create a bson
object:
BSONObject("{a:1}")
so this object's constructor is called:
BSONObject(jsonString::String) = begin
jsonCStr = bytestring(jsonString)
bsonError = BSONError()
_wrap_ = ccall(
(:bson_new_from_json, libbson),
Ptr{Void}, (Ptr{Uint8}, Csize_t, Ptr{Uint8}),
jsonCStr,
length(jsonCStr),
bsonError._wrap_
)
_wrap_ != C_NULL || error(bsonError)
bsonObject = new(_wrap_, None)
finalizer(bsonObject, destroy)
return bsonObject
end
in the https://github.com/pzion/LibBSON.jl/blob/master/src/BSONObject.jl LibBSON package needed to handle MongoDB queries, but the setting is not particularly important. What is important is the ccall
, which passes a string, jsonCStr
, this string's length, and bsonError._wrap_
. This last object comes from https://github.com/pzion/LibBSON.jl/blob/master/src/BSONError.jl and is an array:
type BSONError
_wrap_::Vector{Uint8}
function BSONError()
return new(Array(Uint8, 512))
end
end
created in the above constructor of BSONError
object, an array of 512 Uint8
's. This Julia bsonError._wrap_
refers to the following struct
in C:
typedef struct
{
uint32_t domain;
uint32_t code;
char message[504];
} bson_error_t;
see on http://api.mongodb.org/libbson/current/bson_error_t.html, and this struct is of length 4 + 4 + 504 = 512, so it looks OK.
Now going back to the ccall
, its type signature is Ok: Ptr{Uint8}
points to the string, Csize_t
is the type of its size, and Ptr{Uint8} points to the struct
. This latter, however, returns with an error message:
LoadError: MethodError: `convert` has no method matching convert(::Type{Ptr{UInt8}}, ::Array{UInt8,1})
This may have arisen from a call to the constructor Ptr{UInt8}(...),
since type constructors fall back to convert methods.
Closest candidates are:
call{T}(::Type{T}, ::Any)
convert{T<:Union{Int8,UInt8}}(::Type{Ptr{T<:Union{Int8,UInt8}}}, !Matched::Cstring)
convert{T}(::Type{Ptr{T}}, !Matched::UInt64)
...
while loading In[2], in expression starting on line 1
in convert at /Users/szalmaf/.julia/v0.4/LibBSON/src/BSONError.jl:21
in call at /Users/szalmaf/.julia/v0.4/LibBSON/src/BSONObject.jl:33
apparently, trying to convert the Array
to a type Ptr{UInt8}
.
The Julia manual http://julia.readthedocs.org/en/latest/manual/calling-c-and-fortran-code/#mapping-c-types-to-julia says in the 'Mapping C Types to Julia' section's 'Bits Types' subsection that a Julia Array{T,N}
should be passed as Ptr{T}
, where T
is UInt8
in this case. So the Julia ccall
looks ok, but there is still that error message. It is a pretty burning problem since it prevents more complex queries in the database. Any suggestions as to how to remedy this ccall
problem?
P.S. Note if you install the Mongo package it comes with the LibBSON package and the libbson C library.
Upvotes: 2
Views: 684
Reputation: 7864
The problem is unrelated to the ccall
. It is caused by this line, as indicated in the stack trace.
In 0.4, there is no longer a convert{T}(::Type{Ptr{T}},Array{T})
function.
ccall
calls converts its argument using the (non-exported) unsafe_convert
method (which is why the above code doesn't cause an error). If you want a Ptr
object in user code, the easiest way is to use the pointer
method.
Upvotes: 2
Reputation: 874
It turns out the BSONError.jl has had a bug, the
return bytestring(convert(Ptr{Uint8}, bsonError._wrap_[9:end]))
should be
return bytestring(bsonError._wrap_[9:end])
in the BSONError.jl's convert
function. That is, the error could not be printed out because the error printing is buggy.
Fixing that, from the error message received form C it turns out that the correct bson format is
BSONObject("{\"a\":1}")
to create a bson object.
Upvotes: 1