Generally speaking, Ada will raise a Constraint_Error if you attempt to dereference a null pointer (access type). However, this behavior is disabled if, for example, you have pragma Suppress (all_checks) in use.
Given this scenario, how would one go about checking to see if the access type points to 0x0 (null)?
Consider the following:
type My_Access_Type is access all My_Type'Class;
procedure myProcedure ( myAccess : in My_Access_Type ) is
begin
-- need to have check in here
end
if myAccess = null then
...
end if;
Although it won't necessarily point to 0x0. Access types are not pointers, and may be implemented in a different way than plain addresses.
Another option, that can help you, is to declare the pointer as "not null".
type My_Access is not null access My_Type;
this prevents the declaration of not initialized My_Access types.
X : My_Access; -- compile error
This solution as some disadvantages (see https://en.wikibooks.org/wiki/Ada_Programming/Types/access#Null_exclusions) and its correct usage depends on your needs.
Related
I came across this piece of code from my assignment:
procedure Refs is
type Node is
record
Content : Integer;
Name : Character;
end record;
type XNode is access Node;
type NodeArray is array (Positive range 1 .. 5) of XNode;
[...]
And I cant seem to grok it (to the point that I could explain it to my grandmother) even after reading documentation, wiki etc.
Can someone explain in simple terms what that access keyword means ?
I know nothing about Ada, but thankfully the answer is just 3 seconds of Googling away: XNode is an access type for Node. An access type is a type which grants access to dynamically allocated values of another type.
In other words, it is a pointer. But don't confuse this with the C concept of a pointer. Ada pointers are pointer-safe and memory-safe, you cannot, for example, add 1 to it and have it point to a different piece of memory, or have it point to some random address and claim "this memory is now a Node" (aka type casting).
It is more like an object reference in Java, ECMAScript, Python, or Ruby.
What is the proper way to convert an access to constant to access to
variable? (Yes, I know that then I need to be careful not to modify this
"variable".)
Maybe Unchecked_Conversion?
But:
Is Unchecked_Conversion guaranteed by the standard to work well in this
case?
Is there a simpler way than Unchecked_Conversion?
Unchecked_Conversion is not guaranteed to work in that case, as Ada doesn't give you any guarantees about the memory layout of access types. Supposedly Ada intentionally allows the compiler to have unique memory layouts and meanings for each access type.
If you add a Convention => C aspect to your access types, you may get the common memory layout you want, as C considers all pointers to operate on the same address space.
Ok, what's going on is [likely] that there's a mismatch in parameter modes (and maybe type-definitions) somewhere.
Let's say there's a few types:
Type Window(<>) is tagged private;
Type Constant_Reference is not null constant access Window'Class;
Type Reference is not null access Window'Class;
Type Handle is access Window'Class;
Given Function Get_Handle ( Object : in out aliased Window'Class ) what can we say about Object'Access? Well, it's an access to Window'Class which is not constant -- so it is compatible with both Handle and Reference but not Constant_Reference.
On the other hand, if we had Function Get_Handle ( Object : aliased Window'Class ) then Object'Access would yield an anonymous access which is compatible with Constant_Reference due to the constant nature of the in-parameter.
So, check your parameters and your design, and see if that's what's causing your issues.
I have a specification of a function that acts like a constructor. The specification of the function is
function Create_Controller return Type_Controller;
Also, in the specification file, I have the Type_Controller type, which is an access. I copy the relevant fragment:
type Type_Controller_Implementation;
type Type_Controller is access Type_Controller_Implementation;
So, this is what I've attempted:
function Create_Controller return Type_Controller
is
My_Controller : aliased Type_Controller_Implementation;
begin
return My_Controller'Access;
end Create_Controller;
I tried to compile the program without the aliased keyword, but then, the compiler says:
prefix of "Access" attribute must be aliased
So, I put the aliased keyword and the compiler now suggests that I should change the specification:
result must be general access type
add "all" to type "Controlador_De_Impresion" defined at controller.ads
The problem is that I'm not allowed to change the specification. I've read the chapter about access types in the Ada Programming Wikibook, but I still don't understand why my code doesn't work. What am I doing wrong?
The implementation of the Create_Controller function body is incorrect. Were it to work as coded, you'd be returning a pointer to a variable local to that function body's scope...which would be immediately lost upon returning from the function, leaving you with an invalid pointer.
No, an instance of the type needs to be allocated and returned. If there's no explicit initialization that needs to occur you can simply do:
return new Type_Controller_Implementation;
If there is some initialization/construction that has to occur, then:
function Create_Controller return Type_Controller
is
My_Controller : Type_Controller := new Type_Controller_Implementation;
begin
-- Set fields of My_Controller
...
return My_Controller;
end Create_Controller;
When you declare an access type as access T, you're saying that "this is a pointer to a T and it must point to objects of type T allocated from a pool". (That is, allocated with a new keyword.) When you declare an access type as access all T, you're saying that it can point either to a T allocated from a pool, or to an aliased variable of type T.
If the type is declared as access T and you can't change it, then all access values of the type have to point to something allocated with new. You can't make it point to a variable (even to a "global" variable that isn't located on the stack).
The reasons for this are historical, I think. The first version of Ada (Ada 83) only had "pool-specific types." You couldn't make an access value point to some other variable at all, without trickery. This meant that a compiler could implement access values as indexes into some storage block, or as some other value, instead of making them the actual address of an object. This could save space (an access value could be smaller than an address) or allow more flexibility in how pool memory was managed. Allowing access values to point directly to objects takes away some of that flexibility. I think that's why they decided to keep the old meaning, for backward compatibility, and require an all keyword to indicate the new kind of access.
Im trying to use interfaces to call different subroutines with different types, however, it doesnt seem to work when i use the pointer attribute. for example, take this sample code
MODULE ptr_types
TYPE, abstract :: parent
INTEGER :: q
END TYPE
TYPE, extends(parent) :: child
INTEGER :: m
END TYPE
INTERFACE ptr_interface
MODULE PROCEDURE do_something
END INTERFACE
CONTAINS
SUBROUTINE do_something(atype)
CLASS(parent), POINTER :: atype
! code determines that this allocation is correct from input
ALLOCATE(child::atype)
WRITE (*,*) atype%q
END SUBROUTINE
END MODULE
PROGRAM testpass
USE ptr_types
CLASS(child), POINTER :: ctype
CALL ptr_interface(ctype)
END PROGRAM
This gives error
Error: There is no specific subroutine for the generic 'ptr_interface' at (1)
however if i remove the pointer attribute in the subroutine it compiles fine. Now, normally this wouldnt be a problem, but for my use case i need to be able to treat that argument as a pointer, mainly so i can allocate it if necessary.
Any suggestions? Mind you I'm new to fortran so I may have missed something
edit: forgot to put the allocation in the parents subroutine, the initial input is unallocated
EDIT 2
this is my second attempt, with caller side casting
MODULE ptr_types
TYPE, abstract :: parent
INTEGER :: q
END TYPE
TYPE, extends(parent) :: child
INTEGER :: m
END TYPE
TYPE, extends(parent) :: second
INTEGER :: meow
END TYPE
CONTAINS
SUBROUTINE do_something(this, type_num)
CLASS(parent), POINTER :: this
INTEGER type_num
IF (type_num == 0) THEN
ALLOCATE (child::this)
ELSE IF (type_num == 1) THEN
ALLOCATE (second::this)
ENDIF
END SUBROUTINE
END MODULE
PROGRAM testpass
USE ptr_types
CLASS(child), POINTER :: ctype
SELECT TYPE(ctype)
CLASS is (parent)
CALL do_something(ctype, 0)
END SELECT
WRITE (*,*) ctype%q
END PROGRAM
however this still fails. in the select statement it complains that parent must extend child. Im sure this is due to restrictions when dealing with the pointer attribute, for type safety, however, im looking for a way to convert a pointer into its parent type for generic allocation. Rather than have to write separate allocation functions for every type and hope they dont collide in an interface or something.
hopefully this example will illustrate a little more clearly what im trying to achieve, if you know a better way let me know
As indicated by High Performance Mark, you have a mismatch in the declared type of the actual and dummy arguments for the call to ptr_interface. This isn't permitted if the dummy argument has the pointer or allocatable attribute - see 12.5.2.5p2 of F2008.
There's a simple rationale for this restriction (which is discussed in Note 12.27 in the F2008 standard) - without it it would be possible for the subroutine to allocate the dummy argument to be of a type that is incompatible with the actual argument. For example - imagine if there was another extension of Parent in the program somewhere - a sibling of Child in the type heirarchy. If your do_something procedure allocate its dummy argument to that sibling type, then back in the the calling scope you have something declared as type Child that is actually some other incompatible (not an extension of Child) type.
If the do_something procedure cannot allocate the thing to anything other than type Child, then make its dummy argument of type Child. If it can allocate it to some other type that is an extension of Parent, then you need to make the declared type of the actual argument type Parent as well. You can use the SELECT TYPE construct to then downcast to an object of Child type in the calling scope.
Subsequent to your edits, my suggestion was for your main program to look something like:
PROGRAM testpass
USE ptr_types
IMPLICIT NONE ! <--
CLASS(Parent), POINTER :: ctype
!***
! ctype here is a pointer with undefined association status,
! (so no concept of dynamic type) and declared type Parent.
CALL do_something(ctype, 0)
! Assuming successful ALLOCATE(Child :: xxx) in the procedure,
! ctype here is an associated pointer with dynamic type Child.
SELECT TYPE(ctype)
CLASS is (Child)
! Declared type of ctype in here is Child. Dynamic type
! in this specific case is also Child, but this block would
! also be executed if the dynamic type was a further extension
! of Child, because a CLASS IS guard was used. (A TYPE IS
! guard requires an exact match of dynamic type.)
!
! If the allocate in do_something allocated the dummy argument
! to be of type second or nullified the argument, then this
! block of code would not be executed. If do_something left
! the association status of the pointer undefined, then
! your program is non-conforming, and anything could happen.
WRITE (*,*) ctype%m
! Continue to work with ctype as a thing with declared type
! Child inside this block of the select type construct.
END SELECT
! ctype back to having a declared type of Parent.
WRITE (*,*) ctype%q
! Don't forget deallocation!
END PROGRAM
If I change your line
CLASS(child), POINTER :: ctype
to
CLASS(parent), POINTER :: ctype
then your program compiles and executes. I'm quite new to all this object-oriented Fortran myself so I struggle to point to the clause in the standard which states the rules for rank-type-kind matching in this case and clarifies your mistake. Your mistake may simply be to use a compiler which doesn't implement the latest features of the language. On the other hand, perhaps my compiler (Intel Fortran 13.1) implements the latest features as incorrectly as yours does.
(On past form a guy named IanH here on SO will pass by later and clarify.)
One thing I have learned though, is that if your compiler is Fortran 2003 compliant (enough) then making variables ALLOCATABLE rather than POINTER makes a number of operations easier and passes the responsibility for freeing unwanted memory to the compiler. You don't need pointers for dynamic memory management in Fortran any more.
I think that your problem comes from the POINTER attribute of the argument in the subroutine do_something. Delete it and all should work.
I am making a program in Ada for Data Structures and Algorithms class.
My current problem is the error 'actual for "this" must be a variable'
I did some looking around and I read about it being because of in out mode, but I'm not entirely grasping why it's happening to me I guess.
The Examples I seen made sense but I guess since it's my coding I'm just not seeing it?
Procedure AddUnmarked(g:Grid; this:Linked_List_Coord.List_Type; c:Cell) is
cNorth : Cell := getCell(g, North(c));
cEast : Cell := getCell(g, East(c));
cSouth : Cell := getCell(g, South(c));
cWest : Cell := getCell(g, West(c));
Begin
if CellExists(g, cNorth) and not cNorth.IsMarked then
Linked_List_Coord.Append(this, cNorth.Coords);
elsif CellExists(g, cEast) and not cEast.IsMarked then
Linked_List_Coord.Append(this, cEast.Coords);
elsif CellExists(g, cSouth) and not cSouth.IsMarked then
Linked_List_Coord.Append(this, cSouth.Coords);
elsif CellExists(g, cWest) and not cWest.IsMarked then
Linked_List_Coord.Append(this, cWest.Coords);
end if;
End AddUnmarked;
before "this" is passed to the function it is a Linked_List of my self defined type Coord (2 integers). It is initialized and has had a Coordinate pair added to it before the list is passed to the function above in my code.
What it means is that the list cannot be modified unless you are passing it as a modifiable parameter, that is, in out.
To elaborate, think of LIST_TYPE as being a handle to a tagged-type object; in order to ensure that LIST_TYPE is valid you need to pass it in via an in parameter (or create/manipulate a local object), but to pass out your results you need an out parameter.
So, in order to do your operations on an already-existing object {and get the results back} you need in out.
In Ada, subroutine parameters all have a usage mode associated with them. The available modes are in, out, and in out*. If you don't specify a mode, (like you didn't in your code), then it defaults to in only.
The modes specify what you can do with that parameter on the inside of the subprogram. If you want to read a value passed in from outside the routine, it must have in on it. If you want to write to the parameter (and/or possibly have it read outside the routine), then it must have out on it.
Since none of your parameters have out on them, you cannot write to any of them.
(* - There's another possible mode: access, but that's an advanced topic).