I have an unusual situation in which elaboration code is simply not being executed at all. This is not an elaboration order issue, but rather an elaboration at all issue.
The problem is that I don't "with" the unit in question whatsoever, yet in theory it should still be accessible, as long as its elaboration occurs.
Of course I could just add a useless "with" for the unit in question, but in my real use case there are a large number of units that I would have to do that with.
My question is if there is any way either in the code, through pragmas, in the gpr project file, or through command-line switches that I could force the compiler to include a file even though it thinks the file isn't referenced?
Here is a minimal working example:
as.ads:
package As is
type A is tagged null record;
type Nothing is null record;
function Create (Ignored : not null access Nothing) return A;
function Image (From : A) return String;
end As;
as.adb:
package body As is
function Create (Ignored : not null access Nothing) return A is
(null record);
function Image (From : A) return String is ("A");
end As;
finder.ads:
with Ada.Tags;
package Finder is
procedure Register (Name : String; Tag : Ada.Tags.Tag);
function Find (Name : String; Default : Ada.Tags.Tag) return Ada.Tags.Tag;
end Finder;
finder.adb:
with Ada.Containers.Indefinite_Vectors;
package body Finder is
type Name_Tag (Size : Natural) is
record
Name : String (1 .. Size);
To : Ada.Tags.Tag;
end record;
package Name_Tag_Vectors is new Ada.Containers.Indefinite_Vectors (Positive, Name_Tag);
Name_Tags : Name_Tag_Vectors.Vector := Name_Tag_Vectors.Empty_Vector;
procedure Register (Name : String; Tag : Ada.Tags.Tag) is begin
Name_Tags.Append ((Name'Length, Name, Tag));
end Register;
function Find (Name : String; Default : Ada.Tags.Tag) return Ada.Tags.Tag is begin
for Tag of Name_Tags loop
if Tag.Name = Name then
return Tag.To;
end if;
end loop;
return Default;
end Find;
end Finder;
bs.ads:
with As;
package Bs is
type B is new As.A with null record;
function Create (Ignored : not null access As.Nothing) return B;
function Image (From : B) return String;
end Bs;
bs.adb:
with Finder;
package body Bs is
function Create (Ignored : not null access As.Nothing) return B is
(As.Create (Ignored) with null record);
function Image (From : B) return String is ("B");
begin
Finder.Register ("B", B'Tag);
end Bs;
test.adb:
with As; use As;
-- with Bs; -- (uncommenting this line solves my problem, but what if I had the rest of the alphabet?)
with Finder;
with Ada.Tags.Generic_Dispatching_Constructor;
with Ada.Text_IO;
procedure Test is
function Constructor is new Ada.Tags.Generic_Dispatching_Constructor (
T => A,
Parameters => Nothing,
Constructor => Create);
Nada : aliased Nothing := (null record);
What : A'Class := Constructor (Finder.Find ("B", A'Tag), Nada'Access);
begin
Ada.Text_IO.Put_Line (What.Image);
end Test;
The compiler thinks your package Bs isn't referenced because it isn't. You don't have a with clause for it, so it's not part of your program.
A simple example:
a.ads
package A is
procedure Blah;
end A;
a.adb
with Ada.Text_IO;
package body A is
procedure Blah is begin null; end Blah;
begin
Ada.Text_IO.Put_Line("Elaborate A");
end A;
b.ads
package B is
procedure Blah;
end B;
b.adb
with Ada.Text_IO;
package body B is
procedure Blah is begin null; end Blah;
begin
Ada.Text_IO.Put_Line("Elaborate B");
end B;
main.adb
with Ada.Text_IO;
with A;
procedure Main is
begin
Ada.Text_IO.Put_Line("Main");
end Main;
When I run main, it prints
Elaborate A
Main
It doesn't print Elaborate B because that package isn't part of the program; it's just a couple of source files in the same directory.
The obvious solution is to add the with clauses.
I don't know whether there's a less obvious solution. If there is, it's probably compiler-specific. But I'm not sure why a compiler would have a feature that lets you incorporate an otherwise unused package into a program.
What I’ve done (e.g. here ff) is to actually reference the units in the main program (with pragma Unreferenced to prevent warnings).
Alternatively, you could have a package e.g. Required_Units with all the necessary withs included, and then with that from the main program.
Even if there was some alternative process, you’d have to tell it what units you need to have included; might as well go with the flow and do it in Ada!
Since the package Bs is invisible to your program, so is the type B.
So the next question is: why do you need to register type B if it is not used anywhere?
If an Ada compiler did elaborate all units (packages or standalone subprograms) that are irrelevant to a main program, but are visible through source path, it would become really messy!...
Related
I'm kinda new with Ada and recently got an error that I don't seem to know how to solve.
I have the following code:
data.ads
with Text_IO; use text_io;
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
package data is
type file is private;
type file_set is array (Integer range <>) of file;
procedure file_Print (T : in out file); --Not used
private
type file is record
start, deadline : integer;
end record;
end data;
Main.adb
with ada.Integer_Text_IO; use ada.Integer_Text_IO;
procedure Main is
Num_files: integer:=3;
Files:file_set(1..Num_files);
begin
Files(1):=(2,10); -- Expected private type "file" defined at data.ads
for i in 1..Num_Files loop
Put(integer'Image(i));
New_Line;
data.File_Print(Files(i));
But I'm getting this error Expected private type "file" defined at data.ads
How can I access the file type and declare a new array of values in main?
That's right - you don't get to see or manipulate what's inside a private type. That would be breaking encapsulation. Bugs and security risks follow.
You can only interact with a private type via its methods : functions and procedures declared in the package where it's declared.
Now file_set is NOT a private type (you might consider making it private later, for better encapsulation, but for now ....) you can index it to access a file within it (using one of those procedures).
Files(1):=(2,10);
As you want to create a file here, you need a method to create a file ... a bit similar to a constructor in C++, but really more like the Object Factory design pattern. Add this to the package:
function new_file(start, deadline : integer) return file;
And implement it in the package body:
package body data is
function new_file(start, deadline : integer) return file is
begin
-- check these values are valid so we can guarantee a proper file
-- I have NO idea what start, deadline mean, so write your own checks!
-- also there are better ways, using preconditions in Ada-2012
-- without writing explicit checks, but this illustrates the idea
if deadline < NOW or start < 0 then
raise Program_Error;
end if;
return (start => start, deadline => deadline);
end new_file;
procedure file_Print (T : in out file) is ...
end package body;
and that gives the users of your package permission to write
Files(1):= new_file(2,10);
Files(2):= new_file(start => 3, deadline => 15);
but if they attempt to create garbage to exploit your system
Files(3):= new_file(-99,-10); -- Oh no you don't!
this is the ONLY way to create a file, so they can't bypass your checks.
I'm interfacing to a USB device (on Debian Stretch) using hidraw, and I need to process some information supplied by the USB device in the form of wchar_t* which I need to convert into (Ada) Wide_String. This is giving some trouble and I'm not seeing a clean way forward using the facilities in Interfaces.C and Interfaces.C.Strings.
All files are edited down without destroying their consistency. They will build, but without one of these, they won't actually run.
The problem is that device information like Serial Number and Product Name are presented by the Linux device driver as an access stddef_h.wchar_t from which I want to return a Wide_String or even a normal String) and I'm not seeing any good way to get there.
Interfaces.C.Strings has function Value (Item : in chars_ptr) return String; but no equivalent exists for Wide_String that I can see. So I think I need an equivalent Value function for wide characters.
The approach below uses To_Ada (from Interfaces.C) to return a Wide_String given a wchar_array. It fails, of course, because an access wchar_t is not convertible to a wchar_array.
-- helper function to deal with wchar_t * to wide_string
function Value (P : access stddef_h.wchar_t) return Wide_String is
temp : Wide_String(1 .. 256);
count : natural := 0;
-- ugliness to convert pointer types
type sd_wchar_ptr is access all stddef_h.wchar_t;
type wchar_array_ptr is access wchar_array;
Function To_Wchar_Array_Ptr is new Ada.Unchecked_Conversion(sd_wchar_ptr, wchar_array_ptr);
-- this does NOT create the required wchar_array pointer
WCP : wchar_array_ptr := To_Wchar_Array_Ptr(sd_wchar_ptr(P));
begin
Put_Line("Wide string");
To_Ada(WCP.all, temp, count);
Put_Line("Wide string length " & natural'image(count));
return temp(1..count);
end Value;
and the inevitable result
./test_hid
Wide string
Execution terminated by unhandled exception raised STORAGE_ERROR :
stack overflow or erroneous memory access
A similar character by character approach would be possible ... if (and I can't believe I'm saying this!) you could increment access types...
Feels like there's something missing from Interfaces.C here... what am I missing? any ideas to get round this relatively trivial seeming stumbling block?
EDIT : I'm leaning towards some brazen theft from the Interfaces.C.Strings sources with appropriate changes, but I'd welcome alternative suggestions.
The rest of this below is the full story so far (including all code necessary to reproduce)
Step 1 : generate low level Ada bindings automatically using gcc.
gcc -c -fdump-ada-spec-slim /usr/include/hidapi/hidapi.h
producing the low level binding package hidapi_hidapi_h
pragma Ada_2005;
pragma Style_Checks (Off);
with Interfaces.C; use Interfaces.C;
with Interfaces.C.Strings;
with stddef_h;
with System;
package hidapi_hidapi_h is
-- see source file /usr/include/hidapi/hidapi.h
type hid_device_info is record
path : Interfaces.C.Strings.chars_ptr; -- /usr/include/hidapi/hidapi.h:51
vendor_id : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:53
product_id : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:55
serial_number : access stddef_h.wchar_t; -- /usr/include/hidapi/hidapi.h:57
release_number : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:60
manufacturer_string : access stddef_h.wchar_t; -- /usr/include/hidapi/hidapi.h:62
product_string : access stddef_h.wchar_t; -- /usr/include/hidapi/hidapi.h:64
usage_page : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:67
usage : aliased unsigned_short; -- /usr/include/hidapi/hidapi.h:70
interface_number : aliased int; -- /usr/include/hidapi/hidapi.h:75
next : access hid_device_info; -- /usr/include/hidapi/hidapi.h:78
end record;
pragma Convention (C_Pass_By_Copy, hid_device_info); -- /usr/include/hidapi/hidapi.h:49
function hid_enumerate (arg1 : unsigned_short; arg2 : unsigned_short) return access hid_device_info; -- /usr/include/hidapi/hidapi.h:132
pragma Import (C, hid_enumerate, "hid_enumerate");
end hidapi_hidapi_h;
This is a low level binding, exposing C types (and the binding generator has decided that the wchar_t in Interfaces.C isn't good enough, it wants one from stddef.h too, so...
pragma Ada_2005;
pragma Style_Checks (Off);
with Interfaces.C; use Interfaces.C;
package stddef_h is
-- unsupported macro: NULL ((void *)0)
subtype size_t is unsigned_long; -- /usr/lib/gcc/x86_64-linux-gnu/6/include/stddef.h:216
subtype wchar_t is int; -- /usr/lib/gcc/x86_64-linux-gnu/6/include/stddef.h:328
end stddef_h;
Because it is a low level binding; we want to hide it (and implement RAII etc) behind a simpler and more usable high level binding, so ... (below)
with Ada.Finalization; use Ada.Finalization;
private with hidapi_hidapi_h;
private with System;
package hidapi is
type id is new natural range 0 .. 2**16 - 1;
type hid_device is new Limited_Controlled with private;
-- find first matching devices by enumeration : the RA part of RAII.
function enumerate (vendor_id, product_id : id) return hid_device;
-- accessors for device characteristics on enumerated device
function Serial_No (D : hid_device) return Wide_String;
function Product_String (D : hid_device) return Wide_String;
private
type hid_device is new Limited_Controlled with record
member : access hidapi_hidapi_h.hid_device_info;
addr : System.Address;
end record;
end hidapi;
and its implementation, containing the problem function value to return a Wide_String.
with hidapi_hidapi_h;
with Interfaces.C; use Interfaces.C;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Conversion;
with stddef_h;
package body hidapi is
function enumerate (vendor_id, product_id : id) return hid_device is
use hidapi_hidapi_h;
first : access hid_device_info;
begin
first := hid_enumerate(unsigned_short(vendor_id), unsigned_short(product_id));
if first /= null then
return H : hid_device do
H.member := first;
H.addr := System.Null_Address;
end return;
else raise Program_Error;
end if;
end enumerate;
-- helper function to deal with wchar_t * to wide_string
function Value (P : access stddef_h.wchar_t) return Wide_String is
temp : Wide_String(1 .. 256);
count : natural := 0;
type sd_wchar_ptr is access all stddef_h.wchar_t;
type wchar_array_ptr is access wchar_array;
Function To_Wchar_Array_Ptr is new Ada.Unchecked_Conversion(sd_wchar_ptr, wchar_array_ptr);
WCP : wchar_array_ptr := To_Wchar_Array_Ptr(sd_wchar_ptr(P));
begin
Put_Line("Wide string");
To_Ada(WCP.all, temp, count);
Put_Line("Wide string length " & natural'image(count));
return temp(1..count);
end Value;
function Serial_No (D : hid_device) return Wide_String is
use hidapi_hidapi_h;
begin
return Value(D.member.serial_number);
end Serial_No;
function Product_String (D : hid_device) return Wide_String is
use hidapi_hidapi_h;
begin
return Value(D.member.product_string);
end Product_String;
end hidapi;
And of course a test case to exercise it...
with Hidapi;
with Ada.Wide_Text_IO;
procedure Test_Hid is
usbrelay_vendor_id : constant Hidapi.id := 16#16c0#;
usbrelay_product_id : constant Hidapi.id := 16#05df#;
Device : Hidapi.hid_device := Hidapi.Enumerate(usbrelay_vendor_id, usbrelay_product_id);
begin
Ada.Wide_Text_IO.Put_Line("Serial : " & Device.Serial_No);
Ada.Wide_Text_IO.Put_Line("Product : " & Device.Product_String);
end Test_Hid;
One answer, slavishly copying the approach in the package body for Tnterfaces.C.Strings with necessary changes.
The naughty stuff is in functions "+" and Peek which use Unchecked Conversions on pointers,
to permit address arithmetic. Not pointer increment, but pointer+offset. One change is that the offset has to be scaled for 4 byte characters. I haven't set that scaling in a portable manner, but note that "+" will overload for each different return type so that offsets will be scaled appropriately for different named access types.
to allow the stddef_h.wchar_t to be viewed as a Wide_Wide_Character in the absence of any type conversion function. Whether the representation is correct is another matter (here, it is) but this technique could also be used to fake the input type of a suitable conversion function like To_Ada in Interfaces.C.
The remainder is straightforward character by character handling. One other change (so far) is to return Wide_Wide_Character rather than Wide_Character (because as the stddef_h package above reveals, the stored characters are 32 bit, same size as Interfaces.C.int. I'm happy to change my interface, but Wide_String could be easily handled by Ada.Strings packages.
type sd_wchar_ptr is access all stddef_h.wchar_t;
type w_w_char_ptr is access all char32_t;
-- Two Unchecked_Conversions to allow pointer arithmetic
-- And a third to allow the resulting storage to be interpreted as Wide_Wide_Char
function To_Sd_wchar_ptr is new Ada.Unchecked_Conversion (System.Address, sd_wchar_ptr);
function To_Address is new Ada.Unchecked_Conversion (sd_wchar_ptr, System.Address);
function To_Wchar_Ptr is new Ada.Unchecked_Conversion (sd_wchar_ptr, w_w_char_ptr);
-- pointer + offset arithmetic, with offset scaled for size of stddef_h.wchar_t;
-- TODO: attempted better way of computing word size ran into type errors
function "+" (Left : sd_wchar_ptr; Right : size_t) return sd_wchar_ptr is
begin
return To_Sd_wchar_ptr (To_Address (Left) + Storage_Offset (Right) * 4);
end "+";
function Peek (From : sd_wchar_ptr) return char32_t is
begin
return To_Wchar_Ptr(From).all;
end Peek;
function Strlen (Item : sd_wchar_ptr) return size_t is
Item_Index : size_t := 0;
begin
if Item = Null then
raise Program_Error;
end if;
loop
if Peek (Item + Item_Index) = char32_nul then
return Item_Index;
end if;
Item_Index := Item_Index + 1;
end loop;
end Strlen;
function Value (Item : sd_wchar_ptr) return char32_array is
Result : char32_array (0 .. Strlen (Item));
begin
if Item = Null then
raise Program_Error;
end if;
Put_Line("String length " & size_t'image(Strlen(Item)));
-- Note that the following loop will also copy the terminating Nul
for J in Result'Range loop
Result (J) := Peek (Item + J);
end loop;
return Result;
end Value;
-- helper function to deal with wchar_t * to wide_wide_string
function Value (Item : access stddef_h.wchar_t) return Wide_Wide_String is
begin
return To_Ada (Value (sd_wchar_ptr(Item)));
end Value;
I'm new to programming and just started with Qtada 3.2, so please be indulgent :D
My goal is to drag a picture (png) from one table view model to another.
While the image is already shown during drag in the minimal example below, I guess overriding mouse press and mouse release events should be the solution?
Is there anything else I need to do, like create drop action, mime data or item delegate?
Although I had a look at the QtAda tutorials, I still don't know how to correctly setup overriding events for my table views.
Really tried a lot, but nothing worked. Right now I'm stuck and confused.
It would be nice if somebody could point me to the right direction, or even better, show me a working example!
Bonus question #1: Is there a better / more correct way to load an image into a item model?
Bonus question #2: How to get rid of the line edits in the table? I just need the image to be shown in a cell, nothing else.
Thanks in advance!
Michael
My example:
main.adb
with Qt4.Core_Applications;
with Qt4.Objects;
with Qt4.Push_Buttons.Constructors;
with Qt4.Splitters.Constructors;
with Qt4.Splitters;
with Qt4.Strings;
with Qt_Ada.Application;
with Table_View_Big;
with Table_View_Small;
procedure Main is
begin
Qt_Ada.Application.Initialize;
declare
Quit : constant not null access Qt4.Push_Buttons.Q_Push_Button'Class
:= Qt4.Push_Buttons.Constructors.Create
(Qt4.Strings.From_Utf_16 ("Quit"));
Splitter : constant not null Qt4.Splitters.Q_Splitter_Access
:= Qt4.Splitters.Constructors.Create(The_Orientation => Qt4.Vertical);
Big_Table : constant not null Table_View_Big.Big_Table_Access
:= Table_View_Big.Constructors.Create;
Small_Table : constant not null Table_View_Small.Small_Table_Access
:= Table_View_Small.Constructors.Create;
begin
Splitter.Add_Widget(Big_Table);
Splitter.Add_Widget(Small_Table);
Splitter.Add_Widget(Quit);
Qt4.Objects.Connect (Quit,
Qt4.Signal ("clicked()"),
Qt4.Core_Applications.Instance,
Qt4.Slot ("quit()"));
Splitter.Set_Fixed_Size(640,480);
Splitter.Show;
Qt_Ada.Application.Execute;
end;
end Main;
table_view_small.ads :
with Qt4.Table_Views.Constructors;
with Qt4.Table_Views.Directors;
with Qt4.Abstract_Item_Models;
--with Qt4.Mouse_Events;
package Table_View_Small is
type Small_Table is limited new Qt4.Table_Views.Q_Table_View with private;
type Small_Table_Access is access all Small_Table'Class;
package Constructors is
function Create return not null Small_Table_Access;
end Constructors;
private
type Small_Table is new Qt4.Table_Views.Directors.Q_Table_View_Director with record
Small_Table_Item_Model : Qt4.Abstract_Item_Models.Q_Abstract_Item_Model_Access;
end record;
-- overriding procedure Mouse_Press_Event
-- (Self : not null access Table_Small;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
-- overriding procedure Mouse_Move_Event
-- (Self : not null access Table_Small;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
-- overriding procedure Mouse_Release_Event
-- (Self : not null access Table_Small;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
end Table_View_Small;
table_view_small.adb
with Qt4.Icons;
with Qt4.Model_Indices;
with Qt4.Standard_Item_Models.Constructors;
with Qt4.Strings;
with Qt4.Variants;
with Table_View_Small.MOC;
pragma Warnings (Off, Table_View_Small.MOC);
package body Table_View_Small is
use Qt4;
package body Constructors is
function Create return not null Small_Table_Access is
Self : constant Table_View_Small.Small_Table_Access := new Table_View_Small.Small_Table;
begin
Qt4.Table_Views.Directors.Constructors.Initialize (Self);
declare
Icon : Qt4.Icons.Q_Icon;
Data_Role : Qt4.Item_Data_Role := qt4.Decoration_Role;
Index : Qt4.Model_Indices.Q_Model_Index;
begin
Self.Small_Table_Item_Model := Qt4.Abstract_Item_Models.Q_Abstract_Item_Model_Access
(Qt4.Standard_Item_Models.Constructors.Create (1, 6, Self));
Icon := Qt4.Icons.Create(Qt4.Strings.From_Ucs_4("anypng.png"));
Index := Self.Small_Table_Item_Model.Index(0,0);
Qt4.Abstract_Item_Models.Set_Data(Self.Small_Table_Item_Model,index,icon.To_Q_Variant,Data_Role);
Self.Set_Model(Self.Small_Table_Item_Model);
-- Drag & Drop
Self.Set_Accept_Drops(true);
Self.Set_Drag_Enabled(true);
Self.Set_Drop_Indicator_Shown(true);
-- Self.Set_Drag_Drop_Mode(Qt4.Abstract_Item_Views.Internal_Move);
return Self;
end;
end Create;
end Constructors;
-- overriding procedure Mouse_Press_Event
-- (Self : not null access Small_Table;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class)
-- is
-- null;
-- end Mouse_Press_Event;
end Table_View_Small;
table_view_big.ads
with Qt4.Table_Views.Constructors;
with Qt4.Table_Views.Directors;
with Qt4.Abstract_Item_Models;
package Table_View_Big is
type Big_Table is limited new Qt4.Table_Views.Q_Table_View with private;
type Big_Table_Access is access all Big_Table'Class;
package Constructors is
function Create return not null Big_Table_Access;
end Constructors;
private
type Big_Table is new Qt4.Table_Views.Directors.Q_Table_View_Director with record
Big_Table_Item_Model : Qt4.Abstract_Item_Models.Q_Abstract_Item_Model_Access;
end record;
-- overriding procedure Mouse_Press_Event
-- (Self : not null access Big_Table;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
--
-- overriding procedure Mouse_Move_Event
-- (Self : not null access Big_Table;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
-- overriding procedure Mouse_Release_Event
-- (Self : not null access Big_Table;
-- Event : not null access Qt4.Mouse_Events.Q_Mouse_Event'Class);
end Table_View_Big;
table_view_big.adb
with Qt4.Abstract_Item_Views;
with Qt4.Sizes;
with Qt4.Standard_Item_Models.Constructors;
with Table_View_Big.MOC;
pragma Warnings (Off, Table_View_Big.MOC);
package body Table_View_Big is
package body Constructors is
function Create return not null Big_Table_Access is
Self : constant Table_View_Big.Big_Table_Access := new Table_View_Big.Big_Table;
begin
Qt4.Table_Views.Directors.Constructors.Initialize (Self);
declare
begin
Self.Big_Table_Item_Model := Qt4.Abstract_Item_Models.Q_Abstract_Item_Model_Access
(Qt4.Standard_Item_Models.Constructors.Create (25, 25, Self));
Self.Set_Model(Self.Big_Table_Item_Model);
Self.Set_Selection_Mode(Qt4.Abstract_Item_Views.No_Selection);
-- Drag & Drop
Self.Set_Accept_Drops(true);
Self.Set_Drag_Enabled(false);
Self.Set_Drop_Indicator_Shown(true);
-- Self.Set_Drag_Drop_Mode (Qt4.Abstract_Item_Views.Drop_Only);
return Self;
end;
end Create;
end Constructors;
end Table_View_Big;
table_view_view_test_dd.gpr
with "qt_gui";
project Table_View_Test_DD is
type Build_Modes is ("Application", "Metadata");
Build_Mode : Build_Modes := external ("BUILD_MODE");
for Source_Dirs use (".", ".amoc");
case Build_Mode is
when "Application" =>
for Main use ("main.adb");
for Object_Dir use ".objs";
for Exec_Dir use ".";
when "Metadata" =>
for Languages use ("Amoc");
for Object_Dir use ".amoc";
for Source_Files use ("table_view_small.ads",
"table_view_big.ads");
end case;
package Compiler is
for Default_Switches ("Ada") use ("-g", "-gnat05");
end Compiler;
package IDE is
for QtAda_Amoc_Invocation_Switch use "-XBUILD_MODE=Metadata";
end IDE;
-- for Languages use ("Ada");
package Naming is
for Casing use "lowercase";
end Naming;
end Table_View_Test_DD;
I am attempting to understand how to fix this circular dependency. All the examples I can find online suggest using a limited with, but then they demonstrate the use with two basic types, whereas this is a bit more advanced. The circular dependency is between the two files below. I thought it was between package Chessboard ... and the Piece type, but now I am not so sure. Attempting to put the package Chessboard ... line within chess_types.ads after the Piece type is declared and removing the use and with of Chessboard results in an error: this primitive operation is declared too late for the Move procedure. I am stuck on how to get out of this dependency. Any help would be much appreciated!
Thank you
chessboard.ads:
with Ada.Containers.Indefinite_Vectors;
use Ada.Containers;
with Chess_Types;
use Chess_Types;
package Chessboard is new Indefinite_Vectors(Board_Index, Piece'Class);
chess_types.ads:
with Ada.Containers.Indefinite_Vectors;
use Ada.Containers;
with Chessboard;
use Chessboard;
package Chess_Types is
subtype Board_Index is Integer range 1 .. 64;
type Color is (Black, White);
type Piece is tagged
record
Name : String (1 .. 3) := " ";
Alive : Boolean := False;
Team : Color;
Coordinate : Integer;
end record;
procedure Move_Piece(Board: in Vector; P: in Piece; Move_To: in Integer);
end Chess_Types;
More Code for question in comments:
Chess_Types.Piece_Types.ads:
package Chess_Types.Piece_Types is
type Pawn is new Piece with
record
First_Move : Boolean := True;
end record;
overriding
procedure Move_Piece(Board: in CB_Vector'Class; Po: in Pawn; Move_To: in Board_Index);
-- Other piece types declared here
end Chess_Types.Piece_Types;
Chess_Types.Piece_Types.adb:
with Ada.Text_IO;
use Ada.Text_IO;
package body Chess_Types.Piece_Types is
procedure Move_Piece(Board: in CB_Vector'Class; Po: in Pawn; Move_To: in Board_Index) is
Index_From, Index_To : Board_Index;
Move_From : Board_Index := Po.Coordinate;
begin
-- Obtain locations of Pawn to move from (Index_From) and to (Index_To)
-- in terms of the single dimension vector
for I in Board.First_Index .. Board.Last_Index loop
if Board.Element(I).Coordinate = Move_From then
Index_From := I;
end if;
if Board.Element(I).Coordinate = Move_To then
Index_To := I;
end if;
end loop;
-- Determine if the requested move is legal, and if so, do the move.
-- More possibilties to be entered, very primitive for simple checking.
if Move_To - Move_From = 2 and then Po.First_Move = True then
Board.Swap(I => Index_From, J => Index_To); -- "actual for "Container" must be a variable"
Board.Element(Index_From).First_Move := False; -- "no selector for "First_Move" for type "Piece'Class"
elsif Move_To - Po.Coordinate = 1 then
Board.Swap(Index_From, Index_To); -- "actual for "Container" must be a variable"
end if;
-- Test to make sure we are in the right Move_Piece procedure
Put_Line("1");
end Move_Piece;
-- Other piece type move_piece procedures defined here
end Chess_types.Piece_Types;
As a note to understand further, the Coordinate component of each piece correspond to ICCF numeric notation, which is two digits, so there needs to be some type of conversion between the vector and the ICCF notation, hence the reason for the whole for loop at the start.
This is a tough one. It looks like limited with and generics don't play nice together. The only way to make it work is to go back to using your own access type:
with Ada.Containers.Vectors;
use Ada.Containers;
limited with Chess_Types;
use Chess_Types;
package Chessboard_Package is
subtype Board_Index is Integer range 1 .. 64;
type Piece_Acc is access all Piece'Class;
package Chessboard is new Vectors(Board_Index, Piece_Acc);
end Chessboard_Package;
I had to put the instantiation into a new package, and move the Board_Index there too. Also, I changed it to Vectors since Piece_Acc is a definite type and there's no point in using Indefinite_Vectors. But in any event, this defeats the purpose. I'm just not sure Ada gives you a way to do what you want with two packages like this.
Even doing it in one package is not easy:
with Ada.Containers.Indefinite_Vectors;
use Ada.Containers;
package Chess_Types is
subtype Board_Index is Integer range 1 .. 64;
type Color is (Black, White);
type Piece is tagged record ... end record;
type CB_Vector is tagged;
procedure Move_Piece (Board : in CB_Vector'Class;
P : in Piece;
Move_To : in Board_Index);
package Chessboard is new Indefinite_Vectors(Board_Index, Piece'Class);
type CB_Vector is new Chessboard.Vector with null record;
end Chess_Types;
This compiles, but I had to add extra stuff to get around some of the language rules (in particular, when you instantiate a generic, that "freezes" all prior tagged types so that you can no longer declare a new primitive operation of the type); also, I had to make the Board parameter a classwide type to avoid running into the rule about primitive operations of multiple tagged types.
As I understand it, this will do what you want.
with Ada.Containers.Indefinite_Vectors;
use Ada.Containers;
package Chess_Types is
subtype Board_Index is Integer range 1 .. 64;
type Color is (Black, White);
type Piece is abstract tagged
record
Name : String (1 .. 3) := " ";
Alive : Boolean := False;
Team : Color;
Coordinate : Board_Index;
end record;
type Piece_Ptr is access all Piece'Class;
package Chessboard is new Indefinite_Vectors(Board_Index, Piece_Ptr);
procedure Move_Piece (Board : in Chessboard.Vector;
P : in Piece'Class;
Move_To : in Board_Index) is abstract;
end Chess_Types;
NOTES:
Piece is now abstract, as is the Move_Piece method. This will mean you now need to derive your other piece types (package piece_type-rook.ads, with a move_piece method for rook) etc...
Chessboard now contains pointers (Class wide pointers), beware allocating, deallocating, deep copy, shallow copy issues when using it.
You should now be able to call Move_Piece on any dereference of a piece_ptr and have it dispatch to the correct method.
The Move_To parameter is now the same type as the Board_Index. (Coordinate also brought in line) -- this seems a bit clunky, perhaps rethink this. (Row & Column Indices defining a 2D array perhaps? --No need for Indefinite_Vectors)
To answer the second question in the comment:
To use First_Move, the procedure has to know that it's a Pawn. If the object is declared with type Piece'Class, you can't access components that are defined only for one of the derived types. (That's true in most OO languages.) This may indicate a flaw in your design; if you have a procedure that takes a Piece'Class as a parameter, but you want to do something that makes sense only for a Pawn, then maybe you should add another operation to your Piece that does a default action for most pieces (perhaps it does nothing) and then override it for Pawn. Other possibilities are to use a type conversion:
procedure Something (P : Piece'Class) is ...
if Pawn(P).First_Move then ...
which will raise an exception if P isn't a Pawn. If you want to test first, you can say "if P in Pawn". I sometimes write code like:
if P in Pawn then
declare
P_Pawn : Pawn renames Pawn(P);
begin
if P_Pawn.First_Move then ...
end;
end if;
But defining a new polymorphic operation is preferable. (Note: I haven't tested the above code, hope I didn't make a syntax error somewhere.)
This is a followup to my question here.
I got to a point in the program where I felt like I couldn't proceed any further with the current structure, so I did a lot of rewriting. The Statement type is no longer abstract, and each subtype of Statement creates its own instance of Statement's variables. I also removed the abstract execute function from the Statements package because the compiler didn't like that (each subtype will still have its own execute method regardless). The execute function has been changed to a procedure because the incoming Statement type must be modified. I moved statementFactory (formerly createStatement) to the Statement package.
Here is the error I'm getting:
statements-compoundstatements.adb:15:29: expected type "CompoundStatement" defined at statements-compoundstatements.ads:11
statements-compoundstatements.adb:15:29: found type "Statement'Class" defined at statements.ads:6
I'm a beginner at Ada, but my hunch is that because the execute procedure is in CompoundStatements (which is a "subclass" of Statements) it would never be able to see the execute method of another "subclass" of Statements. The only solution I can think of would be to dump all the execute procedures that call an execute procedure into the Statement package, but that seems undesirable. But that still doesn't explain why stmt.all is being used as a type Statement'Class instead of the type created in statementFactory.
Here is the new code:
package Statements is
type Statement is tagged private;
type Statement_Access is access all Statement'Class;
ParserException : Exception;
procedure createStatement(tokens : Vector; S : out Statement);
procedure statementFactory(S: in out Statement; stmt: out Statement_Access);
--.....A bunch of other procedures and functions.....
private
type Statement is tagged
record
tokens : Vector;
executedtokens : Vector;
end record;
end Statements;
procedure createStatement(tokens : Vector; S : out Statement) is
begin
S.tokens := tokens;
end createStatement;
procedure statementFactory(S: in out Statement; stmt: out Statement_Access) is
currenttoken : Unbounded_String;
C : CompoundStatement;
A : AssignmentStatement;
P : PrintStatement;
begin
currenttoken := getCurrentToken(S);
if currenttoken = "begin" then
createStatement(S.tokens, C);
stmt := new CompoundStatement;
stmt.all := Statement'Class(C);
elsif isVariable(To_String(currenttoken)) then
createStatement(S.tokens, A);
stmt := new AssignmentStatement;
stmt.all := Statement'Class(A);
elsif currenttoken = "print" then
createStatement(S.tokens, P);
stmt := new PrintStatement;
stmt.all := Statement'Class(P);
end statementFactory;
package body Statements.CompoundStatements is
procedure execute(skip: in Boolean; C: in out CompoundStatement; reset: out Integer) is
stmt: Statement_Access;
tokensexecuted: Integer;
currenttoken : Unbounded_String;
begin
match(C, "begin");
currenttoken := getCurrentToken(C);
while(currenttoken /= "end") loop
statementFactory(C, stmt);
execute(skip, stmt.all, tokensexecuted); //ERROR OCCURS HERE
You say “I also removed the abstract execute function from the Statements package because the compiler didn't like that”; but you really need it, because otherwise how is the compiler to know that when you call execute (skip, stmt.all, tokensexecuted) that any stmt.all will provide an execute for it to dispatch to?
Not only does the extended type inherit the attributes of its parent type (so each CompoundStatement etc already has tokens and executedtokens), it inherits the primitive operations of the parent; if the parent operation is abstract, the child must provide its own implementation, if not, the child can provide its own (overriding) implementation.
See the Ada 95 Rationale and Wikibooks for good discussions on this.