如何通过DLL在Ada和C之间传递复杂数据类型,比如记录?

huangapple go评论89阅读模式
英文:

How to pass complex data types, like records between Ada and C via a DLL?

问题

我试图让Ada代码编译成一个DLL,以便在C中调用。到目前为止,我已经成功使简单类型(如整数)的工作,但对于更复杂的类型,如数组或记录/结构体,我遇到了困难。

以下是我的Ada代码,用于记录/结构体。这是一个动态库,我使用命令"gprbuild -P ./math.gpr -p"将其编译成DLL:

  1. person.adb
  2. with Interfaces.C; use Interfaces.C;
  3. package body Person is
  4. function Set_Age_To_Five(P : Person_Record) return Person_Record is
  5. Result : Person_Record;
  6. begin
  7. Result := P;
  8. Result.Age := 5;
  9. return Result;
  10. end Set_Age_To_Five;
  11. end Person;
  1. person.ads
  2. with Interfaces.C; use Interfaces.C;
  3. package Person is
  4. type Person_Record is record
  5. Name : String(1 .. 100);
  6. Age : Interfaces.C.int;
  7. Address : String(1 .. 100);
  8. end record with Convention => C_Pass_By_Copy;
  9. function Set_Age_To_Five(P : Person_Record) return Person_Record;
  10. pragma Export (C, Set_Age_To_Five, "Set_Age_To_Five");
  11. end Person;
  1. math.gpr
  2. library project Math is
  3. for Languages use ("Ada");
  4. for Library_Name use "Person";
  5. for Source_Dirs use ("src");
  6. for Object_Dir use "obj";
  7. for Library_Dir use "lib";
  8. for Library_Kind use "Dynamic";
  9. end Math;

然后,我有一个C头文件math.h:

  1. #ifndef MATH_H
  2. #define MATH_H
  3. #ifdef __cplusplus
  4. extern "C"
  5. #endif
  6. typedef struct {
  7. char Name[101];
  8. int Age;
  9. char Address[101];
  10. } Person_Record;
  11. Person_Record Set_Age_To_Five(Person_Record P);
  12. #ifdef __cplusplus
  13. #endif
  14. #endif /* MATH_H */

最后是我的C代码:

  1. #include <stdio.h>
  2. #include "math.h"
  3. int main() {
  4. Person_Record p, q, r;
  5. // Initialize the person record
  6. snprintf(p.Name, sizeof(p.Name), "John");
  7. p.Age = 25;
  8. snprintf(p.Address, sizeof(p.Address), "123 Main St");
  9. // Call the Set_Age_To_Five function from the DLL
  10. q = Set_Age_To_Five(p);
  11. // Print the modified person record
  12. printf("Name: %s\n", q.Name);
  13. printf("Age: %d\n", q.Age);
  14. printf("Address: %s\n", q.Address);
  15. return 0;
  16. }

这应该在执行时返回:

  1. Name: John
  2. Age: 5
  3. Address: 123 Main St

但实际上返回:

  1. Name: John
  2. Age: 25
  3. Address: 123 Main St

我已尝试通过变量传递,通过引用传递,使用Ada中的Convention C 和Convention Pass_By_C。

英文:

I am trying to get Ada code to compile to a DLL to be callable in C. So far I have managed to get this to work for simple types such as integers but i'm having difficulty with more complex types such as array's or records/structs.

Below is my Ada code for an record/struct. It is a dynamic library which i compile to a dll using the command "gprbuild -P ./math.gpr -p":

  1. person.adb
  2. with Interfaces.C; use Interfaces.C;
  3. package body Person is
  4. function Set_Age_To_Five(P : Person_Record) return Person_Record is
  5. Result : Person_Record;
  6. begin
  7. Result := P;
  8. Result.Age := 5;
  9. return Result;
  10. end Set_Age_To_Five;
  11. end Person;

person.ads

  1. with Interfaces.C; use Interfaces.C;
  2. package Person is
  3. type Person_Record is record
  4. Name : String(1 .. 100);
  5. Age : Interfaces.C.int;
  6. Address : String(1 .. 100);
  7. end record with Convention =&gt; C_Pass_By_Copy;
  8. function Set_Age_To_Five(P : Person_Record) return Person_Record;
  9. pragma Export (C, Set_Age_To_Five, &quot;Set_Age_To_Five&quot;);
  10. end Person;

math.gpr

  1. library project Math is
  2. for Languages use (&quot;Ada&quot;);
  3. for Library_Name use &quot;Person&quot;;
  4. for Source_Dirs use (&quot;src&quot;);
  5. for Object_Dir use &quot;obj&quot;;
  6. for Library_Dir use &quot;lib&quot;;
  7. for Library_Kind use &quot;Dynamic&quot;;
  8. end Math;

I then have a C header file math.h:

  1. #ifndef MATH_H
  2. #define MATH_H
  3. #ifdef __cplusplus
  4. extern &quot;C&quot;
  5. #endif
  6. typedef struct {
  7. char Name[101];
  8. int Age;
  9. char Address[101];
  10. } Person_Record;
  11. Person_Record Set_Age_To_Five(Person_Record P);
  12. #ifdef __cplusplus
  13. #endif
  14. #endif /* MATH_H */

and finally my C code:

  1. #include &lt;stdio.h&gt;
  2. #include &quot;math.h&quot;
  3. int main() {
  4. Person_Record p, q, r;
  5. // Initialize the person record
  6. snprintf(p.Name, sizeof(p.Name), &quot;John&quot;);
  7. p.Age = 25;
  8. snprintf(p.Address, sizeof(p.Address), &quot;123 Main St&quot;);
  9. // Call the Set_Age_To_Five function from the DLL
  10. q = Set_Age_To_Five(p);
  11. // Print the modified person record
  12. printf(&quot;Name: %s\n&quot;, q.Name);
  13. printf(&quot;Age: %d\n&quot;, q.Age);
  14. printf(&quot;Address: %s\n&quot;, q.Address);
  15. return 0;
  16. }

This should when executed return

  1. Name: John
  2. Age: 5
  3. Address: 123 Main St

Instead its returning:

  1. Name: John
  2. Age: 25
  3. Address: 123 Main St

I've tried passing by variable, passing by reference. using convention C and convention pass by c in ada.

答案1

得分: 3

ADA 到 C 的数据结构互操作通常使用指针进行,更可靠。

ADA:

  1. procedure Set_Age_To_Five (P : in out Person_Record);
  2. pragma Export (C, Set_Age_To_Five, "Set_Age_To_Five");
  3. procedure Set_Age_To_Five (P : in out Person_Record) is
  4. begin
  5. P.Age := 5;
  6. end Set_Age_To_Five;

C:

  1. void Set_Age_To_Five(Person_Record *P);
  2. Set_Age_To_Five(&p);

我理解这种方式可能比从ADA返回新结构不太方便,但似乎ADA返回值的方式存在问题。这可能与ADA使用不同的“按值传递”模型有关,与C 不同。

英文:

ADA to C interop of data structures is typically done using pointers, and is likely to be more reliable.

ADA:

  1. procedure Set_Age_To_Five (P : in out Person_Record);
  2. pragma Export (C, Set_Age_To_Five, &quot;Set_Age_To_Five&quot;);
  3. procedure Set_Age_To_Five (P : in out Person_Record) is
  4. begin
  5. P.Age := 5;
  6. end Set_Age_To_Five;

C:

  1. void Set_Age_To_Five(Person_Record *P);
  2. Set_Age_To_Five(&amp;p);

I do understand that this is rather less convenient than returning the new struct from ADA, but there seems to be a problem with the way ADA returns the value. It might have something to do with the fact that ADA uses a different model for "Pass By Value" than C does.

答案2

得分: 3

你需要明确规定约定,以便满足C语言对函数返回值的期望。

对我而言,这个方法有效(在告诉C语言struct Person_Record有100个字符的字符串成分后;你需要以某种方式管理这些字符串的空终止符):

  1. function Set_Age_To_Five(P : Person_Record) return Person_Record
  2. with
  3. Convention => C,
  4. Export,
  5. External_Name => "Set_Age_To_Five";
英文:

You need to be explicit about conventions in order to do what C expects about (here) function returns.

This works for me (after telling the C that struct Person_Record has 100-character string components; you’ll need to manage the null-termination of those strings somehow):

  1. function Set_Age_To_Five(P : Person_Record) return Person_Record
  2. with
  3. Convention =&gt; C,
  4. Export,
  5. External_Name =&gt; &quot;Set_Age_To_Five&quot;;

答案3

得分: 2

Name 和 Address 字段在你的 Ada 记录中都是 100 个字符的字符串,而在 C 等效中它们是 101 个字符。

英文:

The fields Name and Address in your Ada record are both strings of 100 characters, whereas they are 101 characters in the C equivalent.

答案4

得分: 1

以下是要翻译的内容:

  1. Adainit 被调用了吗?
  2. 你确认所有参数模式/类型都已处理?
  3. C 代码在 "Person_Record Set_Age_To_Five(Person_Record P);" 这行之前是否需要一个 "#extern "C"" 来导入该函数?
  4. 尝试让 "Set_Age_To_Five" 返回一个特殊值,例如 "("Dave Smith", -128, "22 Baker St")",并确保该值被函数传递/初始化。(一旦具备互操作性,可以修改函数使其正确。)

也许更好的方法是扩展返回一个仅有一个字段不同的元素:

  1. function Set_Age_To_Five(P : Person_Record) return Person_Record is
  2. begin
  3. Return Result : Person_Record := P do
  4. Result.Age := 5;
  5. End return;
  6. end Set_Age_To_Five;

希望这有所帮助。

英文:

Troubleshooting time:

  1. Is Adainit being called?
  2. Did you make sure all the parameter-modes/types were handled?
  3. Doesn't the C-code need an "#extern &quot;C&quot;" before the line "Person_Record Set_Age_To_Five(Person_Record P);" to import the function?
  4. Try making the "Set_Age_To_Five" return a special value, like (&quot;Dave Smith&quot;, -128, &quot;22 Baker St&quot;) and ensure that the value is being propegated/initialized by the function. (You can modify the function to be correct once you have interop.)

And, perhaps a better way to return an element with only a single field different, would be extended return:

  1. function Set_Age_To_Five(P : Person_Record) return Person_Record is
  2. begin
  3. Return Result : Person_Record:= P do
  4. Result.Age := 5;
  5. End return;
  6. end Set_Age_To_Five;

Hope that helps.

huangapple
  • 本文由 发表于 2023年5月22日 18:56:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76305462.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定