Ferenc
Ferenc

Reputation: 874

ccall with Array type in signature calling a struct in C from Julia

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

Answers (2)

Simon Byrne
Simon Byrne

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

Ferenc
Ferenc

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

Related Questions