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

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

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:

person.adb
with Interfaces.C; use Interfaces.C;

package body Person is

   function Set_Age_To_Five(P : Person_Record) return Person_Record is
      Result : Person_Record;
   begin
      Result := P;
      Result.Age := 5;
      return Result;
   end Set_Age_To_Five;

end Person;
person.ads
with Interfaces.C; use Interfaces.C;

package Person is

   type Person_Record is record
      Name    : String(1 .. 100);
      Age     : Interfaces.C.int;
      Address : String(1 .. 100);
   end record with Convention => C_Pass_By_Copy;

   function Set_Age_To_Five(P : Person_Record) return Person_Record;
   pragma Export (C, Set_Age_To_Five, "Set_Age_To_Five");

end Person;
math.gpr
library project Math is
    for Languages use ("Ada");
    for Library_Name use "Person";
    for Source_Dirs use ("src");
    for Object_Dir use "obj";
    for Library_Dir use "lib";
    for Library_Kind use "Dynamic";
end Math;

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

#ifndef MATH_H
#define MATH_H

#ifdef __cplusplus
extern "C"
#endif

typedef struct {
    char Name[101];
    int Age;
    char Address[101];
} Person_Record;

Person_Record Set_Age_To_Five(Person_Record P);

#ifdef __cplusplus

#endif

#endif /* MATH_H */

最后是我的C代码:

#include <stdio.h>
#include "math.h"

int main() {
    Person_Record p, q, r;

    // Initialize the person record
    snprintf(p.Name, sizeof(p.Name), "John");
    p.Age = 25;
    snprintf(p.Address, sizeof(p.Address), "123 Main St");

    // Call the Set_Age_To_Five function from the DLL
    q = Set_Age_To_Five(p);

    // Print the modified person record
    printf("Name: %s\n", q.Name);
    printf("Age: %d\n", q.Age);
    printf("Address: %s\n", q.Address);

    return 0;
}

这应该在执行时返回:

Name: John
Age: 5
Address: 123 Main St

但实际上返回:

Name: John
Age: 25
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":

person.adb
with Interfaces.C; use Interfaces.C;

package body Person is

   function Set_Age_To_Five(P : Person_Record) return Person_Record is
      Result : Person_Record;
   begin
      Result := P;
      Result.Age := 5;
      return Result;
   end Set_Age_To_Five;

end Person;

person.ads

with Interfaces.C; use Interfaces.C;

package Person is

   type Person_Record is record
      Name    : String(1 .. 100);
      Age     : Interfaces.C.int;
      Address : String(1 .. 100);
   end record with Convention =&gt; C_Pass_By_Copy;

   function Set_Age_To_Five(P : Person_Record) return Person_Record;
   pragma Export (C, Set_Age_To_Five, &quot;Set_Age_To_Five&quot;);

end Person;

math.gpr

library project Math is
    for Languages use (&quot;Ada&quot;);
    for Library_Name use &quot;Person&quot;;
    for Source_Dirs use (&quot;src&quot;);
    for Object_Dir use &quot;obj&quot;;
    for Library_Dir use &quot;lib&quot;;
    for Library_Kind use &quot;Dynamic&quot;;
end Math;

I then have a C header file math.h:

#ifndef MATH_H
#define MATH_H

#ifdef __cplusplus
extern &quot;C&quot;
#endif

typedef struct {
    char Name[101];
    int Age;
    char Address[101];
} Person_Record;

Person_Record Set_Age_To_Five(Person_Record P);

#ifdef __cplusplus

#endif

#endif /* MATH_H */

and finally my C code:

#include &lt;stdio.h&gt;
#include &quot;math.h&quot;

int main() {
    Person_Record p, q, r;

    // Initialize the person record
    snprintf(p.Name, sizeof(p.Name), &quot;John&quot;);
    p.Age = 25;
    snprintf(p.Address, sizeof(p.Address), &quot;123 Main St&quot;);

    // Call the Set_Age_To_Five function from the DLL
    q = Set_Age_To_Five(p);

    // Print the modified person record
    printf(&quot;Name: %s\n&quot;, q.Name);
    printf(&quot;Age: %d\n&quot;, q.Age);
    printf(&quot;Address: %s\n&quot;, q.Address);

    return 0;
}

This should when executed return

Name: John
Age: 5
Address: 123 Main St

Instead its returning:

Name: John
Age: 25
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:

procedure Set_Age_To_Five (P : in out Person_Record);
pragma Export (C, Set_Age_To_Five, "Set_Age_To_Five");

procedure Set_Age_To_Five (P : in out Person_Record) is
begin
   P.Age := 5;
end Set_Age_To_Five;

C:

void Set_Age_To_Five(Person_Record *P);
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:

procedure Set_Age_To_Five (P : in out Person_Record);
pragma Export (C, Set_Age_To_Five, &quot;Set_Age_To_Five&quot;);

procedure Set_Age_To_Five (P : in out Person_Record) is
begin
   P.Age := 5;
end Set_Age_To_Five;

C:

void Set_Age_To_Five(Person_Record *P);
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个字符的字符串成分后;你需要以某种方式管理这些字符串的空终止符):

   function Set_Age_To_Five(P : Person_Record) return Person_Record
   with
     Convention => C,
     Export,
     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):

   function Set_Age_To_Five(P : Person_Record) return Person_Record
   with
     Convention =&gt; C,
     Export,
     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")",并确保该值被函数传递/初始化。(一旦具备互操作性,可以修改函数使其正确。)

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

function Set_Age_To_Five(P : Person_Record) return Person_Record is
begin
  Return Result : Person_Record := P do
     Result.Age := 5;
  End return;
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:

function Set_Age_To_Five(P : Person_Record) return Person_Record is
begin
  Return Result : Person_Record:= P do
     Result.Age := 5;
  End return;
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:

确定