英文:
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 => 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;
I then have a C header file 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 */
and finally my C code:
#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;
}
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, "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);
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 => C,
Export,
External_Name => "Set_Age_To_Five";
答案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
以下是要翻译的内容:
Adainit
被调用了吗?- 你确认所有参数模式/类型都已处理?
- C 代码在 "Person_Record Set_Age_To_Five(Person_Record P);" 这行之前是否需要一个 "#extern "C"" 来导入该函数?
- 尝试让 "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:
- Is
Adainit
being called? - Did you make sure all the parameter-modes/types were handled?
- Doesn't the C-code need an "
#extern "C"
" before the line "Person_Record Set_Age_To_Five(Person_Record P);
" to import the function? - Try making the "
Set_Age_To_Five
" return a special value, like("Dave Smith", -128, "22 Baker St")
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论