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?
Related
I want to make a program that works with only one specified data type. I would establish, with typeof, the type of all data in my program according the first data received.
For example, in this case:
int main() {
float a = 5;
typeof(a) var1;
typeof(b) var2;
//etc...
}
But if I want to do it with an extern function contained in a library?
Example:
int main() {
void* var1 = create_var("hello"); //with the first call 'create_var()' establish the data type: in this case -> string
void* var2 = create_var("c"); //now i want that this is allowed
void* var3 = create_var(2); //and this is not allowed
How can i do this function?
By the time you write the framework to support your create_var function, you will have invented a new language, one which supports runtime type checking (instead of C's compile/link-time type checking) and templates. That's what happened with C++ (it was originally a set of preprocessor macros for C).
It may be easier to start with a dynamically-typed language and add your own type enforcement to your data types and functions.
Examples of previous work:
Runtime Checking C Programs
Template meta-programming in C vs opaque pointer
typing - Support for type hints (Python)
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
I'm using Delphi to load a dll (that I created in Delphi XE-3) for the purposes of interfacing with some C code. My problem is figuring out why my arrays aren't being passed to the c functions - they're the only ones not to. The delphi file (simplified) looks like this:
program CallcCode
uses
SysUtils, Windows,
DLLUnit in 'DLLUnit.pas'; // Header conversion
var
DLLHandle: cardinal;
n: Integer;
A: TArray<Integer>;
result1: Integer;
begin
// Initialize each Array
SetLength(A,n);
A[0] = ...;
// Load the DLL (and confirm its loaded)
DLLhandle := LoadLibrary('dllname.dll');
if DLLhandle <> 0 then
begin
result1 := dll_func1(n,A); // A and B are not passed correctly
end
FreeLibrary(DLLHandle);
end.
I successfully "Trace into" dll_func1 the first time, entering DLLUnit, which has:
const
nameofDLL = 'dllname';
function dll_func1(n: Integer; A: TArray<Integer>): Integer; cdecl; external nameofDLL;
"Tracing-into" again, I arrive at the c file, which still has the correct n and DLLdefs values, but A (under the "Local Variables" heading) has become:
[-] A :(Aplha-Numeric)
..[0] 0 (0x00000000)
I know that I'm at least accessing the DLL (hopefully) correctly because other function calls work as they should and I am able to trace into the dll_func1.c file without a problem. I tried changing the function to
function dll_func1(n: Integer; A: PInteger): Integer; cdecl; external nameofDLL;
...
result1 := dll_func1(n,PInteger(A))
or
function dll_func1(n: Integer; A: PInteger): Integer; cdecl; external nameofDLL;
...
result1 := dll_func1(n,#A[0])
(using both TArray and array of Integer or A) but there is no change, which leaves me to believe this is related to a problem I'm not seeing. The whole thing compiles and runs, but result1 is incorrect because of the TArray failures. Any ideas on what is going wrong?
EDIT The function in C as:
int dll_func1(int n, int A [])
Your question contains two Delphi declarations for the external function. One of them uses TArray<T> as a parameter. That is completely wrong. Don't do that. You cannot use a Delphi dynamic array as an interop type. The reason being that TArray<T> is a complex managed type that can only be created and consumed by Delphi code.
You need to do as I do below, and as I explained in my answer to your previous question, and declare the array parameter as pointer to element type. For example, PInteger, PDouble, etc.
There's quite a lot of confusion here, and needless complexity. What you need is the simplest possible example that shows how to pass an array from your Delphi code to C code.
Here is is.
C code
//testarray.c
void printDouble(double d); // linker will resolve this from the Delphi code
void test(double *arr, int count)
{
int i;
for (i=0; i<count; i++)
{
printDouble(arr[i]);
}
}
Delphi code
program DelphiToC;
{$APPTYPE CONSOLE}
uses
Crtl;
procedure _printDouble(d: Double); cdecl;
begin
Writeln(d);
end;
procedure test(arr: PDouble; count: Integer); cdecl; external name '_test';
{$L testarray.obj}
var
arr: TArray<Double>;
begin
arr := TArray<Double>.Create(1.0, 2.0, 3.0, 42.0, 666.0);
test(PDouble(arr), Length(arr));
Readln;
end.
Compile the C code using, for example, the Borland C compiler like this:
bcc32 -c testarray.c
And the output is:
1.00000000000000E+0000
2.00000000000000E+0000
3.00000000000000E+0000
4.20000000000000E+0001
6.66000000000000E+0002
Note that I linked to the C code statically because that was easier for me. Nothing much changes if you put the C code in a DLL.
The conclusion is that the code I gave you in my answer to your previous, and that I repeat here, is correct. That approach succeeds in passing an array from Delphi code to C. It looks like your diagnostics and debugging is in error.
You are only inspecting A[0] so it's hardly surprising that you only see one value. If only you would look at A[1], A[2], ... , A[n-1] you would see that that all the values are being passed correctly. Or perhaps your debugging was carried out on the erroneous declaration of the external function that used TArray<T> as a parameter.
I'm trying to translate the header of libfprint (fprint.h) to Pascal, but I find a structure that can not understand and some tools such as "C2Pas" only send error.
The structure is as follows:
struct fp_dscv_dev **fp_discover_devs(void);
Where fp_dscv_dev this only declared as follows:
/* structs that applications are not allowed to peek into */
struct fp_dscv_dev;
What would be the equivalent in Pascal?
Thanks in advance
Opague pointers are traditionally basic pointers in Pascal (like void * in c).
If you really want to the struct aspect to return define it as a record without fields:
Type
fp_dscv_dev = record end;
As said in the comments, a ** is a double reference
Since in most modern pascals references can't be in parameter and return value declarations, we define additional types for them:
pfp_dscv_dev = ^fp_dscv_dev
ppfp_dscv_dev = ^pfp_dscv_dev
Finally the declaration itself is
function fp_discover_devs:ppfp_dscv_dev; cdecl;
For the use of a DLL I have only the C Header Files available. Now I am converting these header files to Delphi Interface Files. That works quite well till there occur complex C Typedef like this one:
typedef struct _IFSDK_PAUSE
{
int version;
FPDF_BOOL (*NeedToPauseNow) (struct _IFSDK_PAUSE* pThis);
void* user;
} IFSDK_PAUSE;
that converts to:
type
IFSDK_PAUSE = record
version: Integer;
pThis: <----- Problem!!!
user: Pointer;
end
How do I convert these complex Typedefs correctly?
That's a function pointer. It receives a pointer to the struct, and returns a FPDF_BOOL. Presumably, since there is no calling convention specified, the default calling convention of cdecl applies.
With that information, you would translate to Delphi like this:
type
PIFSDK_PAUSE = ^IFSDK_PAUSE;
TNeedToPauseNow = function(pThis: PIFSDK_PAUSE): FPDF_BOOL; cdecl;
IFSDK_PAUSE = record
version: Integer;
NeedToPauseNow: TNeedToPauseNow;
user: Pointer;
end;
Note that unlike C, a Delphi procedural type is implicitly a pointer.