I am trying to create a package with a singleton variable that is auto initialized. If the singleton variable is not a controlled type, then the compiler doesn't complain, but when I make it controlled, I get the warning:"cannot call Initialize before body seen"
Followed by: "Program error will be raised at run time."
So, I think the compiler wants the singleton variable to be hidden from child objects, but this is not what I want. Here's my code:
with Ada.Finalization;
package static_member is
type singleton_type is tagged limited private;
private
type singleton_type is new Ada.Finalization.Limited_Controlled with record
data_to_init: Natural;
end record;
overriding procedure Initialize(data: in out singleton_type);
overriding procedure Finalize(data: in out singleton_type);
--This is where I get the warnings.
--I want singleton to be visible to child objects.
singleton: singleton_type;
end static_member;
Then the package body is:
package body static_member is
overriding procedure Initialize(data: in out singleton_type) is
begin
data.data_to_init := 0;
end Initialize;
overriding procedure Finalize(data: in out singleton_type) is null;
end static_member;
How would I create a singleton variable that is initialized, and ready to use by the time object instances begin to be created? Thanks in advance.
The answer to this question as posed is that - as the compiler said -
"cannot call Initialize before body seen"; that is, the place in the
program text where Initialize
is called must be after the place in
the program text where the body of Initialize
is found. This is a
(maybe historical) consequence of a desire for one-pass
compilers.
Because the body of Initialize
has to appear in the body of the
package (unless it's null, which is legal in Ada 2012), this means
that singleton
can only be declared in the package body (after the
body of Initialize
, of course).
This means that you need to provide access subprograms for use by child packages: possibly
with Ada.Finalization;
package Static_Member with Elaborate_Body is
type Singleton_Type is tagged limited private;
function Singleton return access Singleton_Type;
private
type Singleton_Type is new Ada.Finalization.Limited_Controlled with record
Data_To_Init: Natural;
end record;
overriding procedure Initialize (Data: in out Singleton_Type);
overriding procedure Finalize (Data: in out Singleton_Type);
end Static_Member;
package body Static_Member is
overriding procedure Initialize (Data: in out Singleton_Type) is
begin
Data.Data_To_Init := 0;
end Initialize;
overriding procedure Finalize (Data: in out Singleton_Type) is null;
The_Singleton: aliased Singleton_Type;
function Singleton return access Singleton_Type is (The_Singleton'Access);
end Static_Member;
The reason for the Elaborate_Body
is that, without it, the compiler
doesn't have to elaborate the package body before other, using,
packages that call package subprograms (e.g. Singleton
): so
The_Singleton: aliased Singleton_Type;
with the implicit call to Initialize
might not have happened (which
would lead to a Program_Error
with a different cause, known in the
trade as "access before elaboration" or ABE).
That said, there are other approaches, which avoid the use of
Ada.Finalization
. Your sample code doesn't show any particular
reason to use it, and indeed there's no point in overriding Finalize
(unless you want something to happen at program shutdown?)
One possibility (for a simple case) would be to declare
Singleton_Type
with initialization:
type Singleton_Type is record
Data_To_Init : Natural := 0;
end record;
An alternate would be to do the initialization in the package body:
package Static_Member with Elaborate_Body is
type Singleton_Type is limited private;
function Singleton return access Singleton_Type;
private
type Singleton_Type is limited record
Data_To_Init : Natural;
end record;
end Static_Member;
package body Static_Member is
The_Singleton: aliased Singleton_Type;
function Singleton return access Singleton_Type is (The_Singleton'Access);
begin
The_Singleton.Data_To_Init := 0;
end Static_Member;