porton
porton

Reputation: 5827

Interfacing Ada enumerations and C enums

Let in C code are defined:

typedef enum { A=1, B=2 } option_type;

void f(option_type option);

Let we also have Ada code:

type Option_Type is (A, B);
for Option_Type'Size use Interfaces.C.int'Size;
for Option_Type use (A=>1, B=>2);

X: Option_Type := A;

Which of the following code is correct (accordingly RM)?

-- First code

declare
   procedure F (Option: Option_Type)
      with Import, Convention=>C, External_Name=>"f";
begin
   F(X);
end;

or

-- Second code

declare
   procedure F (Option: Interfaces.C.unsigned)
      with Import, Convention=>C, External_Name=>"f";
   function Conv is new Ada.Unchecked_Conversion(Option_Type, Interfaces.C.unsigned);
begin
   F(Conv(X));
end;

I think both first and second Ada fragments are correct but am not sure.

Upvotes: 3

Views: 1394

Answers (1)

Keith Thompson
Keith Thompson

Reputation: 263657

Neither is 100% correct.

In C:

typedef enum { A=1, B=2 } option_type;

In Ada:

type Option_Type is (A, B);
for Option_Type'Size use Interfaces.C.int'Size;
for Option_Type use (A=>1, B=>2);

The Ada code assumes that the C type option_type has the same size as a C int. Your second snippet assumes it has the same representation as a C unsigned int.

Neither assumption is supported by the C standard.

Quoting the N1570 draft, section 6.7.2.2, paragraph 4:

Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration.

So the C type option_type could be as narrow as 1 byte or as wide as the widest supported integer type (typically 8 bytes), and it could be either signed or unsigned. C restricts the values of the enumeration constants to the range of type int, but that doesn't imply that the type itself is compatible with int -- or with unsigned int.

If you have knowledge of the characteristics of the particular C compiler you're using (the phrase "implementation-defined" means that those characteristics must be documented), then you can rely on those characteristics -- but your code is going to be non-portable.

I'm not aware of any completely portable way to define an Ada type that's compatible with a given C enumeration type. (I've been away from Ada for a long time, so I could be missing something.)

The only portable approach I can think of is to write a C wrapper function that takes an argument of a specified integer type and calls f(). The conversion from the integer type to option_type is then handled by the C compiler, and the wrapper exposes a function with an argument of known type to Ada.

void f_wrapper(int option) {
    f(option); /* the conversion from int to option_type is implicit */
}

Upvotes: 5

Related Questions