Calling scanf from Ada

2019-02-27 11:57发布

问题:

How do you call scanf from Ada? That is, presumably with an appropriate pragma import declaration, but what would the declaration look like?

(I'm interested in how to call C functions of the more unruly variety from Ada, not how to parse strings per se, so I'm not looking for a pure Ada solution. My setup is Gnat, Ubuntu Linux, x64 if it makes a difference.)

回答1:

This paper points out that

Ada has no way of declaring a function that takes different numbers of parameters of different types. One could declare a set of “printf” functions which take a string, a string and an integer, a string and a floating point number, a string and 2 integers, and so on, and then declare each one to be Import (C)2. But this requires lots of declarations, one for each different kind of use in the application program, so it really isn’t practical.

The same would be true of scanf(), which with Ada 2012 has the added bonus of letting you choose between out and access parameter specs (in earlier revisions, you had to use access because functions weren’t allowed to have out parameters).

In addition, I don’t believe it’s required that the C compiler has to use the same parameter passing mechanisms for variadic functions as it does for ordinary ones (the reference hints at this, and I recall but can’t now find a recent conversation on these lines).

That said, here’s an example which appears to work fine on Mac OS X with GCC 4.6.0:

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;

(not sure about the \n in the format parameter!)



回答2:

One workaround would be to declare multiple non-variadic wrapper functions in C, and import them in Ada.

For example:

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);
}

and then declare overloaded scan() function in Ada with pragma Import.

This way, you're not attempting to call variadic functions from Ada; all the fixed-to-variadic conversion happens on the C side. And as long as all the wrappers are written correctly, you get more type checking than you would with a direct call to scanf().

You just need a distinct wrapper for each set of parmaeter types you want to pass.

This is probably ok if you only have a few calls, but it doesn't scale well.