Calling scanf from Ada
Asked Answered
F

2

2

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.)

Fasciculus answered 8/1, 2012 at 9:58 Comment(1)
See also this Q&A.Cuisine
I
2

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!)

Impatiens answered 8/1, 2012 at 17:40 Comment(4)
You're right abut the parameter passing mechanisms. In C, calling a variadic function requires a visible variadic declaration (prototype) that's compatible with the actual definition of the function; otherwise the behavior is undefined. But most C compilers use a calling convention that's compatible with non-variadic functions, for historical reasons (pre-ANSI C had no prototypes but had to make calls to printf and scanf work anyway, and changing the convention at any time since then would have broken existing code).Yakutsk
"\n" in an Ada string literal is just a backslash followed by a letter n, and To_C doesn't translate it to a newline character. Rather than "%d\n", you'd need to write something like "%d" & Ada.Characters.Latin_1.LF. But in this particular case it's not necessary. Any sequence of whitespace characters in a scanf format string (including newline) reads zero or more consecutive whitespace characters, so "%d " is equivalent to "%d\n". NOTE : Beware that if scanfwith "%d" reads an integer value that overflows, the behavior is undefined.Yakutsk
@Keith, I see what you mean about “\n”. Mixed-language thinking => confusion! Another opportunity for bad behaviour: telling Ada to pass the address of an int and specifying “%g” in the format.Impatiens
Pure C has the same problem; some C compilers will warn about scanf("%g", &an_int_variable), but they're not required to, and the behavior is undefined.Yakutsk
Y
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.

Yakutsk answered 16/1, 2012 at 4:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.