ccall with an opaque struct

132 views
Skip to first unread message

Andrew Adare

unread,
Feb 11, 2016, 10:35:37 AM2/11/16
to julia-users
I'm trying to call out to some library functions from libserialport. It is a small c library that makes extensive use of opaque structs in the API. In c I can do the following:

#include <libserialport.h>
#include <stdio.h>


// Compile:
// gcc -o example example.c $(pkg-config --cflags --libs libserialport)


int main(int argc, char *argv[])
{
 
char *uri = argv[1];
 
struct sp_port *port;


 
if (sp_get_port_by_name(uri, &port) == SP_OK)
 
{
   
char *portname = sp_get_port_name(port);
   
char *portdesc = sp_get_port_description(port);
    printf
("Port name: %s\n", portname);
    printf
("Description: %s\n", portdesc);
 
}
 
else
    printf
("Could not find %s\n", uri);
 
return 0;
}


As an example, if I run this with an ARM STM32 microcontroller plugged in to my USB port, I get the following correct output (on OS X):

$ ./example /dev/cu.usbmodem1413
Port name: /dev/cu.usbmodem1413
Description: STM32 STLink

I want to call sp_get_port_by_name from Julia. It has this signature:

enum sp_return sp_get_port_by_name(const char *portname, struct sp_port **port_ptr);

The ccall docs advise declaring an empty type as a placeholder for an opaque struct, then passing in a reference to it. I've tried many wrong things, such as the following:

julia> type Port end

julia
> p = Ref{Port}()
Base.RefValue{Port}(#undef)

julia
> ccall((:sp_get_port_by_name, "libserialport"),Cint,(AbstractString, Ref{Ref{Port}}),"/dev/cu.usbmodem1413",p)
0

julia
> p
Base.RefValue{Port}(Segmentation fault: 11


Any tips? The double pointer is causing me some confusion.

Andrew
Message has been deleted

Lutfullah Tomak

unread,
Feb 11, 2016, 11:24:07 AM2/11/16
to julia-users
p = Ptr{Int}[0]
ccall((:sp_get_port_by_name, "libserialport"),Cint,(AbstractString, Ref{Ptr{Int}}),"/dev/cu.usbmodem1413",p)

or

immutable Port
portpointer::Ptr{Void}
end
p=Port(0)
ccall((:sp_get_port_by_name, "libserialport"),Cint,(AbstractString, Ref{Port}),"/dev/cu.usbmodem1413",p)

Andrew Adare

unread,
Feb 11, 2016, 12:28:15 PM2/11/16
to julia-users
Thanks for the suggestions, but neither appear to work. The first produces a nonzero pointer,

julia> p = Ptr{Int}[0]
1-element Array{Ptr{Int64},1}:
 
Ptr{Int64} @0x0000000000000000


julia
> ccall((:sp_get_port_by_name, "libserialport"),Cint,(AbstractString, Ref{Ptr{Int}}),"/dev/cu.usbmodem1413",p)
0


julia
> p
1-element Array{Ptr{Int64},1}:
 
Ptr{Int64} @0x00007fb4d8614f70


but the allocated buffer appears to be gibberish:

julia> bytestring(ccall((:sp_get_port_name, "libserialport"), Ptr{Cchar}, (typeof(p),), p))
"pOaش\x7f"


julia
> bytestring(ccall((:sp_get_port_name, "libserialport"), Ptr{Cchar}, (Ptr{Int},), p[]))
"\x10!}\b\x01"


And unfortunately the second approach just produces a null pointer:

julia> immutable Port
           portpointer
::Ptr{Void}
       
end


julia
> p = Port(0)
Port(Ptr{Void} @0x0000000000000000)


julia
> ccall((:sp_get_port_by_name, "libserialport"),Cint,(AbstractString, Ref{Port}),"/dev/cu.usbmodem1413",p)
0


julia
> p
Port(Ptr{Void} @0x0000000000000000)


So I'm still stumped...any further help is appreciated.

Andrew

Yichao Yu

unread,
Feb 11, 2016, 1:04:53 PM2/11/16
to Julia Users
On Thu, Feb 11, 2016 at 12:28 PM, Andrew Adare <andre...@gmail.com> wrote:
> Thanks for the suggestions, but neither appear to work. The first produces a
> nonzero pointer,
>
> julia> p = Ptr{Int}[0]
> 1-element Array{Ptr{Int64},1}:
> Ptr{Int64} @0x0000000000000000
>
>
> julia> ccall((:sp_get_port_by_name, "libserialport"),Cint,(AbstractString,
> Ref{Ptr{Int}}),"/dev/cu.usbmodem1413",p)
> 0
>
>
> julia> p
> 1-element Array{Ptr{Int64},1}:
> Ptr{Int64} @0x00007fb4d8614f70
>
>
> but the allocated buffer appears to be gibberish:
>
> julia> bytestring(ccall((:sp_get_port_name, "libserialport"), Ptr{Cchar},
> (typeof(p),), p))
> "pOaش\x7f"
>
>
> julia> bytestring(ccall((:sp_get_port_name, "libserialport"), Ptr{Cchar},
> (Ptr{Int},), p[]))
> "\x10!}\b\x01"

p = Ref{Ptr{Void}}()
ccall((:sp_get_port_by_name, "libserialport"),Cint,(Cstring,
Ref{Ptr{Void}}),"/dev/cu.usbmodem1413",p)
bytestring(ccall((:sp_get_port_name, "libserialport"), Ptr{Cchar},
(Ptr{Void},), p[]))

Andrew Adare

unread,
Feb 11, 2016, 2:54:09 PM2/11/16
to julia-users
Yes!! That works:

julia> p = Ref{Ptr{Void}}()
Base.RefValue{Ptr{Void}}(Ptr{Void} @0x0000000000000000)

julia
> ccall((:sp_get_port_by_name, "libserialport"),Cint,(Cstring, Ref{Ptr{Void}}), "/dev/cu.usbmodem1413", p)
0

julia
> bytestring(ccall((:sp_get_port_name, "libserialport"), Ptr{Cchar}, (Ptr{Void},), p[]))
"/dev/cu.usbmodem1413"

julia
> bytestring(ccall((:sp_get_port_description, "libserialport"), Ptr{Cchar}, (Ptr{Void},), p[]))
"STM32 STLink"
and so forth.

Based on your help, I'm now also able to ccall a function with this signature

enum sp_return sp_list_ports(struct sp_port ***list_ptr);

as folllows:

julia> ports = Ref{Ptr{Ptr{Void}}}()

julia
> ccall((:sp_list_ports, "libserialport"), Cint, (Ref{Ptr{Ptr{Void}}},), ports)

julia
> ports
Base.RefValue{Ptr{Ptr{Void}}}(Ptr{Ptr{Void}} @0x00007ffe8d8f2a00)

julia
> a = pointer_to_array(ports[], (2,), true)
2-element Array{Ptr{Void},1}:
 
Ptr{Void} @0x00007ffe8d9617e0
 
Ptr{Void} @0x00007ffe8d989990
julia
> bytestring(ccall((:sp_get_port_name, "libserialport"), Ptr{Cchar}, (Ptr{Void},), a[1]))
"/dev/cu.Bluetooth-Incoming-Port"

julia
> bytestring(ccall((:sp_get_port_name, "libserialport"), Ptr{Cchar}, (Ptr{Void},), a[2]))
"/dev/cu.usbmodem1413"

Maybe that's useful to others who, like me, find call signatures like this to be a bit tricky.

Many thanks,
Andrew

Lutfullah Tomak

unread,
Feb 11, 2016, 3:39:01 PM2/11/16
to julia-users
Sorry I couldn't test my examples. I'm glad someone more knowledgeable corrects the code.

I think the second one I'm suggesting needs to be

p=Ref{Port}() #looking Yuchiao comment.

I learn myself too, maybe I should look at docs again. Ref confuses me.

Yichao Yu

unread,
Feb 11, 2016, 3:40:56 PM2/11/16
to Julia Users
On Thu, Feb 11, 2016 at 3:39 PM, Lutfullah Tomak <tomak...@gmail.com> wrote:
> Sorry I couldn't test my examples. I'm glad someone more knowledgeable corrects the code.
>
> I think the second one I'm suggesting needs to be
>
> p=Ref{Port}() #looking Yuchiao comment.

Using a mutable type could probably work.

Lutfullah Tomak

unread,
Feb 11, 2016, 3:50:45 PM2/11/16
to julia-users
Hi Yuchiao. Thanks for advice.
Reply all
Reply to author
Forward
0 new messages