Help with ccall to a clib function


#1

Hi,

I’m trying to implement some routines from termios in clib. I have created the constants to represent the many #defines in the c header file, but I need help with a couple items (please). So, here’s a bit of the code:

@ctypedef tcflag_t Culong
@ctypedef cc_t Cuchar
@ctypedef speed_t Clong

struct termios
    c_iflag::tcflag_t	#input flags
    c_oflag::tcflag_t	#output flags
    c_cflag::tcflag_t	#control flags
    c_lflag::tcflag_t	#local flags
    c_cc::Ptr{cc_t} 	#control chars     #this is what it looks like in C -  cc_t     c_cc[NCCS]; 
    c_ispeed::speed_t	#input speed
    c_ospeed::speed_t	#output speed 
end

So, the first question is - am I translating an array of integers correctly in the struct?

The second question is - how do I initialize the struct (since it has an array in it)?

The C code looks like this:

struct  termios tty;
memset(&tty, 0, sizeof(tty));

this would be followed by:

if (tcgetattrib(fd, &tty) != 0)
{
    printf("error from tcgetattrib");
    return -1;
}

I translated that to:

tty = termios(0,0,0,0,0,0,0)  #I think the initialization is wrong

ret = ccall((:tcgetattr, "libc"), Int32, (Int32, Ref{termios}), fd, tty)

if (ret != 0)
   println("error from tcgetattr")
   return -1
end

Can someone help me along, here?

Frank


Isbits object lifetimes on 0.7
#2

(could you please edit your post and put tripple backquotes ``` around the code parts?)


#3

I think this is the correct way to handle this (not tested):

array = zeros(cc_t, N)
ptr = Base.unsafe_convert(Ptr{cc_t}, array ) #  unsafe! make sure to keep a reference to array alive
tty = termios(0,0,0,0,ptr ,0,0)
# C argument types need to be Ptr (the type closest to C), while Ref should be passed as an argument mirroring Ptr in C.
ret = ccall((:tcgetattr, "libc"), Int32, (Int32, Ptr{termios}), fd, Ref(tty))

Array pointers in structs are not that nicely documented :frowning:


#4

Basically don’t call unsafe_convert manually like this. The code here is allowed to crash.

Also this appears to be duplicated at Help with ccall to clib functions Note that this is not the correct way to declare inline array member (and note that C arrays are NOT pointer).


#5

Oh, I didn’t see the full comment about c_cc being c_cc[NCCS]

Basically don’t call unsafe_convert manually like this. The code here is allowed to crash.

Can you remind me what the correct way is, in case it’s not a statically sized array in C, but indeed a pointer?


#6

You should call Base.cconvert first and call Base.unsafe_convert on top of that. (Or call pointer on the array).
Also, you have to make sure that the result of the cconvert is live throughout the time you need to use the pointer.
There isn’t a well defined way to do this on 0.6 though depending on the context there are a few way to do it taking advantage of the optimizer deficiency (store the reference to a type and makes sure that wrapper type is live until the pointer is not needed works most of the time). As most of the optimizer issues are being fixed on 0.7 https://github.com/JuliaLang/julia/pull/23610 will be the way to do it.


#7

Thanks! Sorry for further derailing but I’m always tripping over this, so I want to make sure I understand it this time. Is this considered to be correct:

immutable JLTest
x::Vector{Cint}
end

immutable CTest
x::Ptr{Cint}
y::Any
end

function cconvert(::Type{CTest}, x::JLTest)
     CTest(pointer(x.x), x.x)
end
ccall(:mutate_in_c, Void, (Ctest,), x::JLTest)

#8

@yuyichao feel free to ignore this, on a second look your examples are pretty clear!


#9

The code is valid though

  1. I think the ccall should use CTest instead of Ctest
  2. Unless the C code actually want a struct { int *; void *; }; as input parameter (passed by value) you might need to change how you write the ccall. (The julia code you have is correct, just that the C API usually doesn’t look like this)

Other examples of valid cconvert def include CTest(pointer(x.x)), x.x or y = cconvert(Ptr{Cint}, x.x); CText(unsafe_convert(Ptr{Cint}, y), y) It is not necessary to store the reference in the struct for this case since it’s in cconvert and you are only dealing with ccall. This is the only case we guarantee on 0.4-0.6 about the rooting lifetime. The example I give is only necessary if you are to keep the converted result for more than a ccall or when you aren’t/can’t pass the object to be rooted to the ccall.