Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
This community is designed for sharing of public information. Please do not share Intel or third-party confidential information here.
27152 Discussions

"0xC0000005: Access violation executing location" when calling interface subroutine

lxander
Beginner
382 Views

I have a fairly simple console program that takes a single argument from the command line and the implements a progress update (to eventually be used in a larger program) ...

 

  ! fortranconsole.f90

  program Console

  use MyLib

  implicit none


  !character(:), allocatable ::                             taskId
  character(len=36) :: taskId
  procedure(ProgressUpdateAction), pointer :: progressUpdate

  ! Print splash line
  print *, 'Analysis Console'

  call ProcessCommandLine(taskId)

  ! Print the task id to the command line
  print *, taskId

  call DoWork(progressUpdate)


  contains


  subroutine ProcessCommandLine(taskId)

  character(len=36), intent(out) :: taskId

  integer :: myArgLength
  integer ::myStat

  ! Check command line for supplied arguments
  if (Command_argument_count() == 0) then
    stop "Error: There were no command line arguments."
  end if

  call Get_command_argument(1, length=myArgLength, status=myStat)
  if (0 < myStat) then
    stop "Error: Could not retrieve the first command argument."
  end if
  if (36 < myArgLength) then
    stop "Error: Argument more than 36 characters long."
  end if

  ! Get first argument
  call Get_command_argument(1, value=taskId, status=myStat)

  if (myStat /= 0) then
    stop "Error GET_COMMAND_ARGUMENT failed unexpectedly."
  end if

  end subroutine

  end program

 

 

 

  ! modules.f90
  
  module MyLib

  implicit none

  interface
  subroutine ProgressUpdateAction(percentProgress)
  integer, intent(in) :: percentProgress
  end subroutine
  end interface

  contains

  subroutine DoWork(progressUpdate)
  !DIR$ ATTRIBUTES DLLEXPORT :: DoWork
  !DIR$ ATTRIBUTES ALIAS: 'DoWork' :: DoWork
  !DIR$ ATTRIBUTES REFERENCE :: progressCallBack

  procedure(ProgressUpdateAction), intent(in), pointer :: progressUpdate

  integer, parameter :: STEP_COUNT = 9
  integer, parameter :: OP_COUNT = 100000000
  integer :: i, j, percentProgress
  real(8) :: n
  
  do i = 1, STEP_COUNT

    ! Update the status.
    percentProgress = (i - 1) * 100.0 / STEP_COUNT
    call progressUpdate(percentProgress)

    ! Do something expensive.
    !do j = 1, OP_COUNT
    !  n = sqrt(real(j,8))
    !  if (j == OP_COUNT) print *, n
    !end do

  end do

  end subroutine

  end module

 

When I try to debug the code in VS2022 (v16.0.6), I get this ...

lxander_0-1644827908136.png

 

... which looks to be where the call to progressUpdate is made.  If I run the executable from the command line I get this ...

lxander_1-1644815809030.png

My compiler/IDE/etc. setup (which is all the latest installs) ...

lxander_0-1644828212583.png

Am I doing something wrong in implementing the interface?  Is the procedure/pointer throwing things off? 

Many thanks.

Labels (1)
0 Kudos
1 Solution
IanH
Black Belt
302 Views

In the blog post that you link to, the author has written the equivalent of a Fortran procedure in C# that does something (it writes to the console) when the DoWork subroutine requests a progress update by invoking the progressUpdate procedure pointer. There's nothing equivalent to that in your code.  How does your code know what to do when a progressUpdate is requested?

You could write a procedure something like:

module MyThings
  implicit none
contains
  ! This procedure matches the characteristics 
  ! defined by the ProgressUpdateAction abstract interface
  ! in the MyLib module.
  subroutine my_progress_updater(percentProgress)
    integer, intent(in) :: percentProgress
    write (*, "('I am about ',i0,'% of the way there.')")  &
        percentProgress
  end subroutine my_progress_updater
end module MyThings

and then pointer assign progressUpdate prior to the call to DoWork

use MyThings
...
progressUpdate => my_progress_updater
call DoWork(progressUpdate)

(The author of the blog post made the progressUpdate thing a procedure pointer possibly because of some idiosyncrasy associated with interfacing with C# (or possibly because they just like pointers).  But there's no need for it in a a Fortran-Fortran case.)

View solution in original post

6 Replies
Arjen_Markus
Valued Contributor III
343 Views

At a first glance I would say that you forgot to define the procedure pointer. At least, I do not see a statement like:

 

progressUpdate => progressUpdateAction

 

or something similar.

lxander
Beginner
315 Views

Thanks @Arjen_Markus .  Tried your suggestion by inserting that statement below line 20 in modules.for (in the MyLib module).

 

  ! modules.f90
  
  module MyLib

  implicit none

  interface
  subroutine ProgressUpdateAction(percentProgress)
  integer, intent(in) :: percentProgress
  end subroutine
  end interface

  contains

  subroutine DoWork(progressUpdate)
  !DIR$ ATTRIBUTES DLLEXPORT :: DoWork
  !DIR$ ATTRIBUTES ALIAS: 'DoWork' :: DoWork
  !DIR$ ATTRIBUTES REFERENCE :: progressCallBack

  procedure(ProgressUpdateAction), intent(in), pointer :: progressUpdate
  
  progressUpdate => progressUpdateAction ! <--

  integer, parameter :: STEP_COUNT = 9
  integer, parameter :: OP_COUNT = 100000000
  integer :: i, j, percentProgress
  real(8) :: n
  
  do i = 1, STEP_COUNT

    ! Update the status.
    percentProgress = (i - 1) * 100.0 / STEP_COUNT
    call progressUpdate(percentProgress)

    ! Do something expensive.
    !do j = 1, OP_COUNT
    !  n = sqrt(real(j,8))
    !  if (j == OP_COUNT) print *, n
    !end do

  end do

  end subroutine

  end module

 

Tried compiling and get "Warning warning #8889: Explicit interface or EXTERNAL declaration is required. [DOWORK]" (referring to line 22 in fortanconsole.for) and then a bunch of errors.

 

I also tried putting that statement below line 12 in fortranconsole.f90 ...

 

  ! fortranconsole.f90

  program Console

  use MyLib

  implicit none


  !character(:), allocatable ::                             taskId
  character(len=36) :: taskId
  procedure(ProgressUpdateAction), pointer :: progressUpdate

  progressUpdate => progressUpdateAction ! <--

  ! Print splash line
  print *, 'Analysis Console'

  call ProcessCommandLine(taskId)

  ! Print the task id to the command line
  print *, taskId

  call DoWork(progressUpdate)


  contains


  subroutine ProcessCommandLine(taskId)

  character(len=36), intent(out) :: taskId

  integer :: myArgLength
  integer ::myStat

  ! Check command line for supplied arguments
  if (Command_argument_count() == 0) then
    stop "Error: There were no command line arguments."
  end if

  call Get_command_argument(1, length=myArgLength, status=myStat)
  if (0 < myStat) then
    stop "Error: Could not retrieve the first command argument."
  end if
  if (36 < myArgLength) then
    stop "Error: Argument more than 36 characters long."
  end if

  ! Get first argument
  call Get_command_argument(1, value=taskId, status=myStat)

  if (myStat /= 0) then
    stop "Error GET_COMMAND_ARGUMENT failed unexpectedly."
  end if

  end subroutine

  end program

 

... but instead get "Error error LNK2019: unresolved external symbol PROGRESSUPDATEACTION referenced in function MAIN__ fortranconsole.obj" and "fatal error LNK1120: 1 unresolved externals x64\Debug\FortranConsole.exe".

I don't think what I'm trying to do is too wild.  The code is based off of Calling Fortran from C# – Monitoring Progress Using Callbacks.  Any other suggestions?  Could there be something in the project properties that I need to set?

Arjen_Markus
Valued Contributor III
299 Views

Definitely nothing too wild :), but what I meant to explain is that the pointer you pass to the routine DoWork must be pointing to an existing routine. Now you use the name of the interface. Your second solution is the way to go (because otherwise you could leave out the argument altogether), but you need an actual routine to do the work.

IanH
Black Belt
303 Views

In the blog post that you link to, the author has written the equivalent of a Fortran procedure in C# that does something (it writes to the console) when the DoWork subroutine requests a progress update by invoking the progressUpdate procedure pointer. There's nothing equivalent to that in your code.  How does your code know what to do when a progressUpdate is requested?

You could write a procedure something like:

module MyThings
  implicit none
contains
  ! This procedure matches the characteristics 
  ! defined by the ProgressUpdateAction abstract interface
  ! in the MyLib module.
  subroutine my_progress_updater(percentProgress)
    integer, intent(in) :: percentProgress
    write (*, "('I am about ',i0,'% of the way there.')")  &
        percentProgress
  end subroutine my_progress_updater
end module MyThings

and then pointer assign progressUpdate prior to the call to DoWork

use MyThings
...
progressUpdate => my_progress_updater
call DoWork(progressUpdate)

(The author of the blog post made the progressUpdate thing a procedure pointer possibly because of some idiosyncrasy associated with interfacing with C# (or possibly because they just like pointers).  But there's no need for it in a a Fortran-Fortran case.)

lxander
Beginner
281 Views

Thank you @Arjen_Markus and @IanH .  Your responses have provided the solution I was looking for.  I really appreciate the speedy replies.

I have updated my code and it compiles and run fine.  Here is where I ended up ...

 

  ! fortranconsole.f90

  program Console

  
  use Library

  
  implicit none
  

  character(len=36) :: taskId
  procedure(IProgressUpdateAction), pointer :: progressUpdate

  ! Print splash line
  print *, 'Analysis Console'

  call ProcessCommandLine(taskId)

  ! Print the task id to the command line
  print *, taskId

  progressUpdate => ProgressUpdateAction
  call DoWork(progressUpdate)


  contains


  subroutine ProcessCommandLine(taskId)

  character(len=36), intent(out) :: taskId

  integer :: myArgLength
  integer ::myStat

  ! Check command line for supplied arguments
  if (Command_argument_count() == 0) then
    stop "Error: There were no command line arguments."
  end if

  call Get_command_argument(1, length=myArgLength, status=myStat)
  if (0 < myStat) then
    stop "Error: Could not retrieve the first command argument."
  end if
  if (36 < myArgLength) then
    stop "Error: Argument more than 36 characters long."
  end if

  ! Get first argument
  call Get_command_argument(1, value=taskId, status=myStat)

  if (myStat /= 0) then
    stop "Error GET_COMMAND_ARGUMENT failed unexpectedly."
  end if

  end subroutine


  subroutine ProgressUpdateAction(percentProgress)
  
  integer, intent(in) :: percentProgress
  
  write (*, "('I am about ',i0,'% of the way there.')")  &
    percentProgress
  
  end subroutine


  end program
  ! modules.f90
  
  module Library


  implicit none


  interface

  subroutine IProgressUpdateAction(percentProgress)

  integer, intent(in) :: percentProgress

  end subroutine

  end interface


  contains


  subroutine DoWork(progressUpdate)
  !DIR$ ATTRIBUTES DLLEXPORT :: DoWork
  !DIR$ ATTRIBUTES ALIAS: 'DoWork' :: DoWork
  !DIR$ ATTRIBUTES REFERENCE :: progressCallBack

  procedure(IProgressUpdateAction), intent(in), pointer :: progressUpdate

  integer, parameter :: STEP_COUNT = 9
  integer, parameter :: OP_COUNT = 100000000
  integer :: i, j, percentProgress
  real(8) :: n

  do i = 1, STEP_COUNT

    ! Update the status.
    percentProgress = (i - 1) * 100.0 / STEP_COUNT
    call progressUpdate(percentProgress)

    ! Do something expensive.
    !do j = 1, OP_COUNT
    !  n = sqrt(real(j,8))
    !  if (j == OP_COUNT) print *, n
    !end do

  end do

  end subroutine
  

  end module

 

 

 

Khalik_K_Intel
Moderator
264 Views

Hello,


This issue has been already resolved by our community members. Therefore, we will no longer respond to this thread.

If you require some further assistance from Intel, please start a new thread.

Any further interaction in this thread will be considered community only.


Regards,

Khalik.


Reply