Derive from Unchecked_Union - ada

I declare a union type like this:
type Access_Kind is (Named, Indexed);
type Array_Type is array (0 .. 1) of Integer;
type Record_Type (Kind : Access_Kind := Access_Kind'First) is record
case Kind is
when Named =>
X, Y : Integer;
when Indexed =>
S : Array_Type;
end case;
end record;
pragma Unchecked_Union (Record_Type);
pragma Convention (C_Pass_By_Copy, Record_Type);
function Create (X, Y : Integer) return Record_Type;
Now when I try to create a derived type:
type Integer2 is new Record_Type;
GNAT gives me the following warning:
warning: in instantiation at line [pragma Convention...]
warning: variant record has no direct equivalent in C
warning: use of convention for type "Integer2" is dubious
So it looks like the pragma Convention is applied to the derived type, but Unchecked_Union is not. I cannot apply it to the derived type again because Record_Type already has primitive operations defined (Integer2 is defined in another package).
Is this correct behavior or a GNAT bug? How can I correctly derive from Unchecked_Union types so that the new type inherits the Unchecked_Union pragma?
GNAT Version: GNAT GPL 2012 (20120509).

One possibility would be to declare the operations of Record_Type in a nested package, say Ops, so that they aren't primitive:
package Union is
type Access_Kind is (Named, Indexed);
type Array_Type is array (0 .. 1) of Integer;
type Record_Type (Kind : Access_Kind := Access_Kind'First) is record
case Kind is
when Named =>
X, Y : Integer;
when Indexed =>
S : Array_Type;
end case;
end record;
pragma Unchecked_Union (Record_Type);
pragma Convention (C_Pass_By_Copy, Record_Type);
-- If P was declared immediately within Union, it would be
-- primitive, and it wouldn't be possible to declare
-- representation aspects for Integer2.
package Ops is
procedure P (R : Record_Type) is null;
-- "is null" only so that I can use -gnatR without needing a
-- body.
end Ops;
type Integer2 is new Record_Type;
pragma Unchecked_Union (Integer2);
pragma Convention (C_Pass_By_Copy, Integer2);
end Union;
Using -gnatR to display the chosen representation, I get
$ gnatmake -c -u -f -gnatwa union.ads -gnatR
gcc -c -gnatwa -gnatR -gnat05 union.ads
Representation information for unit Union (spec)
------------------------------------------------
for Array_Type'Size use 64;
for Array_Type'Alignment use 4;
for Array_Type'Component_Size use 32;
for Record_Type'Size use 64;
for Record_Type'Alignment use 4;
for Record_Type use record
Kind at ?? range 0 .. -1;
X at 0 range 0 .. 31;
Y at 4 range 0 .. 31;
S at 0 range 0 .. 63;
end record;
for Integer2'Size use 64;
for Integer2'Alignment use 4;
for Integer2 use record
Kind at ?? range 0 .. -1;
X at 0 range 0 .. 31;
Y at 4 range 0 .. 31;
S at 0 range 0 .. 63;
end record;
That said, I think GNAT's behaviour is wrong.
ARM 13.1(0.1) says that there are representational and operational aspects, and (1) defines the representational aspects. (10) is why we need to avoid primitive operations. (15) says that representational aspects are inherited by derived types; but (15.1) says that operational aspects are not, "unless specified" for the specific aspect. I guess that "specified" means "in the ARM for language-defined aspects, or by the vendor for vendor-defined aspects".
B3.3(3.1) states that Unchecked_Union is a representation aspect. It should therefore be inherited.
B3.1(0.1) states that interfacing aspects, including Convention, are representation aspects. C_Pass_By_Copy should therefore be inherited.
I'll work up a bug report.

In Ada 2012, pragma Unchecked_Union is obsolescent, and you can now specify aspect Unchecked_Union. In either case, as discussed in Rationale for Ada 2005: 6.4 Pragmas and Restrictions, the warning is a reminder that unchecked union types were "introduced in Ada 2005 for the sole purpose of interfacing to C programs and not for living dangerously." A type derived from Record_Type is not forbidden; it's just a bad idea, as it propagates the opportunity for erroneous execution, as shown in the Notes section. Instead, encapsulate the union in the body of your binding and derive from your higher level type.
Addendum: Checking an older versions for reference,
GNAT 4.6
Copyright 1996-2010, Free Software Foundation, Inc.
...
Representation information for unit Unchecked (body)
----------------------------------------------------
for Array_Type'Size use 64;
for Array_Type'Alignment use 4;
for Array_Type'Component_Size use 32;
for Record_Type'Size use 64;
for Record_Type'Alignment use 4;
for Record_Type use record
Kind at ?? range 0 .. -1;
X at 0 range 0 .. 31;
Y at 4 range 0 .. 31;
S at 0 range 0 .. 63;
end record;
for Integer2'Size use 96;
for Integer2'Alignment use 4;
for Integer2 use record
Kind at 0 range 0 .. 7;
X at 4 range 0 .. 31;
Y at 8 range 0 .. 31;
S at 4 range 0 .. 63;
end record;

Related

Translating C flexible array members with length fields to Ada

While writing bindings for interfacing with C code, I am finding issues translating the numerous instances of structs with flexible array members to Ada, as such
struct example {
size_t length;
int body[];
};
I've been told for Ada that behaviors like that can be replicated with discriminated types, but I cannot find a way to use the length field as a discriminant, while maintaining the layout of the structure so that the records can be used to interface with the C code, something like
type Example (Count : Integer := Length) is record
Length : Unsigned_64;
Body : Integer (1 .. Count);
end record;
Is there any way to create a type like that with that array? I've been defaulting for now to grabbing the address of that location and declaring the array myself on place for use, is there any better way? Thanks in advance.
Here is an example in which Ada code calls C code, passing objects of this kind from Ada to C and from C to Ada. The C header is c_example.h:
typedef struct {
size_t length;
int body[];
} example_t;
extern void example_print (example_t *e /* in */);
// Print the contents of e.
extern example_t * example_get (void);
// Return a pointer to an example_t that remains owned
// by the C part (that is, the caller can use it, but should
// not attempt to deallocate it).
The C code is c_example.c:
#include <stdio.h>
#include "c_example.h"
void example_print (example_t *e /* in */)
{
printf ("C example: length = %zd\n", e->length);
for (int i = 0; i < e->length; i++)
{
printf ("body[ %d ] = %d\n", i, e->body[i]);
}
} // example_print
static example_t datum = {4,{6,7,8,9}};
example_t * example_get (void)
{
return &datum;
} // example_get
The C-to-Ada binding is defined in c_binding.ads:
with Interfaces.C;
package C_Binding
is
pragma Linker_Options ("c_example.o");
use Interfaces.C;
type Int_Array is array (size_t range <>) of int
with Convention => C;
type Example_t (Length : size_t) is record
Bod : Int_Array(1 .. Length);
end record
with Convention => C;
type Example_ptr is access all Example_t
with Convention => C;
procedure Print (e : in Example_t)
with Import, Convention => C, External_Name => "example_print";
function Get return Example_Ptr
with Import, Convention => C, External_Name => "example_get";
end C_Binding;
The test main program is flexarr.adb:
with Ada.Text_IO;
with C_Binding;
procedure FlexArr
is
for_c : constant C_Binding.Example_t :=
(Length => 5, Bod => (55, 66, 77, 88, 99));
from_c : C_Binding.Example_ptr;
begin
C_Binding.Print (for_c);
from_c := C_Binding.Get;
Ada.Text_IO.Put_Line (
"Ada example: length =" & from_c.Length'Image);
for I in 1 .. from_c.Length loop
Ada.Text_IO.Put_Line (
"body[" & I'Image & " ] =" & from_c.Bod(I)'Image);
end loop;
end FlexArr;
I build the program thusly:
gcc -c -Wall c_example.c
gnatmake -Wall flexarr.adb
And this is the output from ./flexarr:
C example: length = 5
body[ 0 ] = 55
body[ 1 ] = 66
body[ 2 ] = 77
body[ 3 ] = 88
body[ 4 ] = 99
Ada example: length = 4
body[ 1 ] = 6
body[ 2 ] = 7
body[ 3 ] = 8
body[ 4 ] = 9
So it seems to work. However, the Ada compiler (gnat) gives me some warnings from the C_Binding package:
c_binding.ads:14:12: warning: discriminated record has no direct equivalent in C
c_binding.ads:14:12: warning: use of convention for type "Example_t" is dubious
This means that while this interfacing method works with gnat, it might not work with other compilers, for example Ada compilers that allocate the Bod component array separately from the fixed-size part of the record (whether such a compiler would accept Convention => C for this record type is questionable).
To make the interface more portable, make the following changes. In C_Binding, change Length from a discriminant to an ordinary component and make Bod a fixed-size array, using some maximum size, here 1000 elements as an example:
type Int_Array is array (size_t range 1 .. 1_000) of int
with Convention => C;
type Example_t is record
Length : size_t;
Bod : Int_Array;
end record
with Convention => C;
In the test main program, change the declaration of for_c to pad the array with zeros:
for_c : constant C_Binding.Example_t :=
(Length => 5, Bod => (55, 66, 77, 88, 99, others => 0));
For speed, you could instead let the unused part of the array be uninitialized: "others => <>".
If you cannot find a reasonably small maximum size, it should be possible to define the C binding to be generic in the actual size. But that is getting rather messy.
Note that if all the record/struct objects are created on the C side, and the Ada side only reads and writes them, then the maximum size defined in the binding is used only for index bounds checking and can be very large without impact on the memory usage.
In this example I made the Ada side start indexing from 1, but you can change it to start from zero if you want to make it more similar to the C code.
Finally, in the non-discriminated case, I recommend making Example_t a "limited" type ("type Example_t is limited record ...") so that you cannot assign whole values of that type, nor compare them. The reason is that when the C side provides an Example_t object to the Ada side, the actual size of the object may be smaller than the maximum size defined on the Ada side, but an Ada assignment or comparison would try to use the maximum size, which could make the program read or write memory that should not be read or written.
The discriminant is itself a component of the record, and has to be stored somewhere.
This code,
type Integer_Array is array (Natural range <>) of Integer;
type Example (Count : Natural) is record
Bdy : Integer_Array (1 .. Count);
end record;
compiled with -gnatR to show representtion information, says
for Integer_Array'Alignment use 4;
for Integer_Array'Component_Size use 32;
for Example'Object_Size use 68719476736;
for Example'Value_Size use ??;
for Example'Alignment use 4;
for Example use record
Count at 0 range 0 .. 31;
Bdy at 4 range 0 .. ??;
end record;
so we can see that GNAT has decided to put Count in the first 4 bytes, just like the C (well, this is a common C idiom, so I suppose it’s defined behaviour for C struct components to be allocated in source order).
Since this is to be used for interfacing with C, we could say so,
type Example (Count : Natural) is record
Bdy : Integer_Array (1 .. Count);
end record
with Convention => C;
but as Niklas points out the compiler is doubtful about this (it’s warning you that the Standard doesn’t specify the meaning of the construct).
We could confirm at least that we want Count to be in the first 4 bytes, adding
for Example use record
Count at 0 range 0 .. 31;
end record;
but I don’t suppose that would stop a different compiler using a different scheme (e.g. two structures, the first containing Count and the address of the second, Bdy).
C array indices always start at 0.
If you want to duplicate the C structure remember that the discriminant is a member of the record.
type Example (Length : Integer) is record
body : array(0 .. Length - 1) of Integer;
end record;

Convention for handling foreign binary data

I'm a C programmer and started learning Ada a few weeks ago. I have been puzzled by how Ada handles foreign binary data, such as when decoding a communications packet stored in a serial input buffer.
In C, I would define a packed structure to reflect the layout of the packet, and then cast the pointer-to-buffer to pointer-to-structure to access the individual elements in the communications data. What is the typical way to do such decoding in Ada?
I have tried to replicate the same method for C in Ada with the following
-- Fundamental types for fields in the packet
type Station_Addr_Type is mod 2**8;
type Func_Code_Type is (FUNC1, FUNC2);
for Func_Code_Type use
(
FUNC1 => 1,
FUNC2 => 2
);
type Packet is
record
Station_Addr : Station_Addr_Type;
Func_Code : Func_Code_Type;
end record;
-- attempts to reflect packet binary layout
for Packet use
record at mod 1;
Station_Addr at 0 range 0 .. 7;
Func_Code at 1 range 0 .. 7;
end record;
I then defined the array for the communications receive buffer that accepts foreign binary data (possibly from a different architecture):
type Communication_Data is mod 2**8;
for Communication_Data'Size use 8;
type Communication_Buffer is array (Natural range <>) of Communication_Data;
Buffer : Communication_Buffer (0 .. 20);
Then a procedure that decodes such communications
procedure Decode_Packet (Packet_Provided : in Packet);
-- non-working sample
declare
begin
-- Attempts to sanity-check packet by object casting
Decode_Packet (Packet (Buffer));
---------------^----
Error: invalid conversion, not compatible with type "Communication_Buffer"
exception
when others =>
raise Decode_Failure;
end;
However, the compiler forbids such casting with error as shown. Thanks for reading this far. My question is,
Regarding the correct way of decoding foreign binary data, am I "in the ball park", or is there a better way of doing this?
If you sure that the data has a right alignment, you can map a Packet object to space allocated by Communication_Data:
Buffer : Communication_Buffer (0 .. 20);
Pkg : Packet;
pragma Import (Ada, Pkg);
for Pkg'Address use Buffer (0)'Address;
The same with aspect syntax:
Buffer : Communication_Buffer (0 .. 20);
Pkg : Packet
with Import, Address => Buffer (0)'Address;
Another way is to use Ada.Unchecked_Conversion, but you should be sure that Buffer and Packet have the same size:
subtype Packet_Buffer is Communication_Buffer (1 .. 2);
function To_Packet is new Ada.Unchecked_Conversion
(Packet_Buffer, Packet);
Pkg : Packet := To_Packet (Buffer (0 .. 1));
PS. If you want an endianness independent code you may also need a Scalar_Storage_Order (GNAT implementation defined) aspect.
PS. I recommend also take a look at "Safe Communication" chapter of Safe and Secure Software booklet.

Is possible in Ada a discontinuous subtype definition?

Let's suppose I have the following constants in order to define a subtype that only admits valid values within its range definition:
type Unsigned_4_T is mod 2**4;
valid_1 : constant Unsigned_4_T := 0;
valid_2 : constant Unsigned_4_T := 1;
invalid_1 : constant Unsigned_4_T := 2;
valid_3 : constant Unsigned_4_T := 3;
Now, I would like to define a subtype VALID_VALUES_T of Unsigned_4_T in a way that it only accepts the valid values using the constants valid_1, valid_2 and valid_3.
I have been trying to achieve that using a Static_Predicate but, when I declare a variable of the wanted subtype and I assign the invalid_1 value, there is no constraint nor compilation error as I expected.
The AdaCore compiler requires you to enable assertions to generate the exception you need. Compile your program with the -gnata flag.
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;
with System.Assertions; use System.Assertions;
procedure Main is
type Unsigned is mod 2**4;
subtype discontinuous is Unsigned with
Static_Predicate => discontinuous in 0 | 1 | 3;
Num : discontinuous;
begin
for I in Unsigned'Range loop
Num := I;
Put_Line(Num'Image);
end loop;
exception
when E :Assert_Failure =>
Put_Line("Invalid value assigned to Num: " & Exception_Message(E));
end Main;
The output of the program is:
0
1
Invalid value assigned to Num: Static_Predicate failed at main.adb:12

How can I define a structure that will contain two multidimensional unconstrained arrays an pass it to a procedure

I am new to Ada so, I have a question: How can I define a structure that will contain two multidimensional unconstrained arrays an pass it to a procedure.
Following the code that I have wrote:
package SDR_Treatment is
-----------------------------------------------------------------------------
-- CONSTANTS -
-----------------------------------------------------------------------------
GEO_SPACIAL_AND_SPEED: constant Natural := 1; --Type of Data
-----------------------------------------------------------------------------
type Slicing_Pointers is array (Positive range <>) of Integer;
type Region is array (Positive range <>, Positive range <>, Positive range <>) of Integer;
type Location_and_Speed_Region is record
Region_Id : Natural := 0;
SP: Slicing_Pointers;
Rig: Region;
end record;
procedure SDR_Encoding(lsr: in out Location_and_Speed_Region);
end SDR_Treatment;
-----------------------------------------------------------------------------
The package body:
with Ada.Text_IO; use Ada.Text_IO;
package body SDR_Treatment is
procedure SDR_Encoding(lsr: in out Location_and_Speed_Rigion) is
-- lsr : rig;
begin
Ada.Text_IO.Put_Line("Encoding SDR...");
end SDR_Encoding;
end SDR_Treatment;
_____________________________________________________________________________
This is the error that i have got from compiler
C:\GNAT\2017\bin\src\sdr_treatment.ads
18:11 unconstrained subtype in component declaration
19:12 unconstrained subtype in component declaration
Thanks in advance :-)
As the compiler says, you can't declare record components with unconstrained subtypes.
You have to fix the constraints as a part of the record declaration:
package SDR_Treatment is
type Slicing_Pointers is array (Positive range <>) of Integer;
type Region is array (Positive range <>,
Positive range <>,
Positive range <>) of Integer;
type Location_and_Speed_Region (Low : Positive;
High : Natural;
X_Low : Positive;
X_High : Natural;
Y_Low : Positive;
Y_High : Natural;
Z_Low : Positive;
Z_High : Natural) is
record
Region_ID : Natural := 0;
SP : Slicing_Pointers (Low .. High);
Rig : Region (X_Low .. X_High,
Y_Low .. Y_High,
Z_Low .. Z_High);
end record;
end SDR_Treatment;
You should think your declaration as C arrays in
type Location_and_Speed_Region is record
Region_Id : Natural := 0;
SP: Slicing_Pointers;
Rig: Region;
end record;
Here, you're trying to declare a record where the size of its static components is not known at compile time which is almost the same as declaring
struct {
char int[];
};
If you want it to be more "dynamic" while avoiding using accesses (i.e. pointers), take a look at discriminated record.
If you prefer something using dynamically allocated memory, use access
types

Is N-1 the largest term which could be used for Generic in VHDL

I am new to VHDL and I wanted to ask that what generic term could I use If i wanted to write any size of input vector which could be developed?
GENERIC (n1 : integer);
x:IN BIT_VECTOR(n1-1 downto 0);
Is that a correct example?
Your generic has no default value visible.
Your declaration for x is incomplete. It appears to be an entity declarative item with a mode while you don't have a port declaration.
This VHDL code is syntactically and semantically valid:
entity foo is
generic ( n1: integer);
port (
x: in bit_vector(n1-1 downto 0)
);
end entity;
architecture fum of foo is
begin
end architecture;
It will analyze. It can't be elaborated without the value of n1 being known:
entity foo_tb is
constant N_1: integer := 4;
end entity;
architecture fum of foo_tb is
signal x: bit_vector (N_1-1 downto 0);
begin
DUT:
entity work.foo
generic map (n1 => N_1)
port map ( x => x);
end architecture;
Entity foo by itself can't be the top level of an elaborated model because n1 isn't defined for elaboration.
Entity foo_tb can be elaborated, it uses the constant N_1 to supply a value to n1.
foo_tb can even be simulated, but it will exit immediately because there are no pending signal assignments after initialization.
Neither foo nor foo_tb can be synthesize. foo_tb because it has no ports and any logic in it's design hierarchy would be optimized away as unused. foo because it only has an output and is at best a constant.
If foo had multiple ports, with outputs depending on inputs it would be eligible for synthesis or simulation as long as the generic was defined for elaboration.
(And the moral here is to use a Minimal, Complete, and Verifiable example so someone doesn't have to wave their hands around it's shortcomings).
You can use every term, as far as it's result does not exceed the BIT_VECTORS's array range.
BIT_VECTOR definition: type BIT_VECTOR is array (NATURAL range <>) of BIT;
So your term can have results from 0 to 2**32 - 1
Term examples:
4*n1 - 1 downto 0
n1/4 + 8 downto 0
log2ceilnz(n1) - 1 downto 0
2**n1 - 1 downto 0
According to the " Paebbels" comment I edit this answer :
Every time you want to synthesize your code, synthesis tool should know about the size of parameters you used, Otherwise what exactly you want to synthesize ?!!! (what hardware ?!)
If you want to synthesize your top module code which contains a generic parameter in it's own entity, you can assign it with a default value such as the following code :
ENTITY ... IS
GENERIC(n1 : INTEGER := 8);
PORT(
-- use generic parameter
);
END ENTITY;
Also you can use the generic parameter inside architecture ( size of signals, index of loops, ... ).

Resources