- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello,
Suppose I have the following class:
interface my_type
module procedure :: ctor
end interface
type :: my_type
byte, allocatable :: buff(:)
contains
final :: dtor
procedure :: putData
...
end type
subroutine dtor(this)
type(my_type) :: this
if (allocated(this.buff)) deallocate(this.buff)
end subroutine
function ctor() result(this)
type(my_type) :: this
allocate(this.buff(100))
! Fill buff with some initial data ...
end function
The subroutine putData puts data into buff (its size will grow if need be, I use move_alloc), but at the very beginning some initial data should be put into it. The main code:
type(my_type) :: a a = my_type() call a.putData(...)
It seems that using constructor in this way is not a good idea, because on assignment a = my_type() a copy of the allocated buffer is created and the original buffer (created in ctor) is destroyed. I see two immediate solutions: 1) introduce the special subroutine for initialization, 2) perform initialization on the first call to putData (using e.g. the logical flag isInitialized). Both seem somewhat deficient: the first one introduces additional subroutine that one should not forget to call after ctor, and the second one complicates the code for I have many putData's for different data types (it is a generic name).
Is it possible to devise something better? Thanks.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Note your finalizer does nothing useful! Allocatable subobjects of an entity being finalized are going to be deallocated automatically anyway.
I don't think Fortran 2003 compiler optimisers are at this point yet, but note - because your type has a finalizer, the language requires the compiler to create a temporary for the function result of your ctor procedure, then requires that result to be assigned as per the assignment statement that reference the constructor, then requires the finalizer to be called on the function result (the thing being assigned to will also be finalized at some point in that sequence). However, if you didn't have a finalizer (or perhaps if all finalizers were pure), then a very smart compiler would know that there was no way you could tell, in a conforming program, that a temporary ever existed, and hence it could simply construct the function result directly in the variable on the left hand side of the assignment.
If the likely potential of a temporary bothers you, then I would just write a subroutine to do the construction. The function form of construction is handy if you are going to use the object in an expression (which generally implies more temporaries), but if all you are going to do is immediately assign the constructed object, then all the function form offers is a possibly less performant syntactic nicety without the flexibility of returning "I failed to construct" flags or similar.
Note that your derived type already has a "I am not initialized flag" - you can use the allocation status of the component.
(The component declaration with the `byte` type specifier and the use of a period as a part separator are not conforming Fortran. I very strongly recommend against using those.)
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Since putData may require the buffer to be resized, why would it complicate matters so much to introduce the logic to do the initialisation there?
You could for instance do:
subroutine putData( a, data ) call a%ensureCapacity( the_calculated_minimum_size ) ... end subroutine putData
That way you put the requirement for making sure that the buffer exists and is large enough in a separate routine. The only extra bit is calling it in every version of putData, but it is only one line and you will that logic anyway.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The problem is not in the buffer resizing, but with its initialization: prior to any putData call, some initial data (header) should be placed into it. I want this to be done in the constructor, but at same time I want to avoid buffer copying during the "a = my_type()" assignment.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You might take advantage of the defined assignment feature - overload the assignment by a routine of your own. That way you can put the new buffer directly in the left-hand side via move_alloc. Something along these lines:
subroutine assign_my_type( left, right)
type(my_type), intent(inout) :: left
type(my_type), intent(in) :: right
if ( .not. associated( left%buff) ) then
call move_alloc( right%buff, left%buff )
endif
... other stuff ...
end subroutine assign_my_type
You may introduce a flag to make sure that the right-hand side is coming from the constructor function.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Note your finalizer does nothing useful! Allocatable subobjects of an entity being finalized are going to be deallocated automatically anyway.
I don't think Fortran 2003 compiler optimisers are at this point yet, but note - because your type has a finalizer, the language requires the compiler to create a temporary for the function result of your ctor procedure, then requires that result to be assigned as per the assignment statement that reference the constructor, then requires the finalizer to be called on the function result (the thing being assigned to will also be finalized at some point in that sequence). However, if you didn't have a finalizer (or perhaps if all finalizers were pure), then a very smart compiler would know that there was no way you could tell, in a conforming program, that a temporary ever existed, and hence it could simply construct the function result directly in the variable on the left hand side of the assignment.
If the likely potential of a temporary bothers you, then I would just write a subroutine to do the construction. The function form of construction is handy if you are going to use the object in an expression (which generally implies more temporaries), but if all you are going to do is immediately assign the constructed object, then all the function form offers is a possibly less performant syntactic nicety without the flexibility of returning "I failed to construct" flags or similar.
Note that your derived type already has a "I am not initialized flag" - you can use the allocation status of the component.
(The component declaration with the `byte` type specifier and the use of a period as a part separator are not conforming Fortran. I very strongly recommend against using those.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Now everything seems to be clear. Thank you very much for the explanation.
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page