I know that a similar question has already been posted, but its solution doesn't work for me. In my spec file I have the code:
type Colour_Component is mod 256;
type Colour is
record
A, R, G, B : Colour_Component;
end record;
type Raw_Image_Data is array (Interfaces.C.int range <>) of Colour;
type Raw_Image is access all Raw_Image_Data;
pragma Convention (C, Raw_Image);
Then I try to interface with a C function:
function C_SDL_CreateRGBSurfaceFrom (
Pixels : Raw_Image;
Width : int;
Height : int;
Depth : int;
Pitch : int;
Rmask : Unsigned_32;
Gmask : Unsigned_32;
Bmask : Unsigned_32;
Amask : Unsigned_32)
return System.Address;
pragma Import (C, C_SDL_CreateRGBSurfaceFrom, "SDL_CreateRGBSurfaceFrom");
But when I try to compile it I get a warning:
warning: type of "C_SDL_CreateRGBSurfaceFrom.Pixels" does not correspond to C pointer
warning: this access type does not correspond to C pointer
Since I have my compiler flags set to treat warnings as errors this doesn't compile. Any advice on how to fix this?
Ada arrays come in two flavours, constrained and unconstrained arrays. C arrays are constrained (newer C standards also have dynamically sized arrays), but if you pass C arrays around function calls you either terminate them with a zero element or with a separate length parameter.
Anyway, you declared your parameter Raw_Image as an unconstrained array. There is no counterpart in C. You can only pass constrained arrays from or to C.
I think you have two options: (1) use address to access conversion or (2) use the binding generator -fdump-ada-spec
(1) declare your first parameter as of type System.Address and use the package System.Address_To_Access_Conversions
(2) The easiest way is to use the gcc switch -fdump-ada-spec on a C header. See Generating Ada Bindings for C and C++ headers
Related
During a C code review of the software that did not want to run on the target HW, the following inconsistency has been discovered in a function use:
The SW component 1 implements the function:
void foo(uint8 par_var[2])
The function foo() also writes the two element of the par_var array.
The SW component 2 gets an external declaration of the foo() as following
extern void foo(uint8 *par_var)
and uses it as following :
uint8 par_var;
foo(&par_var); //-> sending a pointer to a scalar
// instead to an array of 2 elements.
Obviously, it may lead and leads actually to the program failure.
The question is, if it could be possible for compiler/linker to intercept the inconsistency by issuing a warning, for example.
I have scanned and tried some of the gcc (CygWin) compiler options along with the standard ones (-Wall, -pedantic ) https://gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Warning-Options.html
but could not find one that could issue a corresponding warning .
There is a C feature that could aid a compiler in diagnosing this, but I am not aware of a compiler that takes advantage of it and warns. If foo were declared as void foo(uint8 par_var[static 2]);, then a caller is required to pass a pointer to at least two elements, per C 2018 6.7.6.3 7:
If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
So, a compiler seeing uint8 par_var; foo(&par_var); could recognize the failure to pass two elements and provide a warning. (While I am not aware of compilers that check the declared size, some compilers will warn when a null pointer is passed for such a parameter.)
As is well known, in the declaration void foo(uint8 par_var[2]), par_var is automatically adjusted to uint8 *par_var. As an alternative, instead of passing a pointer to uint8, you could pass a pointer to an array of uint8 by declaring foo as void foo(uint8 (*par_var)[2]);.
Then you would have to pass it an array, such as:
uint8 A[2];
foo(&A);
If it were called with a pointer to uint8, the compiler should issue a warning. Unfortunately, this also constraints the routine; you must pass it a pointer to an array of two uint8 and cannot pass it a pointer to a larger array or a pointer to a uint8 in a larger array. So it has limited use. Nonetheless, it could serve in certain situations.
Considering a dynamic library with this native function that returns the sum of all even (32-bit unsigned) numbers in an array:
uint32_t sum_of_even(const uint32_t *numbers, size_t length);
The implementation of the function above was written in Rust as below, and packaged into a C dynamic library.
use libc::size_t;
use std::slice;
#[no_mangle]
pub extern "C" fn sum_of_even(n: *const u32, len: size_t) -> u32 {
let numbers = unsafe {
assert!(!n.is_null());
slice::from_raw_parts(n, len as usize)
};
numbers
.iter()
.filter(|&v| v % 2 == 0)
.sum()
}
I wrote the following Julia (v1.0.1) wrapper function:
lib = Libdl.dlopen(libname)
sumofeven_sym = Libdl.dlsym(lib, :sum_of_even)
sumofeven(a) = ccall(
sumofeven_sym,
UInt32,
(Ptr{UInt32}, Csize_t),
a, length(a)
)
The documentation states multiple times that arguments in ccall are converted to become compatible with the C function prototype (emphasis mine):
Each argvalue to the ccall will be converted to the corresponding argtype, by automatic insertion of calls to unsafe_convert(argtype, cconvert(argtype, argvalue)). (See also the documentation for unsafe_convert and cconvert for further details.) In most cases, this simply results in a call to convert(argtype, argvalue).
And moreover, that when passing an Array{T} by Ptr{U} to a C function, the call is invalidated if the two types T and U are different, since no reinterpret cast is added (section Bits Types):
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 the 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 trunc(Int32, a).
However, this is seemingly not the case. If I deliberately pass an array with another type element:
println(sumofeven(Float32[1, 2, 3, 4, 5, 6]))
The program calls the C function with the array passed directly, without converting the values nor complaining about the different element types, resulting in either senseless output or a segmentation fault.
If I redefine the function to accept a Ref{UInt32} instead of a Ptr{UInt32}, I am prevented from calling it with the array of floats:
ERROR: LoadError: MethodError: Cannot `convert` an object of type Array{Float32,1} to an object of type UInt32
Closest candidates are:
convert(::Type{T<:Number}, !Matched::T<:Number) where T<:Number at number.jl:6
convert(::Type{T<:Number}, !Matched::Number) where T<:Number at number.jl:7
convert(::Type{T<:Integer}, !Matched::Ptr) where T<:Integer at pointer.jl:23
...
However, Ref was not designed for arrays.
Making the example work with Ptr{UInt32} requires me to either specify Array{UInt32} as the type of input a (static enforcement), or convert the array first for a more flexible function.
sumofeven(a:: Array{UInt32}) = ccall( # ← either this
sumofeven_sym,
UInt32,
(Ptr{UInt32}, Csize_t),
convert(Array{UInt32}, a), # ← or this
length(a))
With that, I still feel that there is a gap in my reasoning. What is the documentation really suggesting when it says that an array passed to C as a Ptr{T} is not reinterpret-cast? Why is Julia letting me pass an array of different element types without any explicit conversion?
This turned out to be either a bug in the core library or a very misguided documentation, depending on the perspective (issue #29850). The behavior of the function unsafe_convert changed from version 0.4 to 0.5, in a way that makes it more flexible than what is currently suggested.
According to this commit, unsafe_convert changed from this:
unsafe_convert(::Type{Ptr{Void}}, a::Array) = ccall(:jl_array_ptr, Ptr{Void}, (Any,), a)
To this:
unsafe_convert{S,T}(::Type{Ptr{S}}, a::AbstractArray{T}) = convert(Ptr{S}, unsafe_convert(Ptr{T}, a))
For arrays, this relaxed implementation will enable a transformation from an array of T to a pointer of another type S. In practice, unsafe_convert(cconvert(array)) will reinterpret-cast the array's base pointer, as in the C++ nomenclature. We are left with a dangerously reinterpreted array across the FFI boundary.
The key takeaway is that one needs to take extra care when passing arrays to C functions, as the element type of an array in a C-call function parameter is not statically enforced. Use type signatures and/or explicit conversions where applicable.
I need to pass a structure from C to Ada in an Ada binding application. Thus, I have declared the structure in both Ada and C so that both the source sides can decipher the structure composition.
In C,
typedef struct {
int Status_Code;
int Error_Code;
} Edit_Result_Type;
In Ada,
type Edit_Result_Rec_Type is
record
Status : Integer;
Error_Code : Integer;
end record;
pragma Convention (Convention => C,
Entity => Edit_Result_Rec_Type);
for Edit_Result_Rec_Type use
record
Status at 0 range 0 .. 31;
Error_Code at 0 range 32 .. 63;
end record;
type Edit_Result_Rec_Type_Ptr is access all Edit_Result_Rec_Type;
When I am passing the structure from C to Ada through call by reference, I wanted to know:
Is it ok if I declare an "access all" vector type for the structure (as done above - Edit_Result_Rec_Type_Ptr) in Ada and use it directly as a formal parameter in Ada function. For Eg:
procedure Process_Data (Edit_Result_Ptr : in out Edit_Result_Rec_Type_Ptr) is
begin
Edit_Result_Ptr.Status := 1;
Edit_Result_Ptr.Error_Code := 0;
end Process_Data;
Is this approach FATAL? I know it is, just wanted to know "how" in depth!
Any other (better) approaches for passing through call by reference? I believe I can pass it as a "System.Address" parameter and do an "unchecked conversion" into Edit_Result_Rec_Type_Ptr in a local vector inside the Ada function and then read/write record members? Does this approach has any fatalities?
When interfacing Ada and C, you should really read the RM, Annex B.3, which says:
An Ada parameter of a record type T, of any mode, other than an in parameter of a type of convention C_Pass_By_Copy, is passed as a t* argument to a C function, where t is the C struct corresponding to the Ada type T.
So, in your procedure, just do:
procedure Process_Data (Edit_Result : in out Edit_Result_Rec_Type) is
begin
Edit_Result.Status := 1;
Edit_Result.Error_Code := 0;
end Process_Data;
and
pragma Export(C, Process_Data);
(or use the aspect, if Ada 2012)
That being said, you shouldn't use Integer in your record definition, Interfaces.C.int is the way to go:
type Edit_Result_Rec_Type is
record
Status : Interfaces.C.int;
Error_Code : Interfaces.C.int;
end record;
which will match the C int on your platform (assuming your C compiler is compatible with the Ada compiler)
As for your questions:
That would work, but why mess around with pointers?
No, that wouldn't work, access types (and access values) in Ada are not addresses. Converting a System.Address to an access value requires System.Address_To_Access_Conversions, but again, why mess around with pointers?
I have been thinking about ways to validate types in C macros and so far the best way that I have come up with is this:
#define ASSERT_PTYPE(TYPE, VALUE) (0 && (*(int (*)(TYPE*))0)(VALUE))
This obviously expects a type name and a pointer to that type. A similar ASSERT_TYPE macro can be made as well. This seems to work quite well with GCC. It even gives a very helpful error message in the case that the types do not match. The problems are that I am not completely certain that this is valid C or the best way for that matter.
As I understand it the standard says that you can cast a function pointer, but the result of calling the cast function pointer is undefined. In this case it is impossible for the function to be called at runtime. Is that good enough or does the standard mean that you cannot even write code that cannot be called that calls the cast function?
With C99 and compound literals you can do something like
#define ASSERT_TYPE(TYPE, VALUE) ((TYPE){ 0 } = (VALUE))
This ensures that VALUE is assignment compatible to TYPE. The expression returns an rvalue because of the assignment.
Compound literals work in function scope as well as in file scope and any decent compiler should optimize the extra object that is created out of the way.
Addition: TYPE in that macro can be any valid type name, e.g pointer double*, struct or union struct toto, besides arrays. Array type such as double[4] wouldn't work because of the assignment. Use pointer to
array double(*)[4] instead, e.g as in
double A[4];
(*ASSERT_TYPE(double(*)[4], &A))
where the second line again is a lvalue of type double[4] that is compile time checked for that property.
const int size = 10; // realna ilość danych
int tablica[size+1];
i have:
variable-size type declared outside of any function
Use
#define size 10
instead of a const int. The latter is not a compile-time constant in C, but a variable that cannot be assigned to (unless via a pointer and a cast to get rid of const).
(This is a difference between C and C++.)
You could use an enum.
enum
{
size = 10
};
int table[size + 1];
Use:
enum { size = 10 };
This is a constant value that can be used in declarations and in case labels and so on. In C99, inside a function, the original code would not be a problem -- your array tablica would be a VLA or variable-length array (and the compiler error message is trying to say "you can't have a VLA outside a function").
Using an enum gives better traceability when you use a debugger on your code; the symbol is included in the symbol table. Typically, C preprocessor symbols are not available to the debugger, so trying to print 'size' when it is #define'd doesn't print an answer; printing 'size' when it is an enum does.
See also: "static const" vs "#define" in C
The error is fairly self-explanatory. You can't declare a variable-length array outside of a function. Although the size of the array you're creating is, in practice, fixed at compile time, you've still technically violated the constraints of the language.
The usual choices are:
Move the array into a function. (Usually the best option, remember globals are to be avoided when possible.)
#define size n where n is the size you want, instead of using an int. (Usually better than "magic numbers", and pretty standard practice in traditional C.)
Use a "magic number" (int tablica[11];). (Usually the last choice, though sometimes it does make more sense.)