From db70626b65e05d5746b544471e373d303f3b3311 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 9 Feb 2015 01:19:27 -0500 Subject: [PATCH] write calling-c-and-fortran-code manual section for new ccall/cfunction/Ref types, and improve error messages in code --- doc/manual/calling-c-and-fortran-code.rst | 829 +++++++++++++++------- src/codegen.cpp | 56 +- test/ccall.jl | 65 +- 3 files changed, 666 insertions(+), 284 deletions(-) diff --git a/doc/manual/calling-c-and-fortran-code.rst b/doc/manual/calling-c-and-fortran-code.rst index c9acfc34f2eae..f883932870905 100644 --- a/doc/manual/calling-c-and-fortran-code.rst +++ b/doc/manual/calling-c-and-fortran-code.rst @@ -50,13 +50,16 @@ Finally, you can use ``ccall`` to actually generate a call to the library function. Arguments to ``ccall`` are as follows: 1. (:function, "library") pair (must be a constant, but see below). -2. Return type, which may be any bits type, including ``Int32``, - ``Int64``, ``Float64``, or ``Ptr{T}`` for any type parameter ``T``, - indicating a pointer to values of type ``T``, or ``Ptr{Void}`` for - ``void*`` "untyped pointer" values. -3. A tuple of input types, like those allowed for the return type. - The input types must be written as a literal tuple, not a tuple-valued - variable or expression. + +2. Return type (see below for mapping the declared C type to Julia) + + - This argument will be evaluated at compile-time. + +3. A tuple of input types. The input types must be written as a literal tuple, + not a tuple-valued variable or expression. + + - This argument will be evaluated at compile-time. + 4. The following arguments, if any, are the actual argument values passed to the function. @@ -77,7 +80,7 @@ is that a 1-tuple must be written with a trailing comma. For example, to call the ``getenv`` function to get a pointer to the value of an environment variable, one makes a call like this:: - julia> path = ccall( (:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), "SHELL") + julia> path = ccall((:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), "SHELL") Ptr{UInt8} @0x00007fff5fbffc45 julia> bytestring(path) @@ -85,7 +88,8 @@ of an environment variable, one makes a call like this:: Note that the argument type tuple must be written as ``(Ptr{UInt8},)``, rather than ``(Ptr{UInt8})``. This is because ``(Ptr{UInt8})`` is just -``Ptr{UInt8}``, rather than a 1-tuple containing ``Ptr{UInt8}``:: +the expression ``Ptr{UInt8}`` surrounded by parentheses, rather than +a 1-tuple containing ``Ptr{UInt8}``:: julia> (Ptr{UInt8}) Ptr{UInt8} @@ -104,7 +108,7 @@ in `env.jl `_:: function getenv(var::AbstractString) - val = ccall( (:getenv, "libc"), + val = ccall((:getenv, "libc"), Ptr{UInt8}, (Ptr{UInt8},), var) if val == C_NULL error("getenv: undefined variable: ", var) @@ -129,10 +133,11 @@ machine's hostname:: function gethostname() hostname = Array(UInt8, 128) - ccall( (:gethostname, "libc"), Int32, - (Ptr{UInt8}, UInt), - hostname, length(hostname)) - return bytestring(convert(Ptr{UInt8}, hostname)) + ccall((:gethostname, "libc"), Int32, + (Ptr{UInt8}, Csize_t), + hostname, sizeof(hostname)) + hostname[end] = 0; # ensure null-termination + return bytestring(pointer(hostname)) end This example first allocates an array of bytes, then calls the C library @@ -144,248 +149,545 @@ memory to be passed to the callee and filled in. Allocation of memory from Julia like this is generally accomplished by creating an uninitialized array and passing a pointer to its data to the C function. -A prefix ``&`` is used to indicate that a pointer to a scalar argument -should be passed instead of the scalar value itself (required for all -Fortran function arguments, as noted above). The following -example computes a dot product using a BLAS function. +Creating C-Compatible Julia Function Pointers +--------------------------------------------- -:: +It is possible to pass Julia functions to native functions that accept function +pointer arguments by creating function pointers via the `cfunction` function. - function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) - assert(length(DX) == length(DY)) - n = length(DX) - incx = incy = 1 - product = ccall( (:ddot_, "libLAPACK"), - Float64, - (Ptr{Int32}, Ptr{Float64}, Ptr{Int32}, Ptr{Float64}, Ptr{Int32}), - &n, DX, &incx, DY, &incy) - return product +Finally, you can use ``cfunction`` to actually generate a call to the +Julia library function. Arguments to ``cfunction`` are as follows: + +1. A Julia Function + +2. Return type + +3. A tuple of input types + +A classic example is the standard C library ``qsort`` function, +declared as:: + + void qsort(void *base, size_t nmemb, size_t size, + int(*compare)(const void *a, const void *b)); + +The ``base`` argument is a pointer to an array of length ``nmemb``, with elements of +``size`` bytes each. ``compare`` is a callback function which takes pointers to two +elements ``a`` and ``b`` and returns an integer less/greater than zero if ``a`` should +appear before/after ``b`` (or zero if any order is permitted). Now, suppose that we +have a 1d array ``A`` of values in Julia that we want to sort using the ``qsort`` +function (rather than Julia’s built-in ``sort`` function). Before we worry about calling +``qsort`` and passing arguments, we need to write a comparison function that works for +some arbitrary type T:: + + function mycompare{T}(a::T, b::T) + return convert(Cint, a < b ? -1 : a > b ? +1 : 0)::Cint end -The meaning of prefix ``&`` is not quite the same as in C. In -particular, any changes to the referenced variables will not be -visible in Julia unless the type is mutable (declared via -``type``). However, even for immutable types it will not cause any -harm for called functions to attempt such modifications (that is, -writing through the passed pointers). Moreover, ``&`` may be used with -any expression, such as ``&0`` or ``&f(x)``. +Notice that we have to be careful about the return type: ``qsort`` expects a function +returning a C ``int``, so we must be sure to return ``Cint`` via a call to ``convert`` +and a ``typeassert``. + +In order to pass this function to C, we obtain its address using the function ``cfunction``:: + + const mycompare_c = cfunction(mycompare, Cint, (Ref{Cdouble}, Ref{Cdouble})) + +``cfunction`` accepts three arguments: the Julia function (``mycompare``), the return +type (``Cint``), and a tuple of the argument types, in this case to sort an array of +``Cdouble`` (Float64) elements. + +The final call to ``qsort`` looks like this:: + + A = [1.3, -2.7, 4.4, 3.1] + ccall(:qsort, Void, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Void}), + A, length(A), sizeof(eltype(A)), mycompare_c) + +After this executes, ``A`` is changed to the sorted array ``[-2.7, 1.3, 3.1, 4.4]``. +Note that Julia knows how to convert an array into a ``Ptr{Cdouble}``, how to compute +the size of a type in bytes (identical to C’s ``sizeof`` operator), and so on. +For fun, try inserting a ``println("mycompare($a,$b)")`` line into ``mycompare``, which +will allow you to see the comparisons that ``qsort`` is performing (and to verify that +it is really calling the Julia function that you passed to it). -Currently, it is not possible to reliably pass structs and other non-primitive -types by *value* from Julia to/from C libraries. However, *pointers* -to structs can be passed. The simplest case is that of C functions -that generate and use *opaque* pointers to struct types, which can be -passed to/from Julia as ``Ptr{Void}`` (or any other ``Ptr`` -type). Memory allocation and deallocation of such objects must be -handled by calls to the appropriate cleanup routines in the libraries -being used, just like in any C program. A more complicated approach -is to declare a composite type in Julia that mirrors a C struct, which -allows the structure fields to be directly accessed in Julia. Given a -Julia variable ``x`` of that type, a pointer can be passed as ``&x`` -to a C function expecting a pointer to the corresponding struct. If -the Julia type ``T`` is ``immutable``, then a Julia ``Array{T}`` is -stored in memory identically to a C array of the corresponding struct, -and can be passed to a C program expecting such an array pointer. - -Note that no C header files are used anywhere in the process: you are -responsible for making sure that your Julia types and call signatures -accurately reflect those in the C header file. (The `Clang package -` can be used to generate Julia -code from a C header file.) Mapping C Types to Julia ------------------------ +It is critical to exactly match the declared C type with its declaration +in Julia. Inconsistencies can cause code that works correctly on one system +to fail or produce indeterminate results on a different system. + +Note that no C header files are used anywhere in the process of calling C +functions: you are responsible for making sure that your Julia types and +call signatures accurately reflect those in the C header file. (The `Clang +package` can be used to auto-generate +Julia code from a C header file.) + +Auto-conversion: +~~~~~~~~~~~~~~~~ + Julia automatically inserts calls to the ``convert`` function to convert each argument to the specified type. For example, the following call:: - ccall( (:foo, "libfoo"), Void, (Int32, Float64), - x, y) + ccall((:foo, "libfoo"), Void, (Int32, Float64), x, y) will behave as if the following were written:: - ccall( (:foo, "libfoo"), Void, (Int32, Float64), - convert(Int32, x), convert(Float64, y)) + ccall((:foo, "libfoo"), Void, (Int32, Float64), + Base.cconvert(Int32, Base.cconvert_gcroot(Int32, x)), + Base.cconvert(Float64, Base.cconvert_gcroot(Float64, y))) -When a scalar value is passed with ``&`` as an argument of type -``Ptr{T}``, the value will first be converted to type ``T``. +Note that the primary fall-back method for ``cconvert`` is:: -Array conversions -~~~~~~~~~~~~~~~~~ + cconvert(T,x) = convert(T, x) + +and the primary fallback method for ``cconvert_gcroot`` is:: + + cconvert_gcroot(T,x) = x + +Type Correspondences: +~~~~~~~~~~~~~~~~~~~~~ + +First, a review of some relevant Julia type terminology: -When an array is passed to C as a ``Ptr{T}`` argument, it is -never converted: Julia simply checks that the element type of the -array matches ``T``, and the address of the first element is passed. -This is done in order to avoid copying arrays unnecessarily. +.. rst-class:: text-wrap -Therefore, if an ``Array`` contains data in the wrong format, it will -have to be explicitly converted using a call such as ``Vector{Int32}(a)``. +============================== ============================== ====================================================== +Syntax / Keyword Example Description +============================== ============================== ====================================================== +``type`` ``ASCIIString`` "Leaf Type" :: A group of related data that includes + a type-tag, is managed by the Julia GC, and + is defined by object-identity -To pass an array ``A`` as a pointer of a different type *without* -converting the data beforehand (for example, to pass a ``Float64`` array -to a function that operates on uninterpreted bytes), you can either -declare the argument as ``Ptr{Void}`` or you can explicitly call -``convert(Ptr{T}, pointer(A))``. +``abstract`` ``Any``, "Super Type" :: A super-type (not a leaf-type) + ``AbstractArray{T,N}``, that cannot be instantiated, but can be used to + ``Complex{T}`` describe a group of types +``{T}`` ``Vector{Int}`` "Type Parameter" :: A specialization of a type + (typically used to dispatch or storage optimization) + +``bitstype`` ``Int``, "Bits Type" :: A type with no fields, but a size. It + ``Float64`` is stored and defined by-value + +``immutable`` ``Pair{String,String}`` "Immutable" :: A type with all fields defined to be + constant. It is defined by-value. And may be stored + with a type-tag. + + ``Complex128`` (`isbits`) "Is-Bits" :: A ``bitstype``, or an ``immutable`` type + where all fields are other ``isbits`` types. It is + defined by-value, and is stored without a type-tag + +``type ...; end`` ``nothing`` "Singleton" :: a Leaf Type or Immutable with no fields + +``(...)`` or ``tuple(...)``` ``(1,2,3)`` "Tuple" :: an immutable data-structure similar to an + anonymous immutable type, or a constant array. it's + storage semantics are TBD. + +``typealias`` Not applicable here Type aliases, and other similar mechanisms of + doing type indirection, are resolved to their base + type (this includes assigning a type to another name, + or getting the type out of a function call) +============================== ============================== ====================================================== + +Bits Types: +~~~~~~~~~~~ + +There are several special types to be aware of, as no other type can be defined to behave the same: + +:Float32: Exactly corresponds to the ``float`` type in C (or ``REAL*4`` in Fortran) +:Float64: Exactly corresponds to the ``double`` type in C (or ``REAL*8`` in Fortran) +:Complex64: Exactly corresponds to the ``complex float`` type in C (or ``COMPLEX*8`` in Fortran) +:Complex128: Exactly corresponds to the ``complex double`` type in C (or ``COMPLEX*16`` in Fortran) +:Signed: Exactly corresponds to the ``signed`` type annotation in C (or any ``INTEGER`` type in Fortran). Any Julia type that is not a subtype of ``Signed`` is assumed to be unsigned +:Ref{T}: Behaves like a ``Ptr{T}`` that owns it's memory +:Array{T,N}: + When an array is passed to C as a ``Ptr{T}`` argument, it is + not reinterpret-cast: Julia requires that the element type of the + array matches ``T``, and then address of the first element is passed. + + Therefore, if an ``Array`` contains data in the wrong format, it will + have to be explicitly converted using a call such as ``int32(a)``. + + To pass an array ``A`` as a pointer of a different type *without* + converting the data beforehand (for example, to pass a ``Float64`` array + to a function that operates on uninterpreted bytes), you can either + declare the argument as ``Ptr{Void}`` or you can explicitly call + ``pointer(A)``. + + If an array of eltype ``Ptr{T}`` is passed as a ``Ptr{Ptr{T}}`` argument, the Julia base library + `cconvert_gcroot` function will attempt to first make a null-terminated copy of the array with + each element replaced by it's ``cconvert`` version. This allows, for example, passing an ``argv`` + pointer array of type ``Vector{ByteString}`` to an argument of type ``Ptr{Ptr{Cchar}}``. -Type correspondences -~~~~~~~~~~~~~~~~~~~~ On all systems we currently support, basic C/C++ value types may be translated to Julia types as follows. Every C type also has a corresponding Julia type with the same name, prefixed by C. This can help for writing portable code (and remembering that an int in C is not the same as an Int in Julia). -**System-independent:** - -+------------------------+-------------------+--------------------------------+ -| ``unsigned char`` | ``Cuchar`` | ``UInt8`` | -+------------------------+-------------------+--------------------------------+ -| ``short`` | ``Cshort`` | ``Int16`` | -+------------------------+-------------------+--------------------------------+ -| ``unsigned short`` | ``Cushort`` | ``UInt16`` | -+------------------------+-------------------+--------------------------------+ -| ``int`` | ``Cint`` | ``Int32`` | -+------------------------+-------------------+--------------------------------+ -| ``unsigned int`` | ``Cuint`` | ``UInt32`` | -+------------------------+-------------------+--------------------------------+ -| ``long long`` | ``Clonglong`` | ``Int64`` | -+------------------------+-------------------+--------------------------------+ -| ``unsigned long long`` | ``Culonglong`` | ``UInt64`` | -+------------------------+-------------------+--------------------------------+ -| ``intmax_t`` | ``Cintmax_t`` | ``Int64`` | -+------------------------+-------------------+--------------------------------+ -| ``uintmax_t`` | ``Cuintmax_t`` | ``UInt64`` | -+------------------------+-------------------+--------------------------------+ -| ``float`` | ``Cfloat`` | ``Float32`` | -+------------------------+-------------------+--------------------------------+ -| ``double`` | ``Cdouble`` | ``Float64`` | -+------------------------+-------------------+--------------------------------+ -| ``ptrdiff_t`` | ``Cptrdiff_t`` | ``Int`` | -+------------------------+-------------------+--------------------------------+ -| ``ssize_t`` | ``Cssize_t`` | ``Int`` | -+------------------------+-------------------+--------------------------------+ -| ``size_t`` | ``Csize_t`` | ``UInt`` | -+------------------------+-------------------+--------------------------------+ -| ``void`` | | ``Void`` | -+------------------------+-------------------+--------------------------------+ -| ``void*`` | | ``Ptr{Void}`` | -+------------------------+-------------------+--------------------------------+ -| ``char*`` (or ``char[]``, e.g. a string) | ``Ptr{UInt8}`` | -+------------------------+-------------------+--------------------------------+ -| ``char**`` (or ``*char[]``) | ``Ptr{Ptr{UInt8}}`` | -+------------------------+-------------------+--------------------------------+ -| ``struct T*`` (where T represents an | ``Ptr{T}`` (call using | -| appropriately defined bits type) | &variable_name in the | -| | parameter list) | -+------------------------+-------------------+--------------------------------+ -| ``jl_value_t*`` (any Julia Type) | ``Ptr{Any}`` | -+------------------------+-------------------+--------------------------------+ - -Julia's ``Char`` type is 32 bits, which is not the same as the wide -character type (``wchar_t`` or ``wint_t``) on all platforms. - -A C function declared to return ``void`` will give ``nothing`` in Julia. +**System Independent:** + +.. rst-class:: text-wrap + ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| C name | Fortran name | Standard Julia Alias | Julia Base Type | ++===================================+=================+======================+===================================+ +| ``unsigned char`` | ``CHARACTER`` | ``Cuchar`` | ``UInt8`` | +| | | | | +| ``bool`` (`C++`) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``short`` | ``INTEGER*2`` | ``Cshort`` | ``Int16`` | +| | | | | +| | ``LOGICAL*2`` | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``unsigned short`` | | ``Cushort`` | ``UInt16`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``int`` | ``INTEGER*4`` | ``Cint`` | ``Int32`` | +| | | | | +| ``BOOL`` (`C`, typical) | ``LOGICAL*4`` | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``unsigned int`` | | ``Cuint`` | ``UInt32`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``long long`` | ``INTEGER*8`` | ``Clonglong`` | ``Int64`` | +| | | | | +| | ``LOGICAL*8`` | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``unsigned long long`` | | ``Culonglong`` | ``UInt64`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``intmax_t`` | | ``Cintmax_t`` | ``Int64`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``uintmax_t`` | | ``Cuintmax_t`` | ``UInt64`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``float`` | ``REAL*4i`` | ``Cfloat`` | ``Float32`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``double`` | ``REAL*8`` | ``Cdouble`` | ``Float64`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``complex float`` | ``COMPLEX*8`` | ``Complex64`` | ``Complex{Float32}`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``complex double`` | ``COMPLEX*16`` | ``Complex128`` | ``Complex{Float64}`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``ptrdiff_t`` | | ``Cptrdiff_t`` | ``Int`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``ssize_t`` | | ``Cssize_t`` | ``Int`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``size_t`` | | ``Csize_t`` | ``UInt`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``void`` | | | ``Void`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``void*`` | | | ``Ptr{Void}`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``T*`` (where T represents an | | | ``Ref{T}`` | +| appropriately defined type) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``char*`` | ``CHARACTER*N`` | | ``Ptr{UInt8}`` | +| (or ``char[]``, e.g. a string) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``char**`` (or ``*char[]``) | | | ``Ptr{Ptr{UInt8}}`` | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``jl_value_t*`` | | | ``Any`` | +| (any Julia Type) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``jl_value_t**`` | | | ``Ref{Any}`` | +| (a reference to a Julia Type) | | | | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``va_arg`` | | | Not supported | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ +| ``...`` | | | ```T...``` (where ``T`` | +| (variadic function specification) | | | is one of the above types, | +| | | | variadic functions of different | +| | | | argument types are not supported) | ++-----------------------------------+-----------------+----------------------+-----------------------------------+ **System-dependent:** -====================== ============== ======= -``char`` ``Cchar`` ``Int8`` (x86, x86_64) +====================== ====================== ======= +C name Standard Julia Alias Julia Base Type +====================== ====================== ======= +``char`` ``Cchar`` ``Int8`` (x86, x86_64) - ``UInt8`` (powerpc, arm) -``long`` ``Clong`` ``Int`` (UNIX) + ``UInt8`` (powerpc, arm) - ``Int32`` (Windows) -``unsigned long`` ``Culong`` ``UInt`` (UNIX) +``long`` ``Clong`` ``Int`` (UNIX) - ``UInt32`` (Windows) -``wchar_t`` ``Cwchar_t`` ``Int32`` (UNIX) + ``Int32`` (Windows) - ``UInt16`` (Windows) -====================== ============== ======= +``unsigned long`` ``Culong`` ``UInt`` (UNIX) -For string arguments (``char*``) the Julia type should be ``Ptr{UInt8}``, -not ``ASCIIString``. C functions that take an argument of the type ``char**`` -can be called by using a ``Ptr{Ptr{UInt8}}`` type within Julia. For example, -C functions of the form:: + ``UInt32`` (Windows) - int main(int argc, char **argv); +``wchar_t`` ``Cwchar_t`` ``Int32`` (UNIX) -can be called via the following Julia code:: + ``UInt16`` (Windows) +====================== ====================== ======= - argv = [ "a.out", "arg1", "arg2" ] - ccall(:main, Int32, (Int32, Ptr{Ptr{UInt8}}), length(argv), argv) +`Remember`: when calling a Fortran function, all inputs must be passed by reference, so all type correspondences +above should contain an additional ``Ptr{..}`` or ``Ref{..}`` wrapper around their type specification. + +`Warning`: For string arguments (``char*``) the Julia type should be ``Ptr{Cchar}``, +not ``ASCIIString``. Similarly, for array arguments (``T[]`` or ``T*``), the Julia +type should again be ``Ptr{T}``, not ``Vector{T}``. -For ``wchar_t*`` arguments, the Julia type should be ``Ptr{Wchar_t}``, +`Warning`: Julia's ``Char`` type is 32 bits, which is not the same as the wide +character type (``wchar_t`` or ``wint_t``) on all platforms. + +`Note`: For ``wchar_t*`` arguments, the Julia type should be ``Ptr{Wchar_t}``, and data can be converted to/from ordinary Julia strings by the ``wstring(s)`` function (equivalent to either ``utf16(s)`` or ``utf32(s)`` depending upon the width of ``Cwchar_t``. Note also that ASCII, UTF-8, UTF-16, and UTF-32 string data in Julia is internally NUL-terminated, so it can be passed to C functions expecting NUL-terminated data without making a copy. +`Note`: C functions that take an argument of the type ``char**`` can be called by using +a ``Ptr{Ptr{UInt8}}`` type within Julia. For example, +C functions of the form:: -Accessing Data through a Pointer --------------------------------- -The following methods are described as "unsafe" because they can cause Julia -to terminate abruptly or corrupt arbitrary process memory due to a bad pointer -or type declaration. + int main(int argc, char **argv); -Given a ``Ptr{T}``, the contents of type ``T`` can generally be copied from -the referenced memory into a Julia object using ``unsafe_load(ptr, [index])``. The -index argument is optional (default is 1), and performs 1-based indexing. This -function is intentionally similar to the behavior of ``getindex()`` and ``setindex!()`` -(e.g. ``[]`` access syntax). +can be called via the following Julia code:: -The return value will be a new object initialized -to contain a copy of the contents of the referenced memory. The referenced -memory can safely be freed or released. + argv = [ "a.out", "arg1", "arg2" ] + ccall(:main, Int32, (Int32, Ptr{Ptr{UInt8}}), length(argv), argv) -If ``T`` is ``Any``, then the memory is assumed to contain a reference to -a Julia object (a ``jl_value_t*``), the result will be a reference to this object, -and the object will not be copied. You must be careful in this case to ensure -that the object was always visible to the garbage collector (pointers do not -count, but the new reference does) to ensure the memory is not prematurely freed. -Note that if the object was not originally allocated by Julia, the new object -will never be finalized by Julia's garbage collector. If the ``Ptr`` itself -is actually a ``jl_value_t*``, it can be converted back to a Julia object -reference by ``unsafe_pointer_to_objref(ptr)``. (Julia values ``v`` -can be converted to ``jl_value_t*`` pointers, as ``Ptr{Void}``, by calling -``pointer_from_objref(v)``.) +`Note`: A C function declared to return ``Void`` will return the value ``nothing`` in Julia. -The reverse operation (writing data to a Ptr{T}), can be performed using -``unsafe_store!(ptr, value, [index])``. Currently, this is only supported -for bitstypes or other pointer-free (``isbits``) immutable types. +Struct Type correspondences +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Any operation that throws an error is probably currently unimplemented -and should be posted as a bug so that it can be resolved. +Composite types, aka ``struct`` in C or ``STRUCTURE`` / ``RECORD`` in Fortran), +can be mirrored in Julia by creating a ``type`` or ``immutable`` +definition with the same field layout. -If the pointer of interest is a plain-data array (bitstype or immutable), the -function ``pointer_to_array(ptr,dims,[own])`` may be more useful. The final -parameter should be true if Julia should "take ownership" of the underlying -buffer and call ``free(ptr)`` when the returned ``Array`` object is finalized. -If the ``own`` parameter is omitted or false, the caller must ensure the -buffer remains in existence until all access is complete. +When used recursively, ``isbits`` types are stored inline. +All other types are stored as a pointer to the data. +When mirroring a struct used by-value inside another struct in C, +it is imperative that you do not attempt to manually copy the fields over, +as this will not preserve the correct field alignment. +Instead, declare an immutable isbits type and use that instead. +Unnamed structs are not possible in the translation to Julia. -Arithmetic on the ``Ptr`` type in Julia (e.g. using ``+``) does not behave the -same as C's pointer arithmetic. Adding an integer to a ``Ptr`` in Julia always -moves the pointer by some number of *bytes*, not elements. This way, the -address values obtained from pointer arithmetic do not depend on the -element types of pointers. +Packed structs and union declarations are not supported by Julia. + +You can get a near approximation of a ``union`` if you know, a priori, +the field that will have the greatest size (potentially including padding). +When translating your fields to Julia, declare the Julia field to be only +of that type. + +Arrays of parameters must be expanded manually, currently +(either inline, or in an immutable helper-type). For example:: + + in C: + struct B { + int A[3]; + }; + b_a_2 = B.A[2]; + + in Julia: + immutable B_A + A_1::Cint + A_2::Cint + A_3::Cint + end + type B + A::B_A + end + b_a_2 = B.A.(2) + +Arrays of unknown size are not supported. + +In the future, some of these restrictions may be reduced or eliminated. + +Memory Ownership: +~~~~~~~~~~~~~~~~~ + +**malloc/free** + +Memory allocation and deallocation of such objects must be +handled by calls to the appropriate cleanup routines in the libraries +being used, just like in any C program. Do not try to free an object +received from a C library with `c_free` in Julia, as this may result +in the ``free`` function being called via the wrong `libc` library and +cause Julia to crash. The reverse (passing an object allocated in Julia +to be freed by an external library) is equally invalid. + +**Ptr{T} vs. Array{T} vs. Ref{T} vs. T** + +The choice of type-wrapper declaration strongly depends on who allocated the memory, +and the declared type. +In general, use ``T`` if the memory is intended to be allocated in +(and managed by) Julia (with type-tag). +Use ``Ptr{T}`` if the memory is expected to be populated by ``C`` (without type-tag). +Use ``Ref{T}`` if you have an ``isbits`` type, +but you want to turn it into a pointer to a struct in another struct definition. + +See issue #2818 for some work that needs to be done to simplify this so that Julia +types can be used to recursively mirror c-style structs, +without requiring as much manual management of the ``Ptr`` conversions. +After #2818 is implemented, it will be true that an `Vector{T}` will be equivalent to +an `Ptr{Ptr{T}}`. That is currently not true, and the conversion must be explicitly. + +Mapping C Functions to Julia +---------------------------- + +ccall/cfunction argument translation guide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For translating a ``c`` argument list to ``Julia``: + +* ``T``, where ``T`` is one of the primitive types: + ``char``, ``int``, ``long``, ``short``, ``float``, ``double``, ``complex``, ``enum`` + or any of their `typedef` equivalents + + + ``T``, where ``T`` is an equivalent Julia Bits Type (per the table above) + + if ``T`` is an ``enum``, the argument type should be equivalent to `Cint` or `Cuint` + + argument value will be copied (passed by-value) + +* ``struct T`` (including typedef to a struct) + + + ``T``, where ``T`` is a Julia Leaf Type + + argument value will be copied (passed by-value) + +* ``void*`` + + + depends on how this parameter is used, first translate this to the intended pointer type, + then determine the Julia equivalent using the remaining rules in this list + + this argument may be declared as ``Ptr{Void}``, if it really is just an unknown pointer + +* ``jl_value_t*`` + + + ``Any`` + + argument value must be a valid Julia object + + currently unsupported by cfunction + +* ``jl_value_t**`` + + + ``Ref{Any}`` + + argument value must be a valid Julia object (or ``C_NULL``) + + currently unsupported by cfunction + +* ``T*`` + + + ``Ref{T}``, where ``T`` is the Julia type corresponding to ``T`` + + argument value will be copied if it is an ``isbits`` type + otherwise, the value must be a valid Julia object + +* ``(T*)(...)`` (e.g. a pointer to a function) + + + ``Ptr{Void}`` (you may need to use ``cfunction`` explicitly to create this pointer) + +* ``...`` (e.g. a vararg) + + + ``T...``, where ``T`` is the Julia type + +* ``va_arg`` + + + not supported + +ccall/cfunction return type translation guide +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For translating a ``c`` return type to ``Julia``: + +* ``void`` + + + ``Void`` (this will return the singleton instance ``nothing::Void``) + +* ``T``, where ``T`` is one of the primitive types: + ``char``, ``int``, ``long``, ``short``, ``float``, ``double``, ``complex``, ``enum`` + or any of their `typedef` equivalents + + + ``T``, where ``T`` is an equivalent Julia Bits Type (per the table above) + + if ``T`` is an ``enum``, the argument type should be equivalent to ``Cint`` or ``Cuint`` + + argument value will be copied (returned by-value) + +* ``struct T`` (including typedef to a struct) + + + ``T``, where ``T`` is a Julia Leaf Type + + argument value will be copied (returned by-value) + +* ``void*`` + + + depends on how this parameter is used, first translate this to the intended pointer type, + then determine the Julia equivalent using the remaining rules in this list + + this argument may be declared as ``Ptr{Void}``, if it really is just an unknown pointer + +* ``jl_value_t*`` + + + ``Any`` + + argument value must be a valid Julia object + +* ``jl_value_t**`` + + + ``Ref{Any}`` + + argument value must be a valid Julia object (or ``C_NULL``) + +* ``T*`` + + + If the memory is already owned by Julia, or is an `isbits` type, and is known to be non-null: + + + ``Ref{T}``, where ``T`` is the Julia type corresponding to ``T`` + + a return type of ``Ref{Any}`` is invalid, it should either be ``Any`` + (corresponding to ``jl_value_t*``) or ``Ptr{Any}`` (corresponding to ``Ptr{Any}``) + + currently partially unsupported by cfunction due to #2818 + + C **MUST NOT** modify the memory returned via ``Ref{T}`` if ``T`` is an ``isbits`` type + + + If the memory is owned by C: + + + ``Ptr{T}``, where ``T`` is the Julia type corresponding to ``T`` + +* ``(T*)(...)`` (e.g. a pointer to a function) + + + ``Ptr{Void}`` (you may need to use ``cfunction`` explicitly to create this pointer) Passing Pointers for Modifying Inputs -------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Because C doesn't support multiple return values, often C functions will take -pointers to data that the function will modify. To accomplish this within a -``ccall`` you need to encapsulate the value inside an array of the appropriate -type. When you pass the array as an argument with a ``Ptr`` type, julia will -automatically pass a C pointer to the encapsulated data:: +Because C doesn't support multiple return values, +often C functions will take pointers to data that the function will modify. +To accomplish this within a ``ccall``, +you need to first encapsulate the value inside an ``Ref{T}`` of the appropriate type. +When you pass this ``Ref`` object as an argument, +julia will automatically pass a C pointer to the encapsulated data:: - width = Cint[0] - range = Cfloat[0] - ccall(:foo, Void, (Ptr{Cint}, Ptr{Cfloat}), width, range) + width = Ref{Cint}(0) + range = Ref{Cfloat}(0) + ccall(:foo, Void, (Ref{Cint}, Ref{Cfloat}), width, range) This is used extensively in Julia's LAPACK interface, where an integer ``info`` is passed to LAPACK by reference, and on return, includes the success code. +Special Reference Syntax for ccall (deprecated): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + The ``&`` syntax is deprecated, use the ``Ref{T}`` argument type instead + +A prefix ``&`` is used on an argument to ccall to indicate that a pointer +to a scalar argument should be passed instead of the scalar value itself +(required for all Fortran function arguments, as noted above). The following +example computes a dot product using a BLAS function. + +:: + + function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) + assert(length(DX) == length(DY)) + n = length(DX) + incx = incy = 1 + product = ccall((:ddot_, "libLAPACK"), + Float64, + (Ptr{Int32}, Ptr{Float64}, Ptr{Int32}, Ptr{Float64}, Ptr{Int32}), + &n, DX, &incx, DY, &incy) + return product + end + +The meaning of prefix ``&`` is not quite the same as in C. In +particular, any changes to the referenced variables will not be +visible in Julia unless the type is mutable (declared via +``type``). However, even for immutable types it will not cause any +harm for called functions to attempt such modifications (that is, +writing through the passed pointers). Moreover, ``&`` may be used with +any expression, such as ``&0`` or ``&f(x)``. + +When a scalar value is passed with ``&`` as an argument of type +``Ptr{T}``, the value will first be converted to type ``T``. + + Garbage Collection Safety ------------------------- When passing data to a ccall, it is best to avoid using the ``pointer()`` @@ -395,7 +697,7 @@ preserved from garbage collection until the call returns. If a C API will store a reference to memory allocated by Julia, after the ccall returns, you must arrange that the object remains visible to the garbage collector. The suggested way to handle this is to make a global variable of type -``Array{Any,1}`` to hold these values, until C interface notifies you that +``Array{Ref,1}`` to hold these values, until the C library notifies you that it is finished with them. Whenever you have created a pointer to Julia data, you must ensure the original data @@ -429,14 +731,39 @@ variables will not be available (unless their values are substituted with definitions, for example when wrapping libraries that contain many similar functions. +If your usage is more dynamic, use indirect calls as described in the next section. + + Indirect Calls -------------- -The first argument to ``ccall`` can also be an expression evaluated at -run time. In this case, the expression must evaluate to a ``Ptr``, -which will be used as the address of the native function to call. This -behavior occurs when the first ``ccall`` argument contains references -to non-constants, such as local variables or function arguments. +The first argument to ``ccall`` can also be an expression evaluated at run time. +In this case, the expression must evaluate to a ``Ptr``, +which will be used as the address of the native function to call. +This behavior occurs when the first ``ccall`` argument contains references to non-constants, +such as local variables, function arguments, or non-constant globals. + +For example, you might lookup the function via ``dlsym``, +then cache it in a global variable for that session. For example:: + + macro dlsym(func, lib) + z, zlocal = gensym(string(func)), gensym() + eval(current_module(),:(global $z = C_NULL)) + z = esc(z) + quote + let $zlocal::Ptr{Void} = $z::Ptr{Void} + if $zlocal == C_NULL + $zlocal = dlsym($(esc(lib))::Ptr{Void}, $(esc(func))) + global $z = $zlocal + end + $zlocal + end + end + end + + mylibvar = dlopen("mylib") + ccall(@dlsym("myfunc", mylibvar), Void, ()) + Calling Convention ------------------ @@ -445,15 +772,17 @@ The second argument to ``ccall`` can optionally be a calling convention specifier (immediately preceding return type). Without any specifier, the platform-default C calling convention is used. Other supported conventions are: ``stdcall``, ``cdecl``, ``fastcall``, and ``thiscall``. -For example (from base/libc.jl):: +For example (from base/libc.jl) we see the same ``gethostname`` ccall as above, +but with the correct signature for Windows:: hn = Array(UInt8, 256) - err=ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn)) + err = ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn)) For more information, please see the `LLVM Language Reference`_. .. _LLVM Language Reference: http://llvm.org/docs/LangRef.html#calling-conventions + Accessing Global Variables -------------------------- @@ -468,58 +797,59 @@ the variable:: The result is a pointer giving the address of the value. The value can be manipulated through this pointer using ``unsafe_load`` and ``unsafe_store``. -Passing Julia Callback Functions to C -------------------------------------- - -It is possible to pass Julia functions to native functions that accept function -pointer arguments. A classic example is the standard C library ``qsort`` function, -declared as:: - - void qsort(void *base, size_t nmemb, size_t size, - int(*compare)(const void *a, const void *b)); -The ``base`` argument is a pointer to an array of length ``nmemb``, with elements of -``size`` bytes each. ``compare`` is a callback function which takes pointers to two -elements ``a`` and ``b`` and returns an integer less/greater than zero if ``a`` should -appear before/after ``b`` (or zero if any order is permitted). Now, suppose that we -have a 1d array ``A`` of values in Julia that we want to sort using the ``qsort`` -function (rather than Julia’s built-in sort function). Before we worry about calling -``qsort`` and passing arguments, we need to write a comparison function that works for -some arbitrary type T:: +Accessing Data through a Pointer +-------------------------------- +The following methods are described as "unsafe" because a bad pointer +or type declaration can cause Julia to terminate abruptly +(although, that's quite alike with ccall). - function mycompare{T}(a_::Ptr{T}, b_::Ptr{T}) - a = unsafe_load(a_) - b = unsafe_load(b_) - return convert(Cint, a < b ? -1 : a > b ? +1 : 0) - end +Given a ``Ptr{T}``, the contents of type ``T`` can generally be copied from +the referenced memory into a Julia object using ``unsafe_load(ptr, [index])``. +The index argument is optional (default is 1), +and follows the Julia-convention of 1-based indexing. +This function is intentionally similar to the behavior of ``getindex()`` and ``setindex!()`` +(e.g. ``[]`` access syntax). -Notice that we have to be careful about the return type: ``qsort`` expects a function -returning a C ``int``, so we must be sure to return ``Cint`` via a call to ``convert``. +The return value will be a new object initialized +to contain a copy of the contents of the referenced memory. +The referenced memory can safely be freed or released. -In order to pass this function to C, we obtain its address using the function -``cfunction``:: +If ``T`` is ``Any``, then the memory is assumed to contain a reference to +a Julia object (a ``jl_value_t*``), the result will be a reference to this object, +and the object will not be copied. You must be careful in this case to ensure +that the object was always visible to the garbage collector (pointers do not +count, but the new reference does) to ensure the memory is not prematurely freed. +Note that if the object was not originally allocated by Julia, the new object +will never be finalized by Julia's garbage collector. If the ``Ptr`` itself +is actually a ``jl_value_t*``, it can be converted back to a Julia object +reference by ``unsafe_pointer_to_objref(ptr)``. (Julia values ``v`` +can be converted to ``jl_value_t*`` pointers, as ``Ptr{Void}``, by calling +``pointer_from_objref(v)``.) - const mycompare_c = cfunction(mycompare, Cint, (Ptr{Cdouble}, Ptr{Cdouble})) +The reverse operation (writing data to a Ptr{T}), can be performed using +``unsafe_store!(ptr, value, [index])``. Currently, this is only supported +for bitstypes or other pointer-free (``isbits``) immutable types. -``cfunction`` accepts three arguments: the Julia function (``mycompare``), the return -type (``Cint``), and a tuple of the argument types, in this case to sort an array of -``Cdouble`` (Float64) elements. +Any operation that throws an error is probably currently unimplemented +and should be posted as a bug so that it can be resolved. -The final call to ``qsort`` looks like this:: +If the pointer of interest is a plain-data array (bitstype or immutable), the +function ``pointer_to_array(ptr,dims,[own])`` may be more useful. The final +parameter should be true if Julia should "take ownership" of the underlying +buffer and call ``free(ptr)`` when the returned ``Array`` object is finalized. +If the ``own`` parameter is omitted or false, the caller must ensure the +buffer remains in existence until all access is complete. - A = [1.3, -2.7, 4.4, 3.1] - ccall(:qsort, Void, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Void}), - A, length(A), sizeof(eltype(A)), mycompare_c) +Arithmetic on the ``Ptr`` type in Julia (e.g. using ``+``) does not behave the +same as C's pointer arithmetic. Adding an integer to a ``Ptr`` in Julia always +moves the pointer by some number of *bytes*, not elements. This way, the +address values obtained from pointer arithmetic do not depend on the +element types of pointers. -After this executes, ``A`` is changed to the sorted array ``[ -2.7, 1.3, 3.1, 4.4]``. -Note that Julia knows how to convert an array into a ``Ptr{Cdouble}``, how to compute -the size of a type in bytes (identical to C’s ``sizeof`` operator), and so on. -For fun, try inserting a ``println("mycompare($a,$b)")`` line into ``mycompare``, which -will allow you to see the comparisons that ``qsort`` is performing (and to verify that -it is really calling the Julia function that you passed to it). Thread-safety -~~~~~~~~~~~~~ +------------- Some C libraries execute their callbacks from a different thread, and since Julia isn't thread-safe you'll need to take some extra @@ -535,7 +865,7 @@ The callback you pass to C should only execute a ``ccall`` to ``:uv_async_send``, passing ``cb.handle`` as the argument. More About Callbacks -~~~~~~~~~~~~~~~~~~~~ +-------------------- For more details on how to pass callbacks to C libraries, see this `blog post `_. @@ -543,11 +873,11 @@ For more details on how to pass callbacks to C libraries, see this C++ --- -Limited support for C++ is provided by the `Cpp `_ -and `Clang `_ packages. +Limited support for C++ is provided by the `Cpp `_, +`Clang `_, and `Cxx `_ packages. -Handling Platform Variations ----------------------------- +Handling Operating System Variation +----------------------------------- When dealing with platform libraries, it is often necessary to provide special cases for various platforms. The variable ``OS_NAME`` can be used to write these special @@ -574,3 +904,4 @@ Complex blocks:: Chaining (parentheses optional, but recommended for readability):: @windows? :a : (@osx? :b : :c) + diff --git a/src/codegen.cpp b/src/codegen.cpp index c3abb495914fe..489772951db3f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -782,7 +782,13 @@ static Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_value_ JL_TYPECHK(cfunction, tuple, argt); JL_TYPECHK(cfunction, type, argt); JL_TYPECHK(cfunction, function, (jl_value_t*)f); + if (!jl_is_gf(f)) + jl_error("only generic functions are currently c-callable"); + size_t i, nargs = jl_tuple_len(argt); + if (nargs >= 64) + jl_error("only functions with less than 64 arguments are c-callable"); + uint64_t isref = 0; // bit vector of which argument types are a subtype of Type{Ref{T}} jl_value_t *sigt; // type signature with Ref{} annotations removed JL_GC_PUSH(&sigt); @@ -795,13 +801,19 @@ static Function *jl_cfunction_object(jl_function_t *f, jl_value_t *rt, jl_value_ jl_error("cfunction: argument type Ref should have an element type, not Ref{T}"); isref |= (2<env==(jl_value_t*)jl_null && ff->linfo != NULL) { - jl_lambda_info_t *li = ff->linfo; - if (!jl_types_equal((jl_value_t*)li->specTypes, sigt)) { - jl_errorf("cfunction: type signature of %s does not match", - li->name->name); - } - jl_value_t *astrt = jl_ast_rettype(li, li->ast); - if (rt != NULL) { - if (astrt == (jl_value_t*)jl_bottom_type) { - if (rt != (jl_value_t*)jl_void_type) { - // a function that doesn't return can be passed to C as void - jl_errorf("cfunction: %s does not return", li->name->name); - } - } - else if (!jl_subtype(astrt, rt, 0)) { - jl_errorf("cfunction: return type of %s does not match", - li->name->name); + jl_function_t *ff = jl_get_specialization(f, (jl_tuple_t*)sigt); + if (ff != NULL && ff->env==(jl_value_t*)jl_null && ff->linfo != NULL) { + jl_lambda_info_t *li = ff->linfo; + if (!jl_types_equal((jl_value_t*)li->specTypes, sigt)) { + jl_errorf("cfunction: type signature of %s does not match specification", + li->name->name); + } + jl_value_t *astrt = jl_ast_rettype(li, li->ast); + if (rt != NULL) { + if (astrt == (jl_value_t*)jl_bottom_type) { + if (rt != (jl_value_t*)jl_void_type) { + // a function that doesn't return can be passed to C as void + jl_errorf("cfunction: %s does not return", li->name->name); } } - JL_GC_POP(); // kill list: sigt - return gen_cfun_wrapper(ff, astrt, (jl_tuple_t*)argt, isref); + else if (!jl_subtype(astrt, rt, 0)) { + jl_errorf("cfunction: return type of %s does not match", + li->name->name); + } } + JL_GC_POP(); // kill list: sigt + return gen_cfun_wrapper(ff, astrt, (jl_tuple_t*)argt, isref); } - jl_error("function is not yet c-callable"); + jl_error("cfunction: no method exactly matched the required type signature (function not yet c-callable)"); } // get the address of a C-callable entry point for a function @@ -3708,7 +3718,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t finalize_gc_frame(&ctx); if (isref&1) { - r = builder.CreateConstGEP1_32(r, 1); // skip type-tag + //r = builder.CreateConstGEP1_32(r, 1); // skip type-tag -- broken if you do, broken if you don't builder.CreateRet(r); } else { diff --git a/test/ccall.jl b/test/ccall.jl index 736b1241fea54..097b00afdb900 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -118,30 +118,71 @@ verbose && println("Testing cfunction roundtrip: ") # cfunction roundtrip for (t,v) in ((Complex{Int32},:ci32),(Complex{Int64},:ci64), (Complex64,:cf32),(Complex128,:cf64),(Struct1,:s1)) + fname = symbol("foo"*string(v)) + fname1 = symbol("foo1"*string(v)) @eval begin verbose && println($t) - if($(t).mutable) - a = copy($v) - else - a = $v - end + a = copy($v) verbose && println("A: ",a) - function $(symbol("foo"*string(v)))(s::$t) + function $fname1(s::$t) + verbose && println("B: ",s) + @test s == $v + @test s === a + global c = s + s + end + function $fname1(s) + verbose && println("B(Any): ",s) + @test s == $v + @test s === a + global c = s + s + end + function $fname(s::$t) verbose && println("B: ",s) + @test s == $v + if($(t).mutable) + @test !(s === a) + end + global c = s + s + end + function $fname(s) + verbose && println("B(Any): ",s) + @test s == $v if($(t).mutable) @test !(s === a) end - @test s == a + global c = s s end - b = ccall(cfunction($(symbol("foo"*string(v))),$t,($t,)),$t,($t,),$v) - b = ccall(cfunction($(symbol("foo"*string(v))),Ref{$t},(Ref{$t},)),Ref{$t},(Ref{$t},),$v) - b = ccall(cfunction($(symbol("foo"*string(v))),$t,(Ref{$t},)),$t,(Ref{$t},),$v) - #b = ccall(cfunction($(symbol("foo"*string(v))),Any,(Ref{$t},)),Any,(Ref{$t},),$v) # broken due to #2818 + b = ccall(cfunction($fname1,Ref{$t},(Ref{$t},)),Ref{$t},(Ref{$t},),a) + verbose && println("C: ",b) + @test b == $v + @test b === a + @test b === c + b = ccall(cfunction($fname,$t,($t,)),$t,($t,),a) + verbose && println("C: ",b) + @test b == $v + if ($(t).mutable) + @test !(b === c) + @test !(b === a) + end + b = ccall(cfunction($fname1,$t,(Ref{$t},)),$t,(Ref{$t},),a) + verbose && println("C: ",b) + @test b == $v + if ($(t).mutable) + @test !(b === c) + @test !(b === a) + end + b = ccall(cfunction($fname,Ref{$t},($t,)),Ref{$t},($t,),a) verbose && println("C: ",b) + @test b == $v + @test b === c if ($(t).mutable) @test !(b === a) end - @test a == b + #b = ccall(cfunction($fname,Any,(Ref{Any},)),Any,(Ref{Any},),$v) # unimplemented + #b = ccall(cfunction($fname,Any,(Ref{$t},)),Any,(Ref{$t},),$v) # broken due to #2818 end end