从 Ada 调用 scanf
Posted
技术标签:
【中文标题】从 Ada 调用 scanf【英文标题】:Calling scanf from Ada 【发布时间】:2012-01-08 09:58:15 【问题描述】:如何?也就是说,大概有一个适当的 pragma import 声明,但声明会是什么样子?
(我对如何从 Ada 调用更不规则的 C 函数感兴趣,而不是如何解析字符串本身,所以我不是在寻找纯 Ada 解决方案。我的设置是 Gnat,Ubuntu Linux, x64 如果有区别的话。)
【问题讨论】:
另见Q&A。 【参考方案1】:This paper 指出
Ada 无法声明一个函数,该函数接受不同数量的不同类型的参数。可以声明一组“printf”函数,它们接受一个字符串、一个字符串和一个整数、一个字符串和一个浮点数、一个字符串和 2 个整数,等等,然后将每个函数声明为 Import (C) 2.但这需要大量声明,针对应用程序中的每种不同用途声明,因此确实不实用。
scanf()
也是如此,Ada 2012 的额外好处是让您可以在 out
和 access
参数规范之间进行选择(在早期版本中,您必须使用 access
,因为函数没有'不允许有out
参数)。
此外,我不认为 C 编译器必须对可变参数函数使用与普通函数相同的参数传递机制(the reference 暗示了这一点,我记得但现在不能查找最近关于这些线路的对话)。
也就是说,这里有一个示例,它似乎在带有 GCC 4.6.0 的 Mac OS X 上运行良好:
with Ada.Text_IO; use Ada.Text_IO;
with Interfaces.C; use Interfaces.C;
procedure Variadic is
function Scanf (Fmt : char_array; Result : access int) return int;
pragma Import (C, Scanf, "scanf");
Status : int;
Result : aliased int;
begin
Status := Scanf (To_C ("%d\n"), Result'Access);
Put_Line ("status: " & int'Image (Status));
if Status = 1 then
Put_Line ("result: " & int'Image (Result));
end if;
end Variadic;
(不确定格式参数中的\n
!)
【讨论】:
你是正确的参数传递机制。在 C 中,调用可变参数函数需要与函数的实际定义兼容的可见可变参数声明(原型);否则行为未定义。但是由于历史原因,大多数 C 编译器使用与非可变函数兼容的调用约定(ANSI C 之前没有原型,但无论如何都必须调用printf
和scanf
,并且可以随时更改约定从那时起就会破坏现有的代码)。
"\n"
在 Ada 字符串文字中只是一个反斜杠,后跟一个字母 n
,而 To_C
不会将其转换为换行符。而不是"%d\n"
,你需要写"%d" & Ada.Characters.Latin_1.LF
之类的东西。但在这种特殊情况下,没有必要。 scanf
格式字符串(包括换行符)中的任何空白字符序列都会读取零个或多个连续空白字符,因此"%d "
等效于"%d\n"
。 注意: 请注意,如果 scanf
和 "%d"
读取溢出的整数值,则行为是未定义。
@Keith,我明白你对“\n”
的意思。混合语言思维 => 混乱!不良行为的另一个机会:告诉 Ada 传递int
的地址并在格式中指定“%g”
。
纯C也有同样的问题;一些 C 编译器会警告 scanf("%g", &an_int_variable)
,但它们不是必需的,并且行为未定义。【参考方案2】:
一种解决方法是在 C 中声明多个非可变包装函数,并将它们导入 Ada。
例如:
int scanf_i(const char *format, int *i_ptr)
return scanf(format, i_ptr);
int scanf_d(const char *format, double *d_ptr)
return scanf(format, d_ptr);
然后用pragma Import
在Ada中声明重载的scan()函数。
这样,您不会尝试从 Ada 调用可变参数函数;所有固定到可变的转换都发生在 C 端。只要正确编写所有包装器,您就可以获得比直接调用 scanf()
更多的类型检查。
您只需要为要传递的每组参数类型设置一个不同的包装器。
如果您只有几个调用,这可能没问题,但它不能很好地扩展。
【讨论】:
以上是关于从 Ada 调用 scanf的主要内容,如果未能解决你的问题,请参考以下文章
Ada - (Streams) 如何在事先不知道字符串长度的情况下正确调用 String'Read()