- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hello,
I have a question regarding standard compliance of a specific use of a procedure pointer.
In the following code, I use a procedure pointer that "almost", but not fully matches the interface with which the pointer was declared. Almost because one actual argument of the procedure the pointer points is an extension of the type defined in the interface.
I was suprised that the compiler accepted the code and actually works as intended for method 1 (see sample code). Note that there is an error shown when directly assigning the procedure pointer without an extra subroutine.
I guess the standard compliant way would be what I have included as method 2. However, this incurs a significant runtime overhead vs. method 1.
Therefore I would like to know if you think that the method 1 is standard compliant or not.
Best regards,
Thomas
I have a question regarding standard compliance of a specific use of a procedure pointer.
In the following code, I use a procedure pointer that "almost", but not fully matches the interface with which the pointer was declared. Almost because one actual argument of the procedure the pointer points is an extension of the type defined in the interface.
I was suprised that the compiler accepted the code and actually works as intended for method 1 (see sample code). Note that there is an error shown when directly assigning the procedure pointer without an extra subroutine.
I guess the standard compliant way would be what I have included as method 2. However, this incurs a significant runtime overhead vs. method 1.
Therefore I would like to know if you think that the method 1 is standard compliant or not.
Best regards,
Thomas
[fortran]MODULE TestModule !> A base class TYPE tBase REAL(8) :: Value END TYPE ! An extenstion of that calss TYPE, EXTENDS (tBase) :: tBaseExt REAL(8) :: ValueExt END TYPE TYPE, EXTENDS (tBaseExt) :: tBaseExt2 REAL(8) :: ValueExt2 END TYPE TYPE, EXTENDS (tBaseExt2) :: tBaseExt3 REAL(8) :: ValueExt3 END TYPE ! A test class for procedure pointers TYPE :: tProcPtrTest PROCEDURE (IGet), POINTER, NOPASS :: Proc CLASS (tBase), POINTER :: MyClass CONTAINS PROCEDURE SetProcPointer PROCEDURE CallPrint END TYPE ! Interface for procedure pointer ABSTRACT INTERFACE SUBROUTINE IGet(base, result) IMPORT tBase CLASS (tBase) :: base REAL(8) :: result END SUBROUTINE END INTERFACE CONTAINS ! Print routine with a select type. SUBROUTINE Print(base, result) CLASS (tBase) base REAL(8) :: result SELECT TYPE (base) CLASS IS (tBaseExt) result = base%ValueExt END SELECT END SUBROUTINE ! Print routine without select type and directly using BaseExt SUBROUTINE PrintExt(BaseExt, result) CLASS (tBaseExt) BaseExt REAL(8) :: result result = BaseExt%ValueExt END SUBROUTINE SUBROUTINE SetProcPointer(this, Proc, ClassPtr) CLASS (tProcPtrTest) :: this PROCEDURE (IGet), POINTER, INTENT(IN) :: Proc CLASS (tBase), TARGET, INTENT(IN) :: ClassPtr this%Proc => Proc this%MyClass => ClassPtr END SUBROUTINE SUBROUTINE CallPrint(this, result) CLASS (tProcPtrTest) :: this REAL(8) :: result CALL this%Proc(this%MyClass, result) END SUBROUTINE END MODULE program ProcedurePointerTest USE TestModule implicit none TYPE (tProcPtrTest) :: procPtrTest TYPE (tBase) :: base TYPE (tBaseExt) :: baseExt REAL(8) :: result, sum, tStart, tEnd INTEGER :: i baseExt%ValueExt = 3 !! Method 1 ! Assigning a not fully matching procedure pointer works when using a subroutine CALL procPtrTest%SetProcPointer(PrintExt, baseExt) ! Note that assigning a not fully matching procedure pointer is not allowed directly. ! The following, if uncommented gives an error: ! procPtrTest%Proc => PrintExt ! procPtrTest%MyCmpnt => baseExt CALL CPU_TIME(tStart) sum = 0.0 DO I=1,1e8 CALL procPtrTest%CallPrint(result) sum = sum + result END DO CALL CPU_TIME(tEnd) WRITE (*,*) sum, tEnd-tStart ! Method 2 ! in this case, the interface really matches and a select type is used. CALL procPtrTest%SetProcPointer(Print, baseExt) CALL CPU_TIME(tStart) sum = 0.0 DO I=1,1e8 CALL procPtrTest%CallPrint(result) sum = sum + result END DO CALL CPU_TIME(tEnd) WRITE (*,*) sum, tEnd-tStart end program ProcedurePointerTest [/fortran]
1 Solution
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thomas,
Interesting questions. I had to spend some time with the program and the standard to decide on how to respond.
First, the second commented-out line has two errors: There is no component MyCmpnt in type tProcPtrTest, it should be MyClass, and baseExt needs the TARGET attribute in order to be allowed as the target of a pointer assignment. Neither of these are germane to your questions.
I believe that your first method is not standard-conforming and that the compiler should give an error for the procedure call, but as is often the case, one has to chase around the standard to see why.
12.5.2.9 (Actual arguments associated with dummy procedure entities) says:
"5 If a dummy argument is a procedure pointer [which in this case it is], the corresponding actual argument shall be a procedure pointer, a reference to the intrinsic function NULL, or a valid target for the dummy pointer in a pointer assignment statement."
It's this last clause that applies, so let's find the rule for that. This would be 7.2.2.4 (Procedure pointer assignment):
"3 If the pointer object has an explicit interface [it does], its characteristics shall be the same as the pointer target ... [rest does not apply]"
So are the characteristics of Proc (the procedure pointer dummy argument) and PrintExt (the procedure target) the same? Back to chapter 12, 12.3.1 (Characteristics of procedures):
"1 The characteristics of a procedure are the classification of the procedure as a function or subroutine, whether it is pure, whether it is elemental, whether it has the BIND attribute, the characteristics of its dummy arguments, and the characteristics of its result value if it is a function."
The only item in question here is the characteristics of the dummy arguments. So let's see:
Proc (the procedure dummy argument) is declared using the abstract interface Iget, which is:
SUBROUTINE IGet(base, result)
CLASS (tBase) :: base
REAL(8) :: result
END SUBROUTINE
PrintExt is declared as follows:
SUBROUTINE PrintExt(BaseExt, result)
CLASS (tBaseExt) BaseExt
REAL(8) :: result
END SUBROUTINE
Are the characteristics of the first argument the same? No - one is CLASS(tBase) and one is CLASS(tBaseExt). While these may be type compatible in one direction, they are not "the same", and therefore this is not allowed. The compiler makes the correct call when the pointer assignment is done directly, but misses it when checking the procedure arguments. I will let the developers know about that. Issue ID is DPD200235421.
What is "slow" about the second method is the need to figure out the SELECT TYPE in procedure Print - this generates an awful lot of code whereas PrintExt is just using the declared type and moves the value directly. Maybe this can be improved - I will ask.
Interesting questions. I had to spend some time with the program and the standard to decide on how to respond.
First, the second commented-out line has two errors: There is no component MyCmpnt in type tProcPtrTest, it should be MyClass, and baseExt needs the TARGET attribute in order to be allowed as the target of a pointer assignment. Neither of these are germane to your questions.
I believe that your first method is not standard-conforming and that the compiler should give an error for the procedure call, but as is often the case, one has to chase around the standard to see why.
12.5.2.9 (Actual arguments associated with dummy procedure entities) says:
"5 If a dummy argument is a procedure pointer [which in this case it is], the corresponding actual argument shall be a procedure pointer, a reference to the intrinsic function NULL, or a valid target for the dummy pointer in a pointer assignment statement."
It's this last clause that applies, so let's find the rule for that. This would be 7.2.2.4 (Procedure pointer assignment):
"3 If the pointer object has an explicit interface [it does], its characteristics shall be the same as the pointer target ... [rest does not apply]"
So are the characteristics of Proc (the procedure pointer dummy argument) and PrintExt (the procedure target) the same? Back to chapter 12, 12.3.1 (Characteristics of procedures):
"1 The characteristics of a procedure are the classification of the procedure as a function or subroutine, whether it is pure, whether it is elemental, whether it has the BIND attribute, the characteristics of its dummy arguments, and the characteristics of its result value if it is a function."
The only item in question here is the characteristics of the dummy arguments. So let's see:
Proc (the procedure dummy argument) is declared using the abstract interface Iget, which is:
SUBROUTINE IGet(base, result)
CLASS (tBase) :: base
REAL(8) :: result
END SUBROUTINE
PrintExt is declared as follows:
SUBROUTINE PrintExt(BaseExt, result)
CLASS (tBaseExt) BaseExt
REAL(8) :: result
END SUBROUTINE
Are the characteristics of the first argument the same? No - one is CLASS(tBase) and one is CLASS(tBaseExt). While these may be type compatible in one direction, they are not "the same", and therefore this is not allowed. The compiler makes the correct call when the pointer assignment is done directly, but misses it when checking the procedure arguments. I will let the developers know about that. Issue ID is DPD200235421.
What is "slow" about the second method is the need to figure out the SELECT TYPE in procedure Print - this generates an awful lot of code whereas PrintExt is just using the declared type and moves the value directly. Maybe this can be improved - I will ask.
Link Copied
3 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thomas,
Interesting questions. I had to spend some time with the program and the standard to decide on how to respond.
First, the second commented-out line has two errors: There is no component MyCmpnt in type tProcPtrTest, it should be MyClass, and baseExt needs the TARGET attribute in order to be allowed as the target of a pointer assignment. Neither of these are germane to your questions.
I believe that your first method is not standard-conforming and that the compiler should give an error for the procedure call, but as is often the case, one has to chase around the standard to see why.
12.5.2.9 (Actual arguments associated with dummy procedure entities) says:
"5 If a dummy argument is a procedure pointer [which in this case it is], the corresponding actual argument shall be a procedure pointer, a reference to the intrinsic function NULL, or a valid target for the dummy pointer in a pointer assignment statement."
It's this last clause that applies, so let's find the rule for that. This would be 7.2.2.4 (Procedure pointer assignment):
"3 If the pointer object has an explicit interface [it does], its characteristics shall be the same as the pointer target ... [rest does not apply]"
So are the characteristics of Proc (the procedure pointer dummy argument) and PrintExt (the procedure target) the same? Back to chapter 12, 12.3.1 (Characteristics of procedures):
"1 The characteristics of a procedure are the classification of the procedure as a function or subroutine, whether it is pure, whether it is elemental, whether it has the BIND attribute, the characteristics of its dummy arguments, and the characteristics of its result value if it is a function."
The only item in question here is the characteristics of the dummy arguments. So let's see:
Proc (the procedure dummy argument) is declared using the abstract interface Iget, which is:
SUBROUTINE IGet(base, result)
CLASS (tBase) :: base
REAL(8) :: result
END SUBROUTINE
PrintExt is declared as follows:
SUBROUTINE PrintExt(BaseExt, result)
CLASS (tBaseExt) BaseExt
REAL(8) :: result
END SUBROUTINE
Are the characteristics of the first argument the same? No - one is CLASS(tBase) and one is CLASS(tBaseExt). While these may be type compatible in one direction, they are not "the same", and therefore this is not allowed. The compiler makes the correct call when the pointer assignment is done directly, but misses it when checking the procedure arguments. I will let the developers know about that. Issue ID is DPD200235421.
What is "slow" about the second method is the need to figure out the SELECT TYPE in procedure Print - this generates an awful lot of code whereas PrintExt is just using the declared type and moves the value directly. Maybe this can be improved - I will ask.
Interesting questions. I had to spend some time with the program and the standard to decide on how to respond.
First, the second commented-out line has two errors: There is no component MyCmpnt in type tProcPtrTest, it should be MyClass, and baseExt needs the TARGET attribute in order to be allowed as the target of a pointer assignment. Neither of these are germane to your questions.
I believe that your first method is not standard-conforming and that the compiler should give an error for the procedure call, but as is often the case, one has to chase around the standard to see why.
12.5.2.9 (Actual arguments associated with dummy procedure entities) says:
"5 If a dummy argument is a procedure pointer [which in this case it is], the corresponding actual argument shall be a procedure pointer, a reference to the intrinsic function NULL, or a valid target for the dummy pointer in a pointer assignment statement."
It's this last clause that applies, so let's find the rule for that. This would be 7.2.2.4 (Procedure pointer assignment):
"3 If the pointer object has an explicit interface [it does], its characteristics shall be the same as the pointer target ... [rest does not apply]"
So are the characteristics of Proc (the procedure pointer dummy argument) and PrintExt (the procedure target) the same? Back to chapter 12, 12.3.1 (Characteristics of procedures):
"1 The characteristics of a procedure are the classification of the procedure as a function or subroutine, whether it is pure, whether it is elemental, whether it has the BIND attribute, the characteristics of its dummy arguments, and the characteristics of its result value if it is a function."
The only item in question here is the characteristics of the dummy arguments. So let's see:
Proc (the procedure dummy argument) is declared using the abstract interface Iget, which is:
SUBROUTINE IGet(base, result)
CLASS (tBase) :: base
REAL(8) :: result
END SUBROUTINE
PrintExt is declared as follows:
SUBROUTINE PrintExt(BaseExt, result)
CLASS (tBaseExt) BaseExt
REAL(8) :: result
END SUBROUTINE
Are the characteristics of the first argument the same? No - one is CLASS(tBase) and one is CLASS(tBaseExt). While these may be type compatible in one direction, they are not "the same", and therefore this is not allowed. The compiler makes the correct call when the pointer assignment is done directly, but misses it when checking the procedure arguments. I will let the developers know about that. Issue ID is DPD200235421.
What is "slow" about the second method is the need to figure out the SELECT TYPE in procedure Print - this generates an awful lot of code whereas PrintExt is just using the declared type and moves the value directly. Maybe this can be improved - I will ask.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi Steve,
thanks a lot for this really detailed explanation. I agree with your conclusion, that the use in not standard compliant and there should be at least a warning from the compiler.
I also agree with you, that the select type construct seems to create a lot of code and I really hope that this can be improved in the future. In our current code base, we are not using select type anymore in inner loops due to the current overhead. Of course, I am aware that there will always be some overhead when using the OO-features, but I hope that there is still room for improvement.
Best regards,
Thomas
thanks a lot for this really detailed explanation. I agree with your conclusion, that the use in not standard compliant and there should be at least a warning from the compiler.
I also agree with you, that the select type construct seems to create a lot of code and I really hope that this can be improved in the future. In our current code base, we are not using select type anymore in inner loops due to the current overhead. Of course, I am aware that there will always be some overhead when using the OO-features, but I hope that there is still room for improvement.
Best regards,
Thomas
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
I expect this to be fixed in the 15.0 release later this year.
Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page